summaryrefslogtreecommitdiff
path: root/bcprov/src/main/java/org/bouncycastle/crypto
diff options
context:
space:
mode:
authorSergio Giro <sgiro@google.com>2016-02-02 15:27:40 +0000
committerSergio Giro <sgiro@google.com>2016-02-02 15:30:05 +0000
commit11975162f2da08e65157d37cd272721485f2b34b (patch)
treee2d9c4866d82c0e93854d5e9a6200cfce47e6232 /bcprov/src/main/java/org/bouncycastle/crypto
parentbdb7b3d37025690a0434040b4e0d0623d9fa74af (diff)
downloadbouncycastle-11975162f2da08e65157d37cd272721485f2b34b.tar.gz
bouncycastle: Android tree with upstream code for version 1.54.
Adding missing files Change-Id: Ife77e8b1df7ec05555b29fb48a984f4c0da2e562
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/crypto')
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/Xof.java19
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java580
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java549
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java103
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java61
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java267
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java486
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java303
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java46
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java56
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java159
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java363
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java305
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java81
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java144
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java152
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java119
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java107
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java88
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java89
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java63
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java32
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java127
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java30
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java17
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java126
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java31
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java19
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java114
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java109
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java39
36 files changed, 5067 insertions, 0 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java b/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java
new file mode 100644
index 00000000..8b640993
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Interface for a converter that produces a byte encoding for a char array.
+ */
+public interface CharToByteConverter
+{
+ /**
+ * Return the type of the conversion.
+ *
+ * @return a type name for the conversion.
+ */
+ String getType();
+
+ /**
+ * Return a byte encoded representation of the passed in password.
+ *
+ * @param password the characters to encode.
+ * @return a byte encoding of password.
+ */
+ byte[] convert(char[] password);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java b/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java
new file mode 100644
index 00000000..9183a7a2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto;
+
+/**
+ * With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes.
+ * This interface provides the extra method required to support variable output on an extended digest implementation.
+ */
+public interface Xof
+ extends ExtendedDigest
+{
+ /**
+ * Output the results of the final calculation for this digest to outLen number of bytes.
+ *
+ * @param out output array to write the output bytes to.
+ * @param outOff offset to start writing the bytes at.
+ * @param outLen the number of output bytes requested.
+ * @return the number of bytes written
+ */
+ int doFinal(byte[] out, int outOff, int outLen);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
new file mode 100644
index 00000000..c3353f9d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.crypto.agreement.kdf;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.KDFParameters;
+
+/**
+ * Generator for Concatenation Key Derivation Function defined in NIST SP 800-56A, Sect 5.8.1
+ */
+public class ConcatenationKDFGenerator
+ implements DerivationFunction
+{
+ private Digest digest;
+ private byte[] shared;
+ private byte[] otherInfo;
+ private int hLen;
+
+ /**
+ * @param digest the digest to be used as the source of generated bytes
+ */
+ public ConcatenationKDFGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ this.hLen = digest.getDigestSize();
+ }
+
+ public void init(
+ DerivationParameters param)
+ {
+ if (param instanceof KDFParameters)
+ {
+ KDFParameters p = (KDFParameters)param;
+
+ shared = p.getSharedSecret();
+ otherInfo = p.getIV();
+ }
+ else
+ {
+ throw new IllegalArgumentException("KDF parameters required for KDF2Generator");
+ }
+ }
+
+ /**
+ * return the underlying digest.
+ */
+ public Digest getDigest()
+ {
+ return digest;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * fill len bytes of the output buffer with bytes generated from
+ * the derivation function.
+ *
+ * @throws DataLengthException if the out buffer is too small.
+ */
+ public int generateBytes(
+ byte[] out,
+ int outOff,
+ int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+ if ((out.length - len) < outOff)
+ {
+ throw new DataLengthException("output buffer too small");
+ }
+
+ byte[] hashBuf = new byte[hLen];
+ byte[] C = new byte[4];
+ int counter = 1;
+ int outputLen = 0;
+
+ digest.reset();
+
+ if (len > hLen)
+ {
+ do
+ {
+ ItoOSP(counter, C);
+
+ digest.update(C, 0, C.length);
+ digest.update(shared, 0, shared.length);
+ digest.update(otherInfo, 0, otherInfo.length);
+
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + outputLen, hLen);
+ outputLen += hLen;
+ }
+ while ((counter++) < (len / hLen));
+ }
+
+ if (outputLen < len)
+ {
+ ItoOSP(counter, C);
+
+ digest.update(C, 0, C.length);
+ digest.update(shared, 0, shared.length);
+ digest.update(otherInfo, 0, otherInfo.length);
+
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + outputLen, len - outputLen);
+ }
+
+ return len;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
new file mode 100644
index 00000000..0fd3ebb9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
@@ -0,0 +1,580 @@
+package org.bouncycastle.crypto.digests;
+
+
+/* The BLAKE2 cryptographic hash function was designed by Jean-
+ Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
+ Winnerlein.
+
+ Reference Implementation and Description can be found at: https://blake2.net/
+ Internet Draft: https://tools.ietf.org/html/draft-saarinen-blake2-02
+
+ This implementation does not support the Tree Hashing Mode.
+
+ For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based
+ message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2.
+
+ Algorithm | Target | Collision | Hash | Hash ASN.1 |
+ Identifier | Arch | Security | nn | OID Suffix |
+ ---------------+--------+-----------+------+------------+
+ id-blake2b160 | 64-bit | 2**80 | 20 | x.1.20 |
+ id-blake2b256 | 64-bit | 2**128 | 32 | x.1.32 |
+ id-blake2b384 | 64-bit | 2**192 | 48 | x.1.48 |
+ id-blake2b512 | 64-bit | 2**256 | 64 | x.1.64 |
+ ---------------+--------+-----------+------+------------+
+ */
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+
+/**
+ * Implementation of the cryptographic hash function Blakbe2b.
+ *
+ * Blake2b offers a built-in keying mechanism to be used directly
+ * for authentication ("Prefix-MAC") rather than a HMAC construction.
+ *
+ * Blake2b offers a built-in support for a salt for randomized hashing
+ * and a personal string for defining a unique hash function for each application.
+ *
+ * BLAKE2b is optimized for 64-bit platforms and produces digests of any size
+ * between 1 and 64 bytes.
+ */
+public class Blake2bDigest
+ implements ExtendedDigest
+{
+ // Blake2b Initialization Vector:
+ private final static long blake2b_IV[] =
+ // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
+ // The same as SHA-512 IV.
+ {
+ 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL,
+ 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL,
+ 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L
+ };
+
+ // Message word permutations:
+ private final static byte[][] blake2b_sigma =
+ {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+ };
+
+ private static int rOUNDS = 12; // to use for Catenas H'
+ private final static int BLOCK_LENGTH_BYTES = 128;// bytes
+
+ // General parameters:
+ private int digestLength = 64; // 1- 64 bytes
+ private int keyLength = 0; // 0 - 64 bytes for keyed hashing for MAC
+ private byte[] salt = null;// new byte[16];
+ private byte[] personalization = null;// new byte[16];
+
+ // the key
+ private byte[] key = null;
+
+ // Tree hashing parameters:
+ // Because this class does not implement the Tree Hashing Mode,
+ // these parameters can be treated as constants (see init() function)
+ /*
+ * private int fanout = 1; // 0-255 private int depth = 1; // 1 - 255
+ * private int leafLength= 0; private long nodeOffset = 0L; private int
+ * nodeDepth = 0; private int innerHashLength = 0;
+ */
+
+ // whenever this buffer overflows, it will be processed
+ // in the compress() function.
+ // For performance issues, long messages will not use this buffer.
+ private byte[] buffer = null;// new byte[BLOCK_LENGTH_BYTES];
+ // Position of last inserted byte:
+ private int bufferPos = 0;// a value from 0 up to 128
+
+ private long[] internalState = new long[16]; // In the Blake2b paper it is
+ // called: v
+ private long[] chainValue = null; // state vector, in the Blake2b paper it
+ // is called: h
+
+ private long t0 = 0L; // holds last significant bits, counter (counts bytes)
+ private long t1 = 0L; // counter: Length up to 2^128 are supported
+ private long f0 = 0L; // finalization flag, for last block: ~0L
+
+ // For Tree Hashing Mode, not used here:
+ // private long f1 = 0L; // finalization flag, for last node: ~0L
+
+ public Blake2bDigest()
+ {
+ this(512);
+ }
+
+ public Blake2bDigest(Blake2bDigest digest)
+ {
+ this.bufferPos = digest.bufferPos;
+ this.buffer = Arrays.clone(digest.buffer);
+ this.keyLength = digest.keyLength;
+ this.key = Arrays.clone(digest.key);
+ this.digestLength = digest.digestLength;
+ this.chainValue = Arrays.clone(digest.chainValue);
+ this.personalization = Arrays.clone(personalization);
+ }
+
+ public Blake2bDigest(int digestLength)
+ {
+ if (digestLength != 160 && digestLength != 256 && digestLength != 384 && digestLength != 512)
+ {
+ throw new IllegalArgumentException("Blake2b digest restricted to one of [160, 256, 384, 512]");
+ }
+
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+ keyLength = 0;
+ this.digestLength = digestLength / 8;
+ init();
+ }
+
+ /**
+ * Blake2b for authentication ("Prefix-MAC mode").
+ * After calling the doFinal() method, the key will
+ * remain to be used for further computations of
+ * this instance.
+ * The key can be overwritten using the clearKey() method.
+ *
+ * @param key A key up to 64 bytes or null
+ */
+ public Blake2bDigest(byte[] key)
+ {
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+ if (key != null)
+ {
+ this.key = new byte[key.length];
+ System.arraycopy(key, 0, this.key, 0, key.length);
+
+ if (key.length > 64)
+ {
+ throw new IllegalArgumentException(
+ "Keys > 64 are not supported");
+ }
+ keyLength = key.length;
+ System.arraycopy(key, 0, buffer, 0, key.length);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+ digestLength = 64;
+ init();
+ }
+
+ /**
+ * Blake2b with key, required digest length, salt and personalization.
+ * After calling the doFinal() method, the key, the salt and the personal string
+ * will remain and might be used for further computations with this instance.
+ * The key can be overwritten using the clearKey() method, the salt (pepper)
+ * can be overwritten using the clearSalt() method.
+ *
+ * @param key A key up to 64 bytes or null
+ * @param digestLength from 1 up to 64 bytes
+ * @param salt up to 16 bytes or null
+ * @param personalization up to 16 bytes or null
+ */
+ public Blake2bDigest(byte[] key, int digestLength, byte[] salt,
+ byte[] personalization)
+ {
+
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+ if (digestLength < 1 || digestLength > 64)
+ {
+ throw new IllegalArgumentException(
+ "Invalid digest length (required: 1 - 64)");
+ }
+ this.digestLength = digestLength;
+ if (salt != null)
+ {
+ if (salt.length != 16)
+ {
+ throw new IllegalArgumentException(
+ "salt length must be exactly 16 bytes");
+ }
+ this.salt = new byte[16];
+ System.arraycopy(salt, 0, this.salt, 0, salt.length);
+ }
+ if (personalization != null)
+ {
+ if (personalization.length != 16)
+ {
+ throw new IllegalArgumentException(
+ "personalization length must be exactly 16 bytes");
+ }
+ this.personalization = new byte[16];
+ System.arraycopy(personalization, 0, this.personalization, 0,
+ personalization.length);
+ }
+ if (key != null)
+ {
+ this.key = new byte[key.length];
+ System.arraycopy(key, 0, this.key, 0, key.length);
+
+ if (key.length > 64)
+ {
+ throw new IllegalArgumentException(
+ "Keys > 64 are not supported");
+ }
+ keyLength = key.length;
+ System.arraycopy(key, 0, buffer, 0, key.length);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+ init();
+ }
+
+ // initialize chainValue
+ private void init()
+ {
+
+ if (chainValue == null)
+ {
+ chainValue = new long[8];
+
+ chainValue[0] = blake2b_IV[0]
+ ^ (digestLength | (keyLength << 8) | 0x1010000);
+ // 0x1010000 = ((fanout << 16) | (depth << 24) | (leafLength <<
+ // 32));
+ // with fanout = 1; depth = 0; leafLength = 0;
+ chainValue[1] = blake2b_IV[1];// ^ nodeOffset; with nodeOffset = 0;
+ chainValue[2] = blake2b_IV[2];// ^ ( nodeDepth | (innerHashLength <<
+ // 8) );
+ // with nodeDepth = 0; innerHashLength = 0;
+
+ chainValue[3] = blake2b_IV[3];
+
+ chainValue[4] = blake2b_IV[4];
+ chainValue[5] = blake2b_IV[5];
+ if (salt != null)
+ {
+ chainValue[4] ^= (bytes2long(salt, 0));
+ chainValue[5] ^= (bytes2long(salt, 8));
+ }
+
+ chainValue[6] = blake2b_IV[6];
+ chainValue[7] = blake2b_IV[7];
+ if (personalization != null)
+ {
+ chainValue[6] ^= (bytes2long(personalization, 0));
+ chainValue[7] ^= (bytes2long(personalization, 8));
+ }
+ }
+ }
+
+ private void initializeInternalState()
+ {
+ // initialize v:
+ System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
+ System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4);
+ internalState[12] = t0 ^ blake2b_IV[4];
+ internalState[13] = t1 ^ blake2b_IV[5];
+ internalState[14] = f0 ^ blake2b_IV[6];
+ internalState[15] = blake2b_IV[7];// ^ f1 with f1 = 0
+ }
+
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param b the input byte to be entered.
+ */
+ public void update(byte b)
+ {
+ int remainingLength = 0; // left bytes of buffer
+
+ // process the buffer if full else add to buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength == 0)
+ { // full buffer
+ t0 += BLOCK_LENGTH_BYTES;
+ if (t0 == 0)
+ { // if message > 2^64
+ t1++;
+ }
+ compress(buffer, 0);
+ Arrays.fill(buffer, (byte)0);// clear buffer
+ buffer[0] = b;
+ bufferPos = 1;
+ } else
+ {
+ buffer[bufferPos] = b;
+ bufferPos++;
+ return;
+ }
+ }
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param message the byte array containing the data.
+ * @param offset the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void update(byte[] message, int offset, int len)
+ {
+
+ if (message == null || len == 0)
+ return;
+
+ int remainingLength = 0; // left bytes of buffer
+
+ if (bufferPos != 0)
+ { // commenced, incomplete buffer
+
+ // complete the buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength < len)
+ { // full buffer + at least 1 byte
+ System.arraycopy(message, offset, buffer, bufferPos,
+ remainingLength);
+ t0 += BLOCK_LENGTH_BYTES;
+ if (t0 == 0)
+ { // if message > 2^64
+ t1++;
+ }
+ compress(buffer, 0);
+ bufferPos = 0;
+ Arrays.fill(buffer, (byte) 0);// clear buffer
+ } else
+ {
+ System.arraycopy(message, offset, buffer, bufferPos, len);
+ bufferPos += len;
+ return;
+ }
+ }
+
+ // process blocks except last block (also if last block is full)
+ int messagePos;
+ int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
+ for (messagePos = offset + remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES)
+ { // block wise 128 bytes
+ // without buffer:
+ t0 += BLOCK_LENGTH_BYTES;
+ if (t0 == 0)
+ {
+ t1++;
+ }
+ compress(message, messagePos);
+ }
+
+ // fill the buffer with left bytes, this might be a full block
+ System.arraycopy(message, messagePos, buffer, 0, offset + len
+ - messagePos);
+ bufferPos += offset + len - messagePos;
+ }
+
+ /**
+ * close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ * Key, salt and personal string remain.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOffset the offset into the out array the digest is to start at.
+ */
+ public int doFinal(byte[] out, int outOffset)
+ {
+
+ f0 = 0xFFFFFFFFFFFFFFFFL;
+ t0 += bufferPos;
+ // bufferPos may be < 128, so (t0 == 0) does not work
+ // for 2^64 < message length > 2^64 - 127
+ if ((t0 < 0) && (bufferPos > -t0))
+ {
+ t1++;
+ }
+ compress(buffer, 0);
+ Arrays.fill(buffer, (byte) 0);// Holds eventually the key if input is null
+ Arrays.fill(internalState, 0L);
+
+ for (int i = 0; i < chainValue.length && (i * 8 < digestLength); i++)
+ {
+ byte[] bytes = long2bytes(chainValue[i]);
+
+ if (i * 8 < digestLength - 8)
+ {
+ System.arraycopy(bytes, 0, out, outOffset + i * 8, 8);
+ }
+ else
+ {
+ System.arraycopy(bytes, 0, out, outOffset + i * 8, digestLength - (i * 8));
+ }
+ }
+
+ Arrays.fill(chainValue, 0L);
+
+ reset();
+
+ return digestLength;
+ }
+
+ /**
+ * Reset the digest back to it's initial state.
+ * The key, the salt and the personal string will
+ * remain for further computations.
+ */
+ public void reset()
+ {
+ bufferPos = 0;
+ f0 = 0L;
+ t0 = 0L;
+ t1 = 0L;
+ chainValue = null;
+ if (key != null)
+ {
+ System.arraycopy(key, 0, buffer, 0, key.length);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+ init();
+ }
+
+ private void compress(byte[] message, int messagePos)
+ {
+
+ initializeInternalState();
+
+ long[] m = new long[16];
+ for (int j = 0; j < 16; j++)
+ {
+ m[j] = bytes2long(message, messagePos + j * 8);
+ }
+
+ for (int round = 0; round < rOUNDS; round++)
+ {
+
+ // G apply to columns of internalState:m[blake2b_sigma[round][2 *
+ // blockPos]] /+1
+ G(m[blake2b_sigma[round][0]], m[blake2b_sigma[round][1]], 0, 4, 8,
+ 12);
+ G(m[blake2b_sigma[round][2]], m[blake2b_sigma[round][3]], 1, 5, 9,
+ 13);
+ G(m[blake2b_sigma[round][4]], m[blake2b_sigma[round][5]], 2, 6, 10,
+ 14);
+ G(m[blake2b_sigma[round][6]], m[blake2b_sigma[round][7]], 3, 7, 11,
+ 15);
+ // G apply to diagonals of internalState:
+ G(m[blake2b_sigma[round][8]], m[blake2b_sigma[round][9]], 0, 5, 10,
+ 15);
+ G(m[blake2b_sigma[round][10]], m[blake2b_sigma[round][11]], 1, 6,
+ 11, 12);
+ G(m[blake2b_sigma[round][12]], m[blake2b_sigma[round][13]], 2, 7,
+ 8, 13);
+ G(m[blake2b_sigma[round][14]], m[blake2b_sigma[round][15]], 3, 4,
+ 9, 14);
+ }
+
+ // update chain values:
+ for (int offset = 0; offset < chainValue.length; offset++)
+ {
+ chainValue[offset] = chainValue[offset] ^ internalState[offset]
+ ^ internalState[offset + 8];
+ }
+ }
+
+ private void G(long m1, long m2, int posA, int posB, int posC, int posD)
+ {
+
+ internalState[posA] = internalState[posA] + internalState[posB] + m1;
+ internalState[posD] = rotr64(internalState[posD] ^ internalState[posA],
+ 32);
+ internalState[posC] = internalState[posC] + internalState[posD];
+ internalState[posB] = rotr64(internalState[posB] ^ internalState[posC],
+ 24); // replaces 25 of BLAKE
+ internalState[posA] = internalState[posA] + internalState[posB] + m2;
+ internalState[posD] = rotr64(internalState[posD] ^ internalState[posA],
+ 16);
+ internalState[posC] = internalState[posC] + internalState[posD];
+ internalState[posB] = rotr64(internalState[posB] ^ internalState[posC],
+ 63); // replaces 11 of BLAKE
+ }
+
+ private long rotr64(long x, int rot)
+ {
+ return x >>> rot | (x << (64 - rot));
+ }
+
+ // convert one long value in byte array
+ // little-endian byte order!
+ private final byte[] long2bytes(long longValue)
+ {
+ return new byte[]
+ { (byte) longValue, (byte) (longValue >> 8),
+ (byte) (longValue >> 16), (byte) (longValue >> 24),
+ (byte) (longValue >> 32), (byte) (longValue >> 40),
+ (byte) (longValue >> 48), (byte) (longValue >> 56)
+ };
+ }
+
+ // little-endian byte order!
+ private final long bytes2long(byte[] byteArray, int offset)
+ {
+ return (((long) byteArray[offset] & 0xFF)
+ | (((long) byteArray[offset + 1] & 0xFF) << 8)
+ | (((long) byteArray[offset + 2] & 0xFF) << 16)
+ | (((long) byteArray[offset + 3] & 0xFF) << 24)
+ | (((long) byteArray[offset + 4] & 0xFF) << 32)
+ | (((long) byteArray[offset + 5] & 0xFF) << 40)
+ | (((long) byteArray[offset + 6] & 0xFF) << 48)
+ | (((long) byteArray[offset + 7] & 0xFF) << 56));
+ }
+
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public String getAlgorithmName()
+ {
+ return "Blake2b";
+ }
+
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ public int getDigestSize()
+ {
+ return digestLength;
+ }
+
+ /**
+ * Return the size in bytes of the internal buffer the digest applies it's compression
+ * function to.
+ *
+ * @return byte length of the digests internal buffer.
+ */
+ public int getByteLength()
+ {
+ return BLOCK_LENGTH_BYTES;
+ }
+
+ /**
+ * Overwrite the key
+ * if it is no longer used (zeroization)
+ */
+ public void clearKey()
+ {
+ if (key != null)
+ {
+ Arrays.fill(key, (byte) 0);
+ Arrays.fill(buffer, (byte) 0);
+ }
+ }
+
+ /**
+ * Overwrite the salt (pepper) if it
+ * is secret and no longer used (zeroization)
+ */
+ public void clearSalt()
+ {
+ if (salt != null)
+ {
+ Arrays.fill(salt, (byte) 0);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
new file mode 100644
index 00000000..40194461
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
@@ -0,0 +1,549 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ * <p>
+ * Following the naming conventions used in the C source code to enable easy review of the implementation.
+ */
+public class KeccakDigest
+ implements ExtendedDigest
+{
+ private static long[] KeccakRoundConstants = keccakInitializeRoundConstants();
+
+ private static int[] KeccakRhoOffsets = keccakInitializeRhoOffsets();
+
+ private static long[] keccakInitializeRoundConstants()
+ {
+ long[] keccakRoundConstants = new long[24];
+ byte[] LFSRstate = new byte[1];
+
+ LFSRstate[0] = 0x01;
+ int i, j, bitPosition;
+
+ for (i = 0; i < 24; i++)
+ {
+ keccakRoundConstants[i] = 0;
+ for (j = 0; j < 7; j++)
+ {
+ bitPosition = (1 << j) - 1;
+ if (LFSR86540(LFSRstate))
+ {
+ keccakRoundConstants[i] ^= 1L << bitPosition;
+ }
+ }
+ }
+
+ return keccakRoundConstants;
+ }
+
+ private static boolean LFSR86540(byte[] LFSR)
+ {
+ boolean result = (((LFSR[0]) & 0x01) != 0);
+ if (((LFSR[0]) & 0x80) != 0)
+ {
+ LFSR[0] = (byte)(((LFSR[0]) << 1) ^ 0x71);
+ }
+ else
+ {
+ LFSR[0] <<= 1;
+ }
+
+ return result;
+ }
+
+ private static int[] keccakInitializeRhoOffsets()
+ {
+ int[] keccakRhoOffsets = new int[25];
+ int x, y, t, newX, newY;
+
+ keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0;
+ x = 1;
+ y = 0;
+ for (t = 0; t < 24; t++)
+ {
+ keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64;
+ newX = (0 * x + 1 * y) % 5;
+ newY = (2 * x + 3 * y) % 5;
+ x = newX;
+ y = newY;
+ }
+
+ return keccakRhoOffsets;
+ }
+
+ protected byte[] state = new byte[(1600 / 8)];
+ protected byte[] dataQueue = new byte[(1536 / 8)];
+ protected int rate;
+ protected int bitsInQueue;
+ protected int fixedOutputLength;
+ protected boolean squeezing;
+ protected int bitsAvailableForSqueezing;
+ protected byte[] chunk;
+ protected byte[] oneByte;
+
+ private void clearDataQueueSection(int off, int len)
+ {
+ for (int i = off; i != off + len; i++)
+ {
+ dataQueue[i] = 0;
+ }
+ }
+
+ public KeccakDigest()
+ {
+ this(288);
+ }
+
+ public KeccakDigest(int bitLength)
+ {
+ init(bitLength);
+ }
+
+ public KeccakDigest(KeccakDigest source) {
+ System.arraycopy(source.state, 0, this.state, 0, source.state.length);
+ System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length);
+ this.rate = source.rate;
+ this.bitsInQueue = source.bitsInQueue;
+ this.fixedOutputLength = source.fixedOutputLength;
+ this.squeezing = source.squeezing;
+ this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing;
+ this.chunk = Arrays.clone(source.chunk);
+ this.oneByte = Arrays.clone(source.oneByte);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Keccak-" + fixedOutputLength;
+ }
+
+ public int getDigestSize()
+ {
+ return fixedOutputLength / 8;
+ }
+
+ public void update(byte in)
+ {
+ oneByte[0] = in;
+
+ absorb(oneByte, 0, 8L);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ absorb(in, inOff, len * 8L);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ squeeze(out, outOff, fixedOutputLength);
+
+ reset();
+
+ return getDigestSize();
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
+ {
+ if (partialBits > 0)
+ {
+ oneByte[0] = partialByte;
+ absorb(oneByte, 0, partialBits);
+ }
+
+ squeeze(out, outOff, fixedOutputLength);
+
+ reset();
+
+ return getDigestSize();
+ }
+
+ public void reset()
+ {
+ init(fixedOutputLength);
+ }
+
+ /**
+ * Return the size of block that the compression function is applied to in bytes.
+ *
+ * @return internal byte length of a block.
+ */
+ public int getByteLength()
+ {
+ return rate / 8;
+ }
+
+ private void init(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 288:
+ initSponge(1024, 576);
+ break;
+ case 128:
+ initSponge(1344, 256);
+ break;
+ case 224:
+ initSponge(1152, 448);
+ break;
+ case 256:
+ initSponge(1088, 512);
+ break;
+ case 384:
+ initSponge(832, 768);
+ break;
+ case 512:
+ initSponge(576, 1024);
+ break;
+ default:
+ throw new IllegalArgumentException("bitLength must be one of 128, 224, 256, 288, 384, or 512.");
+ }
+ }
+
+ private void initSponge(int rate, int capacity)
+ {
+ if (rate + capacity != 1600)
+ {
+ throw new IllegalStateException("rate + capacity != 1600");
+ }
+ if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
+ {
+ throw new IllegalStateException("invalid rate value");
+ }
+
+ this.rate = rate;
+ // this is never read, need to check to see why we want to save it
+ // this.capacity = capacity;
+ Arrays.fill(this.state, (byte)0);
+ Arrays.fill(this.dataQueue, (byte)0);
+ this.bitsInQueue = 0;
+ this.squeezing = false;
+ this.bitsAvailableForSqueezing = 0;
+ this.fixedOutputLength = capacity / 2;
+ this.chunk = new byte[rate / 8];
+ this.oneByte = new byte[1];
+ }
+
+ private void absorbQueue()
+ {
+ KeccakAbsorb(state, dataQueue, rate / 8);
+
+ bitsInQueue = 0;
+ }
+
+ protected void absorb(byte[] data, int off, long databitlen)
+ {
+ long i, j, wholeBlocks;
+
+ if ((bitsInQueue % 8) != 0)
+ {
+ throw new IllegalStateException("attempt to absorb with odd length queue.");
+ }
+ if (squeezing)
+ {
+ throw new IllegalStateException("attempt to absorb while squeezing.");
+ }
+
+ i = 0;
+ while (i < databitlen)
+ {
+ if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate)))
+ {
+ wholeBlocks = (databitlen - i) / rate;
+
+ for (j = 0; j < wholeBlocks; j++)
+ {
+ System.arraycopy(data, (int)(off + (i / 8) + (j * chunk.length)), chunk, 0, chunk.length);
+
+// displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8);
+
+ KeccakAbsorb(state, chunk, chunk.length);
+ }
+
+ i += wholeBlocks * rate;
+ }
+ else
+ {
+ int partialBlock = (int)(databitlen - i);
+ if (partialBlock + bitsInQueue > rate)
+ {
+ partialBlock = rate - bitsInQueue;
+ }
+ int partialByte = partialBlock % 8;
+ partialBlock -= partialByte;
+ System.arraycopy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8);
+
+ bitsInQueue += partialBlock;
+ i += partialBlock;
+ if (bitsInQueue == rate)
+ {
+ absorbQueue();
+ }
+ if (partialByte > 0)
+ {
+ int mask = (1 << partialByte) - 1;
+ dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask);
+ bitsInQueue += partialByte;
+ i += partialByte;
+ }
+ }
+ }
+ }
+
+ private void padAndSwitchToSqueezingPhase()
+ {
+ if (bitsInQueue + 1 == rate)
+ {
+ dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
+ absorbQueue();
+ clearDataQueueSection(0, rate / 8);
+ }
+ else
+ {
+ clearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8);
+ dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
+ }
+ dataQueue[(rate - 1) / 8] |= 1 << ((rate - 1) % 8);
+ absorbQueue();
+
+
+// displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---");
+
+
+ if (rate == 1024)
+ {
+ KeccakExtract1024bits(state, dataQueue);
+ bitsAvailableForSqueezing = 1024;
+ }
+ else
+
+ {
+ KeccakExtract(state, dataQueue, rate / 64);
+ bitsAvailableForSqueezing = rate;
+ }
+
+// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+
+ squeezing = true;
+ }
+
+ protected void squeeze(byte[] output, int offset, long outputLength)
+ {
+ long i;
+ int partialBlock;
+
+ if (!squeezing)
+ {
+ padAndSwitchToSqueezingPhase();
+ }
+ if ((outputLength % 8) != 0)
+ {
+ throw new IllegalStateException("outputLength not a multiple of 8");
+ }
+
+ i = 0;
+ while (i < outputLength)
+ {
+ if (bitsAvailableForSqueezing == 0)
+ {
+ keccakPermutation(state);
+
+ if (rate == 1024)
+ {
+ KeccakExtract1024bits(state, dataQueue);
+ bitsAvailableForSqueezing = 1024;
+ }
+ else
+
+ {
+ KeccakExtract(state, dataQueue, rate / 64);
+ bitsAvailableForSqueezing = rate;
+ }
+
+// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+
+ }
+ partialBlock = bitsAvailableForSqueezing;
+ if ((long)partialBlock > outputLength - i)
+ {
+ partialBlock = (int)(outputLength - i);
+ }
+
+ System.arraycopy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8);
+ bitsAvailableForSqueezing -= partialBlock;
+ i += partialBlock;
+ }
+ }
+
+ private void fromBytesToWords(long[] stateAsWords, byte[] state)
+ {
+ for (int i = 0; i < (1600 / 64); i++)
+ {
+ stateAsWords[i] = 0;
+ int index = i * (64 / 8);
+ for (int j = 0; j < (64 / 8); j++)
+ {
+ stateAsWords[i] |= ((long)state[index + j] & 0xff) << ((8 * j));
+ }
+ }
+ }
+
+ private void fromWordsToBytes(byte[] state, long[] stateAsWords)
+ {
+ for (int i = 0; i < (1600 / 64); i++)
+ {
+ int index = i * (64 / 8);
+ for (int j = 0; j < (64 / 8); j++)
+ {
+ state[index + j] = (byte)((stateAsWords[i] >>> ((8 * j))) & 0xFF);
+ }
+ }
+ }
+
+ private void keccakPermutation(byte[] state)
+ {
+ long[] longState = new long[state.length / 8];
+
+ fromBytesToWords(longState, state);
+
+// displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState);
+
+ keccakPermutationOnWords(longState);
+
+// displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState);
+
+ fromWordsToBytes(state, longState);
+ }
+
+ private void keccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes)
+ {
+ int i;
+
+ for (i = 0; i < dataLengthInBytes; i++)
+ {
+ state[i] ^= data[i];
+ }
+
+ keccakPermutation(state);
+ }
+
+ private void keccakPermutationOnWords(long[] state)
+ {
+ int i;
+
+// displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state);
+
+ for (i = 0; i < 24; i++)
+ {
+// displayIntermediateValues.displayRoundNumber(3, i);
+
+ theta(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state);
+
+ rho(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state);
+
+ pi(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state);
+
+ chi(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state);
+
+ iota(state, i);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state);
+ }
+ }
+
+ long[] C = new long[5];
+
+ private void theta(long[] A)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ C[x] = 0;
+ for (int y = 0; y < 5; y++)
+ {
+ C[x] ^= A[x + 5 * y];
+ }
+ }
+ for (int x = 0; x < 5; x++)
+ {
+ long dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >>> (64 - 1)))) ^ C[(x + 4) % 5];
+ for (int y = 0; y < 5; y++)
+ {
+ A[x + 5 * y] ^= dX;
+ }
+ }
+ }
+
+ private void rho(long[] A)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ int index = x + 5 * y;
+ A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >>> (64 - KeccakRhoOffsets[index]))) : A[index]);
+ }
+ }
+ }
+
+ long[] tempA = new long[25];
+
+ private void pi(long[] A)
+ {
+ System.arraycopy(A, 0, tempA, 0, tempA.length);
+
+ for (int x = 0; x < 5; x++)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y];
+ }
+ }
+ }
+
+ long[] chiC = new long[5];
+
+ private void chi(long[] A)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]);
+ }
+ for (int x = 0; x < 5; x++)
+ {
+ A[x + 5 * y] = chiC[x];
+ }
+ }
+ }
+
+ private void iota(long[] A, int indexRound)
+ {
+ A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound];
+ }
+
+ private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes)
+ {
+ keccakPermutationAfterXor(byteState, data, dataInBytes);
+ }
+
+
+ private void KeccakExtract1024bits(byte[] byteState, byte[] data)
+ {
+ System.arraycopy(byteState, 0, data, 0, 128);
+ }
+
+
+ private void KeccakExtract(byte[] byteState, byte[] data, int laneCount)
+ {
+ System.arraycopy(byteState, 0, data, 0, laneCount * 8);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
new file mode 100644
index 00000000..20dc641c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Xof;
+
+
+/**
+ * implementation of SHAKE based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ * <p>
+ * Following the naming conventions used in the C source code to enable easy review of the implementation.
+ */
+public class SHAKEDigest
+ extends KeccakDigest
+ implements Xof
+{
+ private static int checkBitLength(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 128:
+ case 256:
+ return bitLength;
+ default:
+ throw new IllegalArgumentException("'bitLength' " + bitLength + " not supported for SHAKE");
+ }
+ }
+
+ public SHAKEDigest()
+ {
+ this(128);
+ }
+
+ public SHAKEDigest(int bitLength)
+ {
+ super(checkBitLength(bitLength));
+ }
+
+ public SHAKEDigest(SHAKEDigest source) {
+ super(source);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHAKE" + fixedOutputLength;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return doFinal(out, outOff, getDigestSize());
+ }
+
+ public int doFinal(byte[] out, int outOff, int outLen)
+ {
+ absorb(new byte[]{ 0x0F }, 0, 4);
+
+ squeeze(out, outOff, ((long)outLen) * 8);
+
+ reset();
+
+ return outLen;
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
+ {
+ return doFinal(out, outOff, getDigestSize(), partialByte, partialBits);
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected int doFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits)
+ {
+ if (partialBits < 0 || partialBits > 7)
+ {
+ throw new IllegalArgumentException("'partialBits' must be in the range [0,7]");
+ }
+
+ int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits);
+ int finalBits = partialBits + 4;
+
+ if (finalBits >= 8)
+ {
+ oneByte[0] = (byte)finalInput;
+ absorb(oneByte, 0, 8);
+ finalBits -= 8;
+ finalInput >>>= 8;
+ }
+
+ if (finalBits > 0)
+ {
+ oneByte[0] = (byte)finalInput;
+ absorb(oneByte, 0, finalBits);
+ }
+
+ squeeze(out, outOff, ((long)outLen) * 8);
+
+ reset();
+
+ return outLen;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java
new file mode 100644
index 00000000..48ecbe3c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Support class for constructing integrated encryption ciphers
+ * for doing basic message exchanges on top of key agreement ciphers.
+ * Follows the description given in IEEE Std 1363a.
+ */
+public class OldIESEngine
+ extends IESEngine
+{
+ /**
+ * set up for use with stream mode, where the key derivation function
+ * is used to provide a stream of bytes to xor with the message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ */
+ public OldIESEngine(
+ BasicAgreement agree,
+ DerivationFunction kdf,
+ Mac mac)
+ {
+ super(agree, kdf, mac);
+ }
+
+
+ /**
+ * set up for use in conjunction with a block cipher to handle the
+ * message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ * @param cipher the cipher to used for encrypting the message
+ */
+ public OldIESEngine(
+ BasicAgreement agree,
+ DerivationFunction kdf,
+ Mac mac,
+ BufferedBlockCipher cipher)
+ {
+ super(agree, kdf, mac, cipher);
+ }
+
+ protected byte[] getLengthTag(byte[] p2)
+ {
+ byte[] L2 = new byte[4];
+ if (p2 != null)
+ {
+ Pack.intToBigEndian(p2.length * 8, L2, 0);
+ }
+ return L2;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
new file mode 100644
index 00000000..bf70eadb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
@@ -0,0 +1,267 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
+
+/**
+ * SM4 Block Cipher - SM4 is a 128 bit block cipher with a 128 bit key.
+ * <p>
+ * The implementation here is based on the document <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a>
+ * by Whitfield Diffie and George Ledin, which is a translation of Prof. LU Shu-wang's original standard.
+ * </p>
+ */
+public class SM4Engine
+ implements BlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+
+ private final static byte[] Sbox =
+ {
+ (byte)0xd6, (byte)0x90, (byte)0xe9, (byte)0xfe, (byte)0xcc, (byte)0xe1, (byte)0x3d, (byte)0xb7, (byte)0x16, (byte)0xb6, (byte)0x14, (byte)0xc2, (byte)0x28, (byte)0xfb, (byte)0x2c, (byte)0x05,
+ (byte)0x2b, (byte)0x67, (byte)0x9a, (byte)0x76, (byte)0x2a, (byte)0xbe, (byte)0x04, (byte)0xc3, (byte)0xaa, (byte)0x44, (byte)0x13, (byte)0x26, (byte)0x49, (byte)0x86, (byte)0x06, (byte)0x99,
+ (byte)0x9c, (byte)0x42, (byte)0x50, (byte)0xf4, (byte)0x91, (byte)0xef, (byte)0x98, (byte)0x7a, (byte)0x33, (byte)0x54, (byte)0x0b, (byte)0x43, (byte)0xed, (byte)0xcf, (byte)0xac, (byte)0x62,
+ (byte)0xe4, (byte)0xb3, (byte)0x1c, (byte)0xa9, (byte)0xc9, (byte)0x08, (byte)0xe8, (byte)0x95, (byte)0x80, (byte)0xdf, (byte)0x94, (byte)0xfa, (byte)0x75, (byte)0x8f, (byte)0x3f, (byte)0xa6,
+ (byte)0x47, (byte)0x07, (byte)0xa7, (byte)0xfc, (byte)0xf3, (byte)0x73, (byte)0x17, (byte)0xba, (byte)0x83, (byte)0x59, (byte)0x3c, (byte)0x19, (byte)0xe6, (byte)0x85, (byte)0x4f, (byte)0xa8,
+ (byte)0x68, (byte)0x6b, (byte)0x81, (byte)0xb2, (byte)0x71, (byte)0x64, (byte)0xda, (byte)0x8b, (byte)0xf8, (byte)0xeb, (byte)0x0f, (byte)0x4b, (byte)0x70, (byte)0x56, (byte)0x9d, (byte)0x35,
+ (byte)0x1e, (byte)0x24, (byte)0x0e, (byte)0x5e, (byte)0x63, (byte)0x58, (byte)0xd1, (byte)0xa2, (byte)0x25, (byte)0x22, (byte)0x7c, (byte)0x3b, (byte)0x01, (byte)0x21, (byte)0x78, (byte)0x87,
+ (byte)0xd4, (byte)0x00, (byte)0x46, (byte)0x57, (byte)0x9f, (byte)0xd3, (byte)0x27, (byte)0x52, (byte)0x4c, (byte)0x36, (byte)0x02, (byte)0xe7, (byte)0xa0, (byte)0xc4, (byte)0xc8, (byte)0x9e,
+ (byte)0xea, (byte)0xbf, (byte)0x8a, (byte)0xd2, (byte)0x40, (byte)0xc7, (byte)0x38, (byte)0xb5, (byte)0xa3, (byte)0xf7, (byte)0xf2, (byte)0xce, (byte)0xf9, (byte)0x61, (byte)0x15, (byte)0xa1,
+ (byte)0xe0, (byte)0xae, (byte)0x5d, (byte)0xa4, (byte)0x9b, (byte)0x34, (byte)0x1a, (byte)0x55, (byte)0xad, (byte)0x93, (byte)0x32, (byte)0x30, (byte)0xf5, (byte)0x8c, (byte)0xb1, (byte)0xe3,
+ (byte)0x1d, (byte)0xf6, (byte)0xe2, (byte)0x2e, (byte)0x82, (byte)0x66, (byte)0xca, (byte)0x60, (byte)0xc0, (byte)0x29, (byte)0x23, (byte)0xab, (byte)0x0d, (byte)0x53, (byte)0x4e, (byte)0x6f,
+ (byte)0xd5, (byte)0xdb, (byte)0x37, (byte)0x45, (byte)0xde, (byte)0xfd, (byte)0x8e, (byte)0x2f, (byte)0x03, (byte)0xff, (byte)0x6a, (byte)0x72, (byte)0x6d, (byte)0x6c, (byte)0x5b, (byte)0x51,
+ (byte)0x8d, (byte)0x1b, (byte)0xaf, (byte)0x92, (byte)0xbb, (byte)0xdd, (byte)0xbc, (byte)0x7f, (byte)0x11, (byte)0xd9, (byte)0x5c, (byte)0x41, (byte)0x1f, (byte)0x10, (byte)0x5a, (byte)0xd8,
+ (byte)0x0a, (byte)0xc1, (byte)0x31, (byte)0x88, (byte)0xa5, (byte)0xcd, (byte)0x7b, (byte)0xbd, (byte)0x2d, (byte)0x74, (byte)0xd0, (byte)0x12, (byte)0xb8, (byte)0xe5, (byte)0xb4, (byte)0xb0,
+ (byte)0x89, (byte)0x69, (byte)0x97, (byte)0x4a, (byte)0x0c, (byte)0x96, (byte)0x77, (byte)0x7e, (byte)0x65, (byte)0xb9, (byte)0xf1, (byte)0x09, (byte)0xc5, (byte)0x6e, (byte)0xc6, (byte)0x84,
+ (byte)0x18, (byte)0xf0, (byte)0x7d, (byte)0xec, (byte)0x3a, (byte)0xdc, (byte)0x4d, (byte)0x20, (byte)0x79, (byte)0xee, (byte)0x5f, (byte)0x3e, (byte)0xd7, (byte)0xcb, (byte)0x39, (byte)0x48
+ };
+
+ private final static int[] CK =
+ {
+ 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
+ 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
+ 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
+ 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
+ 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
+ 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
+ 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
+ 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
+ };
+
+ private final static int[] FK =
+ {
+ 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
+ };
+
+ private final int[] X = new int[4];
+
+ private int[] rk;
+
+ private int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ // non-linear substitution tau.
+ private int tau(
+ int A)
+ {
+ int b0 = Sbox[(A >> 24) & 0xff] & 0xff;
+ int b1 = Sbox[(A >> 16) & 0xff] & 0xff;
+ int b2 = Sbox[(A >> 8) & 0xff] & 0xff;
+ int b3 = Sbox[A & 0xff] & 0xff;
+
+ return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+ }
+
+ private int L_ap(
+ int B)
+ {
+ return (B ^ (rotateLeft(B, 13)) ^ (rotateLeft(B, 23)));
+ }
+
+ private int T_ap(
+ int Z)
+ {
+ return L_ap(tau(Z));
+ }
+
+ // Key expansion
+ private int[] expandKey(boolean forEncryption, byte[] key)
+ {
+ int[] rk = new int[32];
+ int[] MK = new int[4];
+
+ MK[0] = Pack.bigEndianToInt(key, 0);
+ MK[1] = Pack.bigEndianToInt(key, 4);
+ MK[2] = Pack.bigEndianToInt(key, 8);
+ MK[3] = Pack.bigEndianToInt(key, 12);
+
+ int i;
+ int[] K = new int[4];
+ K[0] = MK[0] ^ FK[0];
+ K[1] = MK[1] ^ FK[1];
+ K[2] = MK[2] ^ FK[2];
+ K[3] = MK[3] ^ FK[3];
+
+ if (forEncryption)
+ {
+ rk[0] = K[0] ^ T_ap(K[1] ^ K[2] ^ K[3] ^ CK[0]);
+ rk[1] = K[1] ^ T_ap(K[2] ^ K[3] ^ rk[0] ^ CK[1]);
+ rk[2] = K[2] ^ T_ap(K[3] ^ rk[0] ^ rk[1] ^ CK[2]);
+ rk[3] = K[3] ^ T_ap(rk[0] ^ rk[1] ^ rk[2] ^ CK[3]);
+ for (i = 4; i < 32; i++)
+ {
+ rk[i] = rk[i - 4] ^ T_ap(rk[i - 3] ^ rk[i - 2] ^ rk[i - 1] ^ CK[i]);
+ }
+ }
+ else
+ {
+ rk[31] = K[0] ^ T_ap(K[1] ^ K[2] ^ K[3] ^ CK[0]);
+ rk[30] = K[1] ^ T_ap(K[2] ^ K[3] ^ rk[31] ^ CK[1]);
+ rk[29] = K[2] ^ T_ap(K[3] ^ rk[31] ^ rk[30] ^ CK[2]);
+ rk[28] = K[3] ^ T_ap(rk[31] ^ rk[30] ^ rk[29] ^ CK[3]);
+ for (i = 27; i >= 0; i--)
+ {
+ rk[i] = rk[i + 4] ^ T_ap(rk[i + 3] ^ rk[i + 2] ^ rk[i + 1] ^ CK[31 - i]);
+ }
+ }
+
+ return rk;
+ }
+
+
+ // Linear substitution L
+ private int L(int B)
+ {
+ int C;
+ C = (B ^ (rotateLeft(B, 2)) ^ (rotateLeft(B, 10)) ^ (rotateLeft(B,
+ 18)) ^ (rotateLeft(B, 24)));
+ return C;
+ }
+
+ // Mixer-substitution T
+ private int T(int Z)
+ {
+ return L(tau(Z));
+ }
+
+ // reverse substitution
+ private void R(int[] A, int off)
+ {
+ int off0 = off;
+ int off1 = off + 1;
+ int off2 = off + 2;
+ int off3 = off + 3;
+
+ A[off0] = A[off0] ^ A[off3];
+ A[off3] = A[off0] ^ A[off3];
+ A[off0] = A[off0] ^ A[off3];
+ A[off1] = A[off1] ^ A[off2];
+ A[off2] = A[off1] ^ A[off2];
+ A[off1] = A[off1] ^ A[off2];
+ }
+
+ // The round functions
+ private int F0(int[] X, int rk)
+ {
+ return (X[0] ^ T(X[1] ^ X[2] ^ X[3] ^ rk));
+ }
+
+ private int F1(int[] X, int rk)
+ {
+ return (X[1] ^ T(X[2] ^ X[3] ^ X[0] ^ rk));
+ }
+
+ private int F2(int[] X, int rk)
+ {
+ return (X[2] ^ T(X[3] ^ X[0] ^ X[1] ^ rk));
+ }
+
+ private int F3(int[] X, int rk)
+ {
+ return (X[3] ^ T(X[0] ^ X[1] ^ X[2] ^ rk));
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof KeyParameter)
+ {
+ byte[] key = ((KeyParameter)params).getKey();
+
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("SM4 requires a 128 bit key");
+ }
+
+ rk = expandKey(forEncryption, key);
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to SM4 init - " + params.getClass().getName());
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SM4";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (rk == null)
+ {
+ throw new IllegalStateException("SM4 not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ X[0] = Pack.bigEndianToInt(in, inOff);
+ X[1] = Pack.bigEndianToInt(in, inOff + 4);
+ X[2] = Pack.bigEndianToInt(in, inOff + 8);
+ X[3] = Pack.bigEndianToInt(in, inOff + 12);
+
+ int i;
+
+ for (i = 0; i < 32; i += 4)
+ {
+ X[0] = F0(X, rk[i]);
+ X[1] = F1(X, rk[i + 1]);
+ X[2] = F2(X, rk[i + 2]);
+ X[3] = F3(X, rk[i + 3]);
+ }
+ R(X, 0);
+
+ Pack.intToBigEndian(X[0], out, outOff);
+ Pack.intToBigEndian(X[1], out, outOff + 4);
+ Pack.intToBigEndian(X[2], out, outOff + 8);
+ Pack.intToBigEndian(X[3], out, outOff + 12);
+
+ return 16;
+ }
+
+ public void reset()
+ {
+
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java
new file mode 100644
index 00000000..c47b2c26
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java
@@ -0,0 +1,486 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+public abstract class SerpentEngineBase
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 16;
+
+ static final int ROUNDS = 32;
+ static final int PHI = 0x9E3779B9; // (sqrt(5) - 1) * 2**31
+
+ protected boolean encrypting;
+ protected int[] wKey;
+
+ protected int X0, X1, X2, X3; // registers
+
+ SerpentEngineBase()
+ {
+
+ }
+
+ /**
+ * initialise a Serpent cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @throws IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.wKey = makeWorkingKey(((KeyParameter)params).getKey());
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to " + getAlgorithmName() + " init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Serpent";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @return the number of bytes processed and produced.
+ * @throws DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @throws IllegalStateException if the cipher isn't initialised.
+ */
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (wKey == null)
+ {
+ throw new IllegalStateException(getAlgorithmName() + " not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ protected static int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ protected static int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+
+ /**
+ * The sboxes below are based on the work of Brian Gladman and
+ * Sam Simpson, whose original notice appears below.
+ * <p>
+ * For further details see:
+ * http://fp.gladman.plus.com/cryptography_technology/serpent/
+ */
+
+ /* Partially optimised Serpent S Box boolean functions derived */
+ /* using a recursive descent analyser but without a full search */
+ /* of all subtrees. This set of S boxes is the result of work */
+ /* by Sam Simpson and Brian Gladman using the spare time on a */
+ /* cluster of high capacity servers to search for S boxes with */
+ /* this customised search engine. There are now an average of */
+ /* 15.375 terms per S box. */
+ /* */
+ /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */
+ /* and Sam Simpson (s.simpson@mia.co.uk) */
+ /* 17th December 1998 */
+ /* */
+ /* We hereby give permission for information in this file to be */
+ /* used freely subject only to acknowledgement of its origin. */
+
+ /**
+ * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms.
+ */
+ protected final void sb0(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t3 = c ^ t1;
+ int t4 = b ^ t3;
+ X3 = (a & d) ^ t4;
+ int t7 = a ^ (b & t1);
+ X2 = t4 ^ (c | t7);
+ int t12 = X3 & (t3 ^ t7);
+ X1 = (~t3) ^ t12;
+ X0 = t12 ^ (~t7);
+ }
+
+ /**
+ * InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms.
+ */
+ protected final void ib0(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t4 = d ^ (t1 | t2);
+ int t5 = c ^ t4;
+ X2 = t2 ^ t5;
+ int t8 = t1 ^ (d & t2);
+ X1 = t4 ^ (X2 & t8);
+ X3 = (a & t4) ^ (t5 | X1);
+ X0 = X3 ^ (t5 ^ t8);
+ }
+
+ /**
+ * S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms.
+ */
+ protected final void sb1(int a, int b, int c, int d)
+ {
+ int t2 = b ^ (~a);
+ int t5 = c ^ (a | t2);
+ X2 = d ^ t5;
+ int t7 = b ^ (d | t2);
+ int t8 = t2 ^ X2;
+ X3 = t8 ^ (t5 & t7);
+ int t11 = t5 ^ t7;
+ X1 = X3 ^ t11;
+ X0 = t5 ^ (t8 & t11);
+ }
+
+ /**
+ * InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps.
+ */
+ protected final void ib1(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t3 = a ^ (b & t1);
+ int t4 = t1 ^ t3;
+ X3 = c ^ t4;
+ int t7 = b ^ (t1 & t3);
+ int t8 = X3 | t7;
+ X1 = t3 ^ t8;
+ int t10 = ~X1;
+ int t11 = X3 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = t4 ^ (t10 | t11);
+ }
+
+ /**
+ * S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms.
+ */
+ protected final void sb2(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = b ^ d;
+ int t3 = c & t1;
+ X0 = t2 ^ t3;
+ int t5 = c ^ t1;
+ int t6 = c ^ X0;
+ int t7 = b & t6;
+ X3 = t5 ^ t7;
+ X2 = a ^ ((d | t7) & (X0 | t5));
+ X1 = (t2 ^ X3) ^ (X2 ^ (d | t1));
+ }
+
+ /**
+ * InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps.
+ */
+ protected final void ib2(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t2 = ~t1;
+ int t3 = a ^ c;
+ int t4 = c ^ t1;
+ int t5 = b & t4;
+ X0 = t3 ^ t5;
+ int t7 = a | t2;
+ int t8 = d ^ t7;
+ int t9 = t3 | t8;
+ X3 = t1 ^ t9;
+ int t11 = ~t4;
+ int t12 = X0 | X3;
+ X1 = t11 ^ t12;
+ X2 = (d & t11) ^ (t3 ^ t12);
+ }
+
+ /**
+ * S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms.
+ */
+ protected final void sb3(int a, int b, int c, int d)
+ {
+ int t1 = a ^ b;
+ int t2 = a & c;
+ int t3 = a | d;
+ int t4 = c ^ d;
+ int t5 = t1 & t3;
+ int t6 = t2 | t5;
+ X2 = t4 ^ t6;
+ int t8 = b ^ t3;
+ int t9 = t6 ^ t8;
+ int t10 = t4 & t9;
+ X0 = t1 ^ t10;
+ int t12 = X2 & X0;
+ X1 = t9 ^ t12;
+ X3 = (b | d) ^ (t4 ^ t12);
+ }
+
+ /**
+ * InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms
+ */
+ protected final void ib3(int a, int b, int c, int d)
+ {
+ int t1 = a | b;
+ int t2 = b ^ c;
+ int t3 = b & t2;
+ int t4 = a ^ t3;
+ int t5 = c ^ t4;
+ int t6 = d | t4;
+ X0 = t2 ^ t6;
+ int t8 = t2 | t6;
+ int t9 = d ^ t8;
+ X2 = t5 ^ t9;
+ int t11 = t1 ^ t9;
+ int t12 = X0 & t11;
+ X3 = t4 ^ t12;
+ X1 = X3 ^ (X0 ^ t11);
+ }
+
+ /**
+ * S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms.
+ */
+ protected final void sb4(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t2 = d & t1;
+ int t3 = c ^ t2;
+ int t4 = b | t3;
+ X3 = t1 ^ t4;
+ int t6 = ~b;
+ int t7 = t1 | t6;
+ X0 = t3 ^ t7;
+ int t9 = a & X0;
+ int t10 = t1 ^ t6;
+ int t11 = t4 & t10;
+ X2 = t9 ^ t11;
+ X1 = (a ^ t3) ^ (t10 & X2);
+ }
+
+ /**
+ * InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms.
+ */
+ protected final void ib4(int a, int b, int c, int d)
+ {
+ int t1 = c | d;
+ int t2 = a & t1;
+ int t3 = b ^ t2;
+ int t4 = a & t3;
+ int t5 = c ^ t4;
+ X1 = d ^ t5;
+ int t7 = ~a;
+ int t8 = t5 & X1;
+ X3 = t3 ^ t8;
+ int t10 = X1 | t7;
+ int t11 = d ^ t10;
+ X0 = X3 ^ t11;
+ X2 = (t3 & t11) ^ (X1 ^ t7);
+ }
+
+ /**
+ * S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms.
+ */
+ protected final void sb5(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = a ^ d;
+ int t4 = c ^ t1;
+ int t5 = t2 | t3;
+ X0 = t4 ^ t5;
+ int t7 = d & X0;
+ int t8 = t2 ^ X0;
+ X1 = t7 ^ t8;
+ int t10 = t1 | X0;
+ int t11 = t2 | t7;
+ int t12 = t3 ^ t10;
+ X2 = t11 ^ t12;
+ X3 = (b ^ t7) ^ (X1 & t12);
+ }
+
+ /**
+ * InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms.
+ */
+ protected final void ib5(int a, int b, int c, int d)
+ {
+ int t1 = ~c;
+ int t2 = b & t1;
+ int t3 = d ^ t2;
+ int t4 = a & t3;
+ int t5 = b ^ t1;
+ X3 = t4 ^ t5;
+ int t7 = b | X3;
+ int t8 = a & t7;
+ X1 = t3 ^ t8;
+ int t10 = a | d;
+ int t11 = t1 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = (b & t10) ^ (t4 | (a ^ c));
+ }
+
+ /**
+ * S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms.
+ */
+ protected final void sb6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ d;
+ int t3 = b ^ t2;
+ int t4 = t1 | t2;
+ int t5 = c ^ t4;
+ X1 = b ^ t5;
+ int t7 = t2 | X1;
+ int t8 = d ^ t7;
+ int t9 = t5 & t8;
+ X2 = t3 ^ t9;
+ int t11 = t5 ^ t8;
+ X0 = X2 ^ t11;
+ X3 = (~t5) ^ (t3 & t11);
+ }
+
+ /**
+ * InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms.
+ */
+ protected final void ib6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = c ^ t2;
+ int t4 = c | t1;
+ int t5 = d ^ t4;
+ X1 = t3 ^ t5;
+ int t7 = t3 & t5;
+ int t8 = t2 ^ t7;
+ int t9 = b | t8;
+ X3 = t5 ^ t9;
+ int t11 = b | X3;
+ X0 = t8 ^ t11;
+ X2 = (d & t1) ^ (t3 ^ t11);
+ }
+
+ /**
+ * S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms.
+ */
+ protected final void sb7(int a, int b, int c, int d)
+ {
+ int t1 = b ^ c;
+ int t2 = c & t1;
+ int t3 = d ^ t2;
+ int t4 = a ^ t3;
+ int t5 = d | t1;
+ int t6 = t4 & t5;
+ X1 = b ^ t6;
+ int t8 = t3 | X1;
+ int t9 = a & t4;
+ X3 = t1 ^ t9;
+ int t11 = t4 ^ t8;
+ int t12 = X3 & t11;
+ X2 = t3 ^ t12;
+ X0 = (~t11) ^ (X3 & X2);
+ }
+
+ /**
+ * InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms.
+ */
+ protected final void ib7(int a, int b, int c, int d)
+ {
+ int t3 = c | (a & b);
+ int t4 = d & (a | b);
+ X3 = t3 ^ t4;
+ int t6 = ~d;
+ int t7 = b ^ t4;
+ int t9 = t7 | (X3 ^ t6);
+ X1 = a ^ t9;
+ X0 = (c ^ t7) ^ (d | X1);
+ X2 = (t3 ^ X1) ^ (X0 ^ (a & X3));
+ }
+
+ /**
+ * Apply the linear transformation to the register set.
+ */
+ protected final void LT()
+ {
+ int x0 = rotateLeft(X0, 13);
+ int x2 = rotateLeft(X2, 3);
+ int x1 = X1 ^ x0 ^ x2 ;
+ int x3 = X3 ^ x2 ^ x0 << 3;
+
+ X1 = rotateLeft(x1, 1);
+ X3 = rotateLeft(x3, 7);
+ X0 = rotateLeft(x0 ^ X1 ^ X3, 5);
+ X2 = rotateLeft(x2 ^ X3 ^ (X1 << 7), 22);
+ }
+
+ /**
+ * Apply the inverse of the linear transformation to the register set.
+ */
+ protected final void inverseLT()
+ {
+ int x2 = rotateRight(X2, 22) ^ X3 ^ (X1 << 7);
+ int x0 = rotateRight(X0, 5) ^ X1 ^ X3;
+ int x3 = rotateRight(X3, 7);
+ int x1 = rotateRight(X1, 1);
+ X3 = x3 ^ x2 ^ x0 << 3;
+ X1 = x1 ^ x0 ^ x2;
+ X2 = rotateRight(x2, 3);
+ X0 = rotateRight(x0, 13);
+ }
+
+ protected abstract int[] makeWorkingKey(byte[] key);
+
+ protected abstract void encryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+
+ protected abstract void decryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java
new file mode 100644
index 00000000..a34e0162
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java
@@ -0,0 +1,303 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.util.Pack;
+
+/**
+ * Tnepres is a 128-bit 32-round block cipher with variable key lengths,
+ * including 128, 192 and 256 bit keys conjectured to be at least as
+ * secure as three-key triple-DES.
+ * <p>
+ * Tnepres is based on Serpent which was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
+ * candidate algorithm for the NIST AES Quest. Unfortunately there was an endianness issue
+ * with test vectors in the AES submission and the resulting confusion lead to the Tnepres cipher
+ * as well, which is a byte swapped version of Serpent.
+ * <p>
+ * For full details see <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a>
+ */
+public final class TnepresEngine
+ extends SerpentEngineBase
+{
+ public String getAlgorithmName()
+ {
+ return "Tnepres";
+ }
+
+ /**
+ * Expand a user-supplied key material into a session key.
+ *
+ * @param key The user-key bytes (multiples of 4) to use.
+ * @exception IllegalArgumentException
+ */
+ protected int[] makeWorkingKey(
+ byte[] key)
+ throws IllegalArgumentException
+ {
+ //
+ // pad key to 256 bits
+ //
+ int[] kPad = new int[16];
+ int off = 0;
+ int length = 0;
+
+ for (off = key.length - 4; off > 0; off -= 4)
+ {
+ kPad[length++] = Pack.bigEndianToInt(key, off);
+ }
+
+ if (off == 0)
+ {
+ kPad[length++] = Pack.bigEndianToInt(key, 0);
+ if (length < 8)
+ {
+ kPad[length] = 1;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("key must be a multiple of 4 bytes");
+ }
+
+ //
+ // expand the padded key up to 33 x 128 bits of key material
+ //
+ int amount = (ROUNDS + 1) * 4;
+ int[] w = new int[amount];
+
+ //
+ // compute w0 to w7 from w-8 to w-1
+ //
+ for (int i = 8; i < 16; i++)
+ {
+ kPad[i] = rotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11);
+ }
+
+ System.arraycopy(kPad, 8, w, 0, 8);
+
+ //
+ // compute w8 to w136
+ //
+ for (int i = 8; i < amount; i++)
+ {
+ w[i] = rotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+ }
+
+ //
+ // create the working keys by processing w with the Sbox and IP
+ //
+ sb3(w[0], w[1], w[2], w[3]);
+ w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3;
+ sb2(w[4], w[5], w[6], w[7]);
+ w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3;
+ sb1(w[8], w[9], w[10], w[11]);
+ w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3;
+ sb0(w[12], w[13], w[14], w[15]);
+ w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3;
+ sb7(w[16], w[17], w[18], w[19]);
+ w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3;
+ sb6(w[20], w[21], w[22], w[23]);
+ w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3;
+ sb5(w[24], w[25], w[26], w[27]);
+ w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3;
+ sb4(w[28], w[29], w[30], w[31]);
+ w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3;
+ sb3(w[32], w[33], w[34], w[35]);
+ w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3;
+ sb2(w[36], w[37], w[38], w[39]);
+ w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3;
+ sb1(w[40], w[41], w[42], w[43]);
+ w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3;
+ sb0(w[44], w[45], w[46], w[47]);
+ w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3;
+ sb7(w[48], w[49], w[50], w[51]);
+ w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3;
+ sb6(w[52], w[53], w[54], w[55]);
+ w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3;
+ sb5(w[56], w[57], w[58], w[59]);
+ w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3;
+ sb4(w[60], w[61], w[62], w[63]);
+ w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3;
+ sb3(w[64], w[65], w[66], w[67]);
+ w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3;
+ sb2(w[68], w[69], w[70], w[71]);
+ w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3;
+ sb1(w[72], w[73], w[74], w[75]);
+ w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3;
+ sb0(w[76], w[77], w[78], w[79]);
+ w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3;
+ sb7(w[80], w[81], w[82], w[83]);
+ w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3;
+ sb6(w[84], w[85], w[86], w[87]);
+ w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3;
+ sb5(w[88], w[89], w[90], w[91]);
+ w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3;
+ sb4(w[92], w[93], w[94], w[95]);
+ w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3;
+ sb3(w[96], w[97], w[98], w[99]);
+ w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3;
+ sb2(w[100], w[101], w[102], w[103]);
+ w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3;
+ sb1(w[104], w[105], w[106], w[107]);
+ w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3;
+ sb0(w[108], w[109], w[110], w[111]);
+ w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3;
+ sb7(w[112], w[113], w[114], w[115]);
+ w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3;
+ sb6(w[116], w[117], w[118], w[119]);
+ w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3;
+ sb5(w[120], w[121], w[122], w[123]);
+ w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3;
+ sb4(w[124], w[125], w[126], w[127]);
+ w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3;
+ sb3(w[128], w[129], w[130], w[131]);
+ w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3;
+
+ return w;
+ }
+
+ /**
+ * Encrypt one block of plaintext.
+ *
+ * @param input the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param output the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ protected void encryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ X3 = Pack.bigEndianToInt(input, inOff);
+ X2 = Pack.bigEndianToInt(input, inOff + 4);
+ X1 = Pack.bigEndianToInt(input, inOff + 8);
+ X0 = Pack.bigEndianToInt(input, inOff + 12);
+
+ sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+ sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+ sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+ sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+ sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+ sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+ sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+ sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+ sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+ sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+ sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+ sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+ sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+ sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+ sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+ sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+ sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+ sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+ sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+ sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+ sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+ sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+ sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+ sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+ sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+ sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+ sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+ sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+ sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+ sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+ sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+ sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+ Pack.intToBigEndian(wKey[131] ^ X3, output, outOff);
+ Pack.intToBigEndian(wKey[130] ^ X2, output, outOff + 4);
+ Pack.intToBigEndian(wKey[129] ^ X1, output, outOff + 8);
+ Pack.intToBigEndian(wKey[128] ^ X0, output, outOff + 12);
+ }
+
+ /**
+ * Decrypt one block of ciphertext.
+ *
+ * @param input the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param output the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ protected void decryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ X3 = wKey[131] ^ Pack.bigEndianToInt(input, inOff);
+ X2 = wKey[130] ^ Pack.bigEndianToInt(input, inOff + 4);
+ X1 = wKey[129] ^ Pack.bigEndianToInt(input, inOff + 8);
+ X0 = wKey[128] ^ Pack.bigEndianToInt(input, inOff + 12);
+
+ ib7(X0, X1, X2, X3);
+ X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+ inverseLT(); ib0(X0, X1, X2, X3);
+
+ Pack.intToBigEndian(X3 ^ wKey[3], output, outOff);
+ Pack.intToBigEndian(X2 ^ wKey[2], output, outOff + 4);
+ Pack.intToBigEndian(X1 ^ wKey[1], output, outOff + 8);
+ Pack.intToBigEndian(X0 ^ wKey[0], output, outOff + 12);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java
new file mode 100644
index 00000000..b34e9938
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.crypto.prng;
+
+/**
+ * Utility methods for making use of EntropySources.
+ */
+public class EntropyUtil
+{
+ /**
+ * Generate numBytes worth of entropy from the passed in entropy source.
+ *
+ * @param entropySource the entropy source to request the data from.
+ * @param numBytes the number of bytes of entropy requested.
+ * @return a byte array populated with the random data.
+ */
+ public static byte[] generateSeed(EntropySource entropySource, int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ if (numBytes * 8 <= entropySource.entropySize())
+ {
+ byte[] ent = entropySource.getEntropy();
+
+ System.arraycopy(ent, 0, bytes, 0, bytes.length);
+ }
+ else
+ {
+ int entSize = entropySource.entropySize() / 8;
+
+ for (int i = 0; i < bytes.length; i += entSize)
+ {
+ byte[] ent = entropySource.getEntropy();
+
+ if (ent.length <= bytes.length - i)
+ {
+ System.arraycopy(ent, 0, bytes, i, ent.length);
+ }
+ else
+ {
+ System.arraycopy(ent, 0, bytes, i, bytes.length - i);
+ }
+ }
+ }
+
+ return bytes;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
new file mode 100644
index 00000000..2793d60a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto.signers;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Integers;
+
+public class ISOTrailers
+{
+ private static final Map<String, Integer> trailerMap;
+
+ static final public int TRAILER_IMPLICIT = 0xBC;
+
+ static final public int TRAILER_RIPEMD160 = 0x31CC;
+ static final public int TRAILER_RIPEMD128 = 0x32CC;
+ static final public int TRAILER_SHA1 = 0x33CC;
+ static final public int TRAILER_SHA256 = 0x34CC;
+ static final public int TRAILER_SHA512 = 0x35CC;
+ static final public int TRAILER_SHA384 = 0x36CC;
+ static final public int TRAILER_WHIRLPOOL = 0x37CC;
+ static final public int TRAILER_SHA224 = 0x38CC;
+ static final public int TRAILER_SHA512_224 = 0x39CC;
+ static final public int TRAILER_SHA512_256 = 0x40CC;
+
+ static
+ {
+ Map<String, Integer> trailers = new HashMap<String, Integer>();
+
+ trailers.put("RIPEMD128", Integers.valueOf(TRAILER_RIPEMD128));
+ trailers.put("RIPEMD160", Integers.valueOf(TRAILER_RIPEMD160));
+
+ trailers.put("SHA-1", Integers.valueOf(TRAILER_SHA1));
+ trailers.put("SHA-224", Integers.valueOf(TRAILER_SHA224));
+ trailers.put("SHA-256", Integers.valueOf(TRAILER_SHA256));
+ trailers.put("SHA-384", Integers.valueOf(TRAILER_SHA384));
+ trailers.put("SHA-512", Integers.valueOf(TRAILER_SHA512));
+ trailers.put("SHA-512/224", Integers.valueOf(TRAILER_SHA512_224));
+ trailers.put("SHA-512/256", Integers.valueOf(TRAILER_SHA512_256));
+
+ trailers.put("Whirlpool", Integers.valueOf(TRAILER_WHIRLPOOL));
+
+ trailerMap = Collections.unmodifiableMap(trailers);
+ }
+
+ public static Integer getTrailer(Digest digest)
+ {
+ return (Integer)trailerMap.get(digest.getAlgorithmName()); // JDK 1.4 compatibility
+ }
+
+ public static boolean noTrailerAvailable(Digest digest)
+ {
+ return !trailerMap.containsKey(digest.getAlgorithmName());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
new file mode 100644
index 00000000..e962af94
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
@@ -0,0 +1,159 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.UnsupportedEncodingException;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.Blake2bDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Blake2bDigestTest
+ extends SimpleTest
+{
+
+ private static final String[][] keyedTestVectors =
+ { // input/message, key, hash
+
+ // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt
+ {
+ "",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" },
+
+ {
+ "00",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" },
+
+ {
+ "0001",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" },
+
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" },
+
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" },
+
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" } };
+
+ private final static String[][] unkeyedTestVectors =
+ { // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c
+ // hash, input/message
+ // digests without leading $BLAKE2$
+ {
+ "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e",
+ "blake2" },
+ {
+ "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0",
+ "hello world" },
+ {
+ "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7",
+ "verystrongandlongpassword" },
+ {
+ "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
+ "The quick brown fox jumps over the lazy dog" },
+ {
+ "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
+ "" },
+ {
+ "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
+ "abc" },
+ };
+
+ public String getName()
+ {
+ return "Blake2b";
+ }
+
+ private void offsetTest(
+ Digest digest,
+ byte[] input,
+ byte[] expected)
+ {
+ byte[] resBuf = new byte[expected.length + 11];
+
+ digest.update(input, 0, input.length);
+
+ digest.doFinal(resBuf, 11);
+
+ if (!areEqual(Arrays.copyOfRange(resBuf, 11, resBuf.length), expected))
+ {
+ fail("Offset failed got " + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ public void performTest() throws Exception
+ {
+ // test keyed test vectors:
+
+ Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.decode(keyedTestVectors[0][1]));
+ for (int tv = 0; tv < keyedTestVectors.length; tv++)
+ {
+
+ byte[] input = Hex.decode(keyedTestVectors[tv][0]);
+ blake2bkeyed.reset();
+
+ blake2bkeyed.update(input, 0, input.length);
+ byte[] keyedHash = new byte[64];
+ blake2bkeyed.doFinal(keyedHash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(keyedTestVectors[tv][2]), keyedHash))
+ {
+ fail("Blake2b mismatch on test vector ",
+ keyedTestVectors[tv][2],
+ new String(Hex.encode(keyedHash)));
+ }
+
+ offsetTest(blake2bkeyed, input, keyedHash);
+ }
+
+ Blake2bDigest blake2bunkeyed = new Blake2bDigest();
+ // test unkeyed test vectors:
+ for (int i = 0; i < unkeyedTestVectors.length; i++)
+ {
+
+ try
+ {
+ // blake2bunkeyed.update(
+ // unkeyedTestVectors[i][1].getBytes("UTF-8"));
+ // test update(byte b)
+ byte[] unkeyedInput = unkeyedTestVectors[i][1]
+ .getBytes("UTF-8");
+ for (int j = 0; j < unkeyedInput.length; j++)
+ {
+ blake2bunkeyed.update(unkeyedInput[j]);
+ }
+ } catch (UnsupportedEncodingException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ byte[] unkeyedHash = new byte[64];
+ blake2bunkeyed.doFinal(unkeyedHash, 0);
+ blake2bunkeyed.reset();
+
+ if (!Arrays.areEqual(Hex.decode(unkeyedTestVectors[i][0]),
+ unkeyedHash))
+ {
+ fail("Blake2b mismatch on test vector ",
+ unkeyedTestVectors[i][0],
+ new String(Hex.encode(unkeyedHash)));
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception
+ {
+ runTest(new Blake2bDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
new file mode 100644
index 00000000..7d9bdf12
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
@@ -0,0 +1,363 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.KeccakDigest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Keccak Digest Test
+ */
+public class KeccakDigestTest
+ extends SimpleTest
+{
+ final static String[] messages = {
+ "",
+ "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67",
+ "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"
+ };
+
+ final static String[] digests288 = { // the default settings
+ "6753e3380c09e385d0339eb6b050a68f66cfd60a73476e6fd6adeb72f5edd7c6f04a5d01", // message[0]
+ "0bbe6afae0d7e89054085c1cc47b1689772c89a41796891e197d1ca1b76f288154933ded", // message[1]
+ "82558a209b960ddeb531e6dcb281885b2400ca160472462486e79f071e88a3330a8a303d", // message[2]
+ "94049e1ad7ef5d5b0df2b880489e7ab09ec937c3bfc1b04470e503e1ac7b1133c18f86da", // 64k a-test
+ "a9cb5a75b5b81b7528301e72553ed6770214fa963956e790528afe420de33c074e6f4220", // random alphabet test
+ "eadaf5ba2ad6a2f6f338fce0e1efdad2a61bb38f6be6068b01093977acf99e97a5d5827c" // extremely long data test
+ };
+
+ final static String[] digests224 = {
+ "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
+ "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
+ "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab",
+ "f621e11c142fbf35fa8c22841c3a812ba1e0151be4f38d80b9f1ff53",
+ "68b5fc8c87193155bba68a2485377e809ee4f81a85ef023b9e64add0",
+ "c42e4aee858e1a8ad2976896b9d23dd187f64436ee15969afdbc68c5"
+ };
+
+ final static String[] digests256 = {
+ "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
+ "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d",
+ "0047a916daa1f92130d870b542e22d3108444f5a7e4429f05762fb647e6ed9ed",
+ "db368762253ede6d4f1db87e0b799b96e554eae005747a2ea687456ca8bcbd03",
+ "5f313c39963dcf792b5470d4ade9f3a356a3e4021748690a958372e2b06f82a4"
+ };
+
+ final static String[] digests384 = {
+ "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
+ "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
+ "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b",
+ "c704cfe7a1a53208ca9526cd24251e0acdc252ecd978eee05acd16425cfb404ea81f5a9e2e5e97784d63ee6a0618a398",
+ "d4fe8586fd8f858dd2e4dee0bafc19b4c12b4e2a856054abc4b14927354931675cdcaf942267f204ea706c19f7beefc4",
+ "9b7168b4494a80a86408e6b9dc4e5a1837c85dd8ff452ed410f2832959c08c8c0d040a892eb9a755776372d4a8732315"
+ };
+
+ final static String[] digests512 = {
+ "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
+ "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
+ "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760",
+ "34341ead153aa1d1fdcf6cf624c2b4f6894b6fd16dc38bd4ec971ac0385ad54fafcb2e0ed86a1e509456f4246fdcb02c3172824cd649d9ad54c51f7fb49ea67c",
+ "dc44d4f4d36b07ab5fc04016cbe53548e5a7778671c58a43cb379fd00c06719b8073141fc22191ffc3db5f8b8983ae8341fa37f18c1c969664393aa5ceade64e",
+ "3e122edaf37398231cfaca4c7c216c9d66d5b899ec1d7ac617c40c7261906a45fc01617a021e5da3bd8d4182695b5cb785a28237cbb167590e34718e56d8aab8"
+ };
+
+ // test vectors from http://www.di-mgt.com.au/hmac_sha3_testvectors.html
+ final static byte[][] macKeys =
+ {
+ Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
+ Hex.decode("4a656665"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516171819"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaa"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaa"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+ };
+
+ final static String[] macData =
+ {
+ "4869205468657265",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +
+ "dddddddddddddddddddddddddddddddddddd",
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" +
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" +
+ "65204b6579202d2048617368204b6579204669727374",
+ "5468697320697320612074657374207573696e672061206c6172676572207468" +
+ "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+ "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+ "647320746f20626520686173686564206265666f7265206265696e6720757365" +
+ "642062792074686520484d414320616c676f726974686d2e",
+ "5468697320697320612074657374207573696e672061206c6172676572207468" +
+ "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+ "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+ "647320746f20626520686173686564206265666f7265206265696e6720757365\n" +
+ "642062792074686520484d414320616c676f726974686d2e"
+ };
+
+ final static String[] mac224 =
+ {
+ "b73d595a2ba9af815e9f2b4e53e78581ebd34a80b3bbaac4e702c4cc",
+ "e824fec96c074f22f99235bb942da1982664ab692ca8501053cbd414",
+ "770df38c99d6e2bacd68056dcfe07d4c89ae20b2686a6185e1faa449",
+ "305a8f2dfb94bad28861a03cbc4d590febe775c58cb4961c28428a0b",
+ "e7a52dfa45f95a217c100066b239aa8ad519be9b35d667268b1b57ff",
+ "ba13009405a929f398b348885caa5419191bb948ada32194afc84104",
+ "92649468be236c3c72c189909c063b13f994be05749dc91310db639e"
+ };
+
+ final static String[] mac256 =
+ {
+ "9663d10c73ee294054dc9faf95647cb99731d12210ff7075fb3d3395abfb9821",
+ "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1",
+ "95f43e50f8df80a21977d51a8db3ba572dcd71db24687e6f86f47c1139b26260",
+ "6331ba9b4af5804a68725b3663eb74814494b63c6093e35fb320a85d507936fd",
+ "b4d0cdee7ec2ba81a88b86918958312300a15622377929a054a9ce3ae1fac2b6",
+ "1fdc8cb4e27d07c10d897dec39c217792a6e64fa9c63a77ce42ad106ef284e02",
+ "fdaa10a0299aecff9bb411cf2d7748a4022e4a26be3fb5b11b33d8c2b7ef5484"
+ };
+
+ final static String[] mac384 =
+ {
+ "892dfdf5d51e4679bf320cd16d4c9dc6f749744608e003add7fba894acff87361efa4e5799be06b6461f43b60ae97048",
+ "5af5c9a77a23a6a93d80649e562ab77f4f3552e3c5caffd93bdf8b3cfc6920e3023fc26775d9df1f3c94613146ad2c9d",
+ "4243c29f2201992ff96441e3b91ff81d8c601d706fbc83252684a4bc51101ca9b2c06ddd03677303c502ac5331752a3c",
+ "b730724d3d4090cda1be799f63acbbe389fef7792fc18676fa5453aab398664650ed029c3498bbe8056f06c658e1e693",
+ "d62482ef601d7847439b55236e9679388ffcd53c62cd126f39be6ea63de762e26cd5974cb9a8de401b786b5555040f6f",
+ "4860ea191ac34994cf88957afe5a836ef36e4cc1a66d75bf77defb7576122d75f60660e4cf731c6effac06402787e2b9",
+ "fe9357e3cfa538eb0373a2ce8f1e26ad6590afdaf266f1300522e8896d27e73f654d0631c8fa598d4bb82af6b744f4f5"
+ };
+
+ final static String[] mac512 =
+ {
+ "8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755",
+ "c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb",
+ "eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66",
+ "b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb",
+ "d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5",
+ "2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572",
+ "6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8"
+ };
+
+ final static KeyParameter truncKey = new KeyParameter(Hex.decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"));
+ final static byte[] truncData = Hex.decode("546573742057697468205472756e636174696f6e");
+
+ final static byte[] trunc224 = Hex.decode("f52bbcfd654264e7133085c5e69b72c3");
+ final static byte[] trunc256 = Hex.decode("745e7e687f8335280d54202ef13cecc6");
+ final static byte[] trunc384 = Hex.decode("fa9aea2bc1e181e47cbb8c3df243814d");
+ final static byte[] trunc512 = Hex.decode("04c929fead434bba190dacfa554ce3f5");
+
+ final static byte[] xtremeData = Hex.decode("61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f");
+
+ KeccakDigestTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "Keccak";
+ }
+
+ private void testDigest(Digest digest, String[] expected)
+ {
+ byte[] hash = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i != messages.length; i++)
+ {
+ if (messages.length != 0)
+ {
+ byte[] data = Hex.decode(messages[i]);
+
+ digest.update(data, 0, data.length);
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[i]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " index " + i);
+ }
+ }
+
+ byte[] k64 = new byte[1024 * 64];
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ k64[i] = (byte)'a';
+ }
+
+ digest.update(k64, 0, k64.length);
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k a");
+ }
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ digest.update((byte)'a');
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k a single");
+ }
+
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ k64[i] = (byte)('a' + (i % 26));
+ }
+
+ digest.update(k64, 0, k64.length);
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k alpha");
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ digest.update(k64[i * 1024]);
+ digest.update(k64, i * 1024 + 1, 1023);
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k chunked alpha");
+ }
+
+ testDigestDoFinal(digest);
+
+ //
+ // extremely long data test
+ //
+// System.out.println("Starting very long");
+// for (int i = 0; i != 16384; i++)
+// {
+// for (int j = 0; j != 1024; j++)
+// {
+// digest.update(xtremeData, 0, xtremeData.length);
+// }
+// }
+//
+// digest.doFinal(hash, 0);
+//
+// if (!Arrays.areEqual(Hex.decode(expected[messages.length + 2]), hash))
+// {
+// fail("Keccak mismatch on " + digest.getAlgorithmName() + " extreme data test");
+// }
+// System.out.println("Done");
+ }
+
+ private void testDigestDoFinal(Digest digest)
+ {
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ for (int i = 0; i <= digest.getDigestSize(); ++i)
+ {
+ byte[] cmp = new byte[2 * digest.getDigestSize()];
+ System.arraycopy(hash, 0, cmp, i, hash.length);
+
+ byte[] buf = new byte[2 * digest.getDigestSize()];
+ digest.doFinal(buf, i);
+
+ if (!Arrays.areEqual(cmp, buf))
+ {
+ fail("Keccak offset doFinal on " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ private void testMac(Digest digest, byte[][] keys, String[] data, String[] expected, byte[] truncExpected)
+ {
+ Mac mac = new HMac(digest);
+
+ for (int i = 0; i != keys.length; i++)
+ {
+ mac.init(new KeyParameter(keys[i]));
+
+ byte[] mData = Hex.decode(data[i]);
+
+ mac.update(mData, 0, mData.length);
+
+ byte[] macV = new byte[mac.getMacSize()];
+
+ mac.doFinal(macV, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[i]), macV))
+ {
+ fail("Keccak HMAC mismatch on " + digest.getAlgorithmName());
+ }
+ }
+
+ mac = new HMac(digest);
+
+ mac.init(truncKey);
+
+ mac.update(truncData, 0, truncData.length);
+
+ byte[] macV = new byte[mac.getMacSize()];
+
+ mac.doFinal(macV, 0);
+
+ for (int i = 0; i != truncExpected.length; i++)
+ {
+ if (macV[i] != truncExpected[i])
+ {
+ fail("mismatch on truncated HMAC for " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ public void performTest() throws Exception
+ {
+ testDigest(new KeccakDigest(), digests288);
+ testDigest(new KeccakDigest(224), digests224);
+ testDigest(new KeccakDigest(256), digests256);
+ testDigest(new KeccakDigest(384), digests384);
+ testDigest(new KeccakDigest(512), digests512);
+
+ testMac(new KeccakDigest(224), macKeys, macData, mac224, trunc224);
+ testMac(new KeccakDigest(256), macKeys, macData, mac256, trunc256);
+ testMac(new KeccakDigest(384), macKeys, macData, mac384, trunc384);
+ testMac(new KeccakDigest(512), macKeys, macData, mac512, trunc512);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new KeccakDigest((KeccakDigest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KeccakDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java
new file mode 100644
index 00000000..166c49da
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.test;
+
+import java.util.Random;
+
+class RNGUtils
+{
+ public static int nextInt(Random rng, int n)
+ {
+ return rng.nextInt(n);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java
new file mode 100644
index 00000000..a3ec2eba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java
@@ -0,0 +1,305 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * SHAKE Digest Test
+ */
+public class SHAKEDigestTest
+ extends SimpleTest
+{
+ static class MySHAKEDigest extends SHAKEDigest
+ {
+ MySHAKEDigest(int bitLength)
+ {
+ super(bitLength);
+ }
+
+ int myDoFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits)
+ {
+ return doFinal(out, outOff, outLen, partialByte, partialBits);
+ }
+ }
+
+ SHAKEDigestTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "SHAKE";
+ }
+
+ public void performTest() throws Exception
+ {
+ testVectors();
+ }
+
+ public void testVectors() throws Exception
+ {
+ BufferedReader r = new BufferedReader(new InputStreamReader(
+ getClass().getResourceAsStream("SHAKETestVectors.txt")));
+
+ String line;
+ while (null != (line = readLine(r)))
+ {
+ if (line.length() != 0)
+ {
+ TestVector v = readTestVector(r, line);
+ runTestVector(v);
+ }
+ }
+
+ r.close();
+ }
+
+ private MySHAKEDigest createDigest(String algorithm) throws Exception
+ {
+ if (algorithm.startsWith("SHAKE-"))
+ {
+ int bits = parseDecimal(algorithm.substring("SHAKE-".length()));
+ return new MySHAKEDigest(bits);
+ }
+ throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+ }
+
+ private byte[] decodeBinary(String block)
+ {
+ int bits = block.length();
+ int fullBytes = bits / 8;
+ int totalBytes = (bits + 7) / 8;
+ byte[] result = new byte[totalBytes];
+
+ for (int i = 0; i < fullBytes; ++i)
+ {
+ String byteStr = reverse(block.substring(i * 8, (i + 1) * 8));
+ result[i] = (byte)parseBinary(byteStr);
+ }
+
+ if (totalBytes > fullBytes)
+ {
+ String byteStr = reverse(block.substring(fullBytes * 8));
+ result[fullBytes] = (byte)parseBinary(byteStr);
+ }
+
+ return result;
+ }
+
+ private int parseBinary(String s)
+ {
+ return Integer.parseInt(s, 2);
+ }
+
+ private int parseDecimal(String s)
+ {
+ return Integer.parseInt(s);
+ }
+
+ private String readBlock(BufferedReader r) throws IOException
+ {
+ StringBuffer b = new StringBuffer();
+ String line;
+ while ((line = readBlockLine(r)) != null)
+ {
+ b.append(line);
+ }
+ return b.toString();
+ }
+
+ private String readBlockLine(BufferedReader r) throws IOException
+ {
+ String line = readLine(r);
+ if (line == null || line.length() == 0)
+ {
+ return null;
+ }
+
+ char[] chars = line.toCharArray();
+
+ int pos = 0;
+ for (int i = 0; i != chars.length; i++)
+ {
+ if (chars[i] != ' ')
+ {
+ chars[pos++] = chars[i];
+ }
+ }
+
+ return new String(chars, 0, pos);
+ }
+
+ private TestVector readTestVector(BufferedReader r, String header) throws IOException
+ {
+ String[] parts = splitAround(header, TestVector.SAMPLE_OF);
+
+ String algorithm = parts[0];
+ int bits = parseDecimal(stripFromChar(parts[1], '-'));
+
+ skipUntil(r, TestVector.MSG_HEADER);
+ String messageBlock = readBlock(r);
+ if (messageBlock.length() != bits)
+ {
+ throw new IllegalStateException("Test vector length mismatch");
+ }
+ byte[] message = decodeBinary(messageBlock);
+
+ skipUntil(r, TestVector.OUTPUT_HEADER);
+ byte[] output = Hex.decode(readBlock(r));
+
+ return new TestVector(algorithm, bits, message, output);
+ }
+
+ private String readLine(BufferedReader r) throws IOException
+ {
+ String line = r.readLine();
+ return line == null ? null : stripFromChar(line, '#').trim();
+ }
+
+ private String requireLine(BufferedReader r) throws IOException
+ {
+ String line = readLine(r);
+ if (line == null)
+ {
+ throw new EOFException();
+ }
+ return line;
+ }
+
+ private String reverse(String s)
+ {
+ return new StringBuffer(s).reverse().toString();
+ }
+
+ private void runTestVector(TestVector v) throws Exception
+ {
+ int bits = v.getBits();
+ int partialBits = bits % 8;
+
+ byte[] expected = v.getOutput();
+
+// System.out.println(v.getAlgorithm() + " " + bits + "-bit");
+// System.out.println(Hex.toHexString(v.getMessage()).toUpperCase());
+// System.out.println(Hex.toHexString(expected).toUpperCase());
+
+ int outLen = expected.length;
+
+ MySHAKEDigest d = createDigest(v.getAlgorithm());
+ byte[] output = new byte[outLen];
+
+ byte[] m = v.getMessage();
+ if (partialBits == 0)
+ {
+ d.update(m, 0, m.length);
+ d.doFinal(output, 0, outLen);
+ }
+ else
+ {
+ d.update(m, 0, m.length - 1);
+ d.myDoFinal(output, 0, outLen, m[m.length - 1], partialBits);
+ }
+
+ if (!Arrays.areEqual(expected, output))
+ {
+ fail(v.getAlgorithm() + " " + v.getBits() + "-bit test vector hash mismatch");
+// System.err.println(v.getAlgorithm() + " " + v.getBits() + "-bit test vector hash mismatch");
+// System.err.println(Hex.toHexString(output).toUpperCase());
+ }
+ }
+
+ private void skipUntil(BufferedReader r, String header) throws IOException
+ {
+ String line;
+ do
+ {
+ line = requireLine(r);
+ }
+ while (line.length() == 0);
+ if (!line.equals(header))
+ {
+ throw new IOException("Expected: " + header);
+ }
+ }
+
+ private String[] splitAround(String s, String separator)
+ {
+ List strings = new ArrayList();
+
+ String remaining = s;
+ int index;
+
+ while ((index = remaining.indexOf(separator)) > 0)
+ {
+ strings.add(remaining.substring(0, index));
+ remaining = remaining.substring(index + separator.length());
+ }
+ strings.add(remaining);
+
+ return (String[])strings.toArray(new String[strings.size()]);
+ }
+
+ private String stripFromChar(String s, char c)
+ {
+ int i = s.indexOf(c);
+ if (i >= 0)
+ {
+ s = s.substring(0, i);
+ }
+ return s;
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHAKEDigestTest());
+ }
+
+ private static class TestVector
+ {
+ private static String SAMPLE_OF = " sample of ";
+ private static String MSG_HEADER = "Msg as bit string";
+ private static String OUTPUT_HEADER = "Output val is";
+
+ private String algorithm;
+ private int bits;
+ private byte[] message;
+ private byte[] output;
+
+ private TestVector(String algorithm, int bits, byte[] message, byte[] output)
+ {
+ this.algorithm = algorithm;
+ this.bits = bits;
+ this.message = message;
+ this.output = output;
+ }
+
+ public String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public int getBits()
+ {
+ return bits;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getOutput()
+ {
+ return output;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java
new file mode 100644
index 00000000..7c12e5b1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.SM4Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * SM4 tester, vectors from <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a>
+ */
+public class SM4Test
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new SM4Engine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")),
+ "0123456789abcdeffedcba9876543210",
+ "681edf34d206965e86b3e94f536e4246")
+ };
+
+ SM4Test()
+ {
+ super(tests, new SM4Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ test1000000();
+ }
+
+ private void test1000000()
+ {
+ byte[] plain = Hex.decode("0123456789abcdeffedcba9876543210");
+ byte[] key = Hex.decode("0123456789abcdeffedcba9876543210");
+ byte[] cipher = Hex.decode("595298c7c6fd271f0402f804c33d3f66");
+ byte[] buf = new byte[16];
+
+ BlockCipher engine = new SM4Engine();
+
+ engine.init(true, new KeyParameter(key));
+
+ System.arraycopy(plain, 0, buf, 0, buf.length);
+
+ for (int i = 0; i != 1000000; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!areEqual(cipher, buf))
+ {
+ fail("1000000 encryption test failed");
+ }
+
+ engine.init(false, new KeyParameter(key));
+
+ for (int i = 0; i != 1000000; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!areEqual(plain, buf))
+ {
+ fail("1000000 decryption test failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "SM4";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SM4Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java
new file mode 100644
index 00000000..6b873587
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.TnepresEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors based on Floppy 4 of the Serpent AES submission.
+ */
+public class TnepresTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000000", "8910494504181950f98dd998a82b6749"),
+ new BlockCipherVectorTest(1, new TnepresEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "10b5ffb720b8cb9002a1142b0ba2e94a"),
+ new BlockCipherVectorTest(2, new TnepresEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000008000000000000000000000", "4f057a42d8d5bd9746e434680ddcd5e5"),
+ new BlockCipherVectorTest(3, new TnepresEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000400000000000", "99407bf8582ef12550886ef5b6f169b9"),
+ new BlockCipherVectorTest(4, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "40000000000000000000000000000000", "d522a3b8d6d89d4d2a124fdd88f36896"),
+ new BlockCipherVectorTest(5, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000200000000000000000000", "189b8ec3470085b3da97e82ca8964e32"),
+ new BlockCipherVectorTest(6, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000000000000008000000000", "f77d868cf760b9143a89809510ccb099"),
+ new BlockCipherVectorTest(7, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "08000000000000000000000000000000", "d43b7b981b829342fce0e3ec6f5f4c82"),
+ new BlockCipherVectorTest(8, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000100000000000000", "0bf30e1a0c33ccf6d5293177886912a7"),
+ new BlockCipherVectorTest(9, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000001", "6a7f3b805d2ddcba49b89770ade5e507"),
+ new BlockCipherVectorTest(10, new TnepresEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "49afbfad9d5a34052cd8ffa5986bd2dd"),
+ new BlockCipherVectorTest(11, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000004000000000000000000000")),
+ "00000000000000000000000000000000", "ba8829b1de058c4b48615d851fc74f17"),
+ new BlockCipherVectorTest(12, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000100000000")),
+ "00000000000000000000000000000000", "89f64377bf1e8a46c8247044e8056a98"),
+/*
+ new BlockCipherMonteCarloTest(13, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "003380e19f10065740394f48e2fe80b7"),
+*/
+ new BlockCipherMonteCarloTest(13, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "4db75303d815c2f7cc6ca935d1c5a046"),
+/*
+ new BlockCipherMonteCarloTest(14, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "afb5425426906db26b70bdf842ac5400"),
+*/
+ new BlockCipherMonteCarloTest(14, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "fc53a50f4d3bc9836001893d2f41742d"),
+/*
+ new BlockCipherMonteCarloTest(15, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "1949d506ada7de1f1344986e8ea049b2"),
+*/
+ new BlockCipherMonteCarloTest(15, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "77117e6a9e80f40b2a36b7d755573c2d"),
+/*
+ new BlockCipherMonteCarloTest(16, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "e57f45559027cb1f2ed9603d814e1c34"),
+*/
+ new BlockCipherMonteCarloTest(16, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "dcd7f13ea0dcdfd0139d1a42e2ffb84b")
+ };
+
+ TnepresTest()
+ {
+ super(tests, new TnepresEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ doCbcMonte(new byte[16], new byte[16], new byte[16], Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"));
+ doCbcMonte(Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"), Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"), Hex.decode("b4813d8a66244188b9e92c75913fa2f4"), Hex.decode("f86b2c265b9c75869f31e2c684c13e9f"));
+ }
+
+ private void doCbcMonte(byte[] key, byte[] iv, byte[] pt, byte[] expected)
+ {
+ BlockCipher c = new TnepresEngine();
+
+ byte[] ct = new byte[16];
+
+ System.arraycopy(iv, 0, ct, 0, 16);
+
+ for (int i = 0; i < 10000; i++)
+ {
+ for (int k = 0; k != iv.length; k++)
+ {
+ iv[k] ^= pt[k];
+ }
+ System.arraycopy(ct, 0, pt, 0, 16);
+
+ c.init(true, new KeyParameter(key));
+
+ c.processBlock(iv, 0, ct, 0);
+
+ System.arraycopy(ct, 0, iv, 0, 16);
+ }
+
+ if (!Arrays.areEqual(expected, ct))
+ {
+ fail("CBC monte test failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "Tnepres";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TnepresTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java
new file mode 100644
index 00000000..81f86e57
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.util.Properties;
+
+public interface CAVPListener
+{
+ public void setup();
+
+ public void receiveStart(String name);
+
+ public void receiveCAVPVectors(String name, Properties config, Properties vectors);
+
+ public void receiveCommentLine(String commentLine);
+
+ public void receiveEnd();
+
+ public void tearDown();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java
new file mode 100644
index 00000000..9cd33afc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.macs.HMac;
+
+public class CAVPReader
+{
+
+ private static final Pattern COMMENT_PATTERN = Pattern.compile("^\\s*\\#\\s*(.*)$");
+ private static final Pattern CONFIG_PATTERN = Pattern.compile("^\\s*+\\[\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+\\]\\s*+$");
+ private static final Pattern VECTOR_PATTERN = Pattern.compile("^\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+$");
+ private static final Pattern EMPTY_PATTERN = Pattern.compile("^\\s*+$");
+ static final Pattern PATTERN_FOR_R = Pattern.compile("(\\d+)_BITS");
+ private final CAVPListener listener;
+ private String name;
+ private BufferedReader lineReader;
+
+
+ public CAVPReader(CAVPListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public void setInput(String name, Reader reader)
+ {
+ this.name = name;
+ this.lineReader = new BufferedReader(reader);
+ }
+
+ public void readAll()
+ throws IOException
+ {
+
+ listener.setup();
+
+ Properties config = new Properties();
+
+ boolean startNewVector = true;
+
+ Properties vectors = new Properties();
+
+ while (true)
+ {
+ final String line = lineReader.readLine();
+ if (line == null)
+ {
+ listener.receiveEnd();
+ break;
+ }
+
+ final Matcher commentMatcher = COMMENT_PATTERN.matcher(line);
+ if (commentMatcher.matches())
+ {
+ listener.receiveCommentLine(commentMatcher.group(1));
+ continue;
+ }
+
+ final Matcher configMatcher = CONFIG_PATTERN.matcher(line);
+ if (configMatcher.matches())
+ {
+ config.put(configMatcher.group(1), configMatcher.group(2));
+ continue;
+ }
+
+ final Matcher vectorMatcher = VECTOR_PATTERN.matcher(line);
+ if (vectorMatcher.matches())
+ {
+ vectors.put(vectorMatcher.group(1), vectorMatcher.group(2));
+ startNewVector = false;
+ continue;
+ }
+
+ final Matcher emptyMatcher = EMPTY_PATTERN.matcher(line);
+ if (emptyMatcher.matches())
+ {
+ if (startNewVector)
+ {
+ continue;
+ }
+
+ listener.receiveCAVPVectors(name, config, vectors);
+ vectors = new Properties();
+ startNewVector = true;
+ }
+ }
+
+ listener.tearDown();
+ }
+
+ static Mac createPRF(Properties config)
+ {
+ final Mac prf;
+ if (config.getProperty("PRF").matches("CMAC_AES\\d\\d\\d"))
+ {
+ BlockCipher blockCipher = new AESFastEngine();
+ prf = new CMac(blockCipher);
+ }
+ else if (config.getProperty("PRF").matches("CMAC_TDES\\d"))
+ {
+ BlockCipher blockCipher = new DESedeEngine();
+ prf = new CMac(blockCipher);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA1"))
+ {
+ Digest digest = new SHA1Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA224"))
+ {
+ Digest digest = new SHA224Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA256"))
+ {
+ Digest digest = new SHA256Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA384"))
+ {
+ Digest digest = new SHA384Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA512"))
+ {
+ Digest digest = new SHA512Digest();
+ prf = new HMac(digest);
+ }
+ else
+ {
+ throw new IllegalStateException("Unknown Mac for PRF");
+ }
+ return prf;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java
new file mode 100644
index 00000000..81f10824
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFCounterBytesGenerator;
+import org.bouncycastle.crypto.params.KDFCounterParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFCounterBytesGenerator gen = new KDFCounterBytesGenerator(prf);
+
+
+ Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN"));
+ if (!matcherForR.matches())
+ {
+ throw new IllegalStateException("RLEN value should always match");
+ }
+ final int r = Integer.parseInt(matcherForR.group(1));
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+
+ //Three variants of this KDF are possible, with the counter before the fixed data, after the fixed data, or in the middle of the fixed data.
+ if (config.getProperty("CTRLOCATION").matches("BEFORE_FIXED"))
+ {
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFCounterParameters params = new KDFCounterParameters(ki, null, fixedInputData, r);
+ gen.init(params);
+ }
+ else if (config.getProperty("CTRLOCATION").matches("AFTER_FIXED"))
+ {
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFCounterParameters params = new KDFCounterParameters(ki, fixedInputData, null, r);
+ gen.init(params);
+ }
+ else if (config.getProperty("CTRLOCATION").matches("MIDDLE_FIXED"))
+ {
+ final byte[] DataBeforeCtrData = Hex.decode(vectors.getProperty("DataBeforeCtrData"));
+ final byte[] DataAfterCtrData = Hex.decode(vectors.getProperty("DataAfterCtrData"));
+ final KDFCounterParameters params = new KDFCounterParameters(ki, DataBeforeCtrData, DataAfterCtrData, r);
+ gen.init(params);
+ }
+ else
+ {
+ return; // Unknown CTRLOCATION
+ }
+
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFCTR.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java
new file mode 100644
index 00000000..5b3df023
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator;
+import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFDoublePipelineCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+ // out.println(" === " + name + " === ");
+ // out.println(" --- config --- ");
+ // out.println(config);
+ // out.println(" --- vectors --- ");
+ // out.println(vectors);
+
+ // always skip AFTER_FIXED
+ if (!config.getProperty("CTRLOCATION").matches("AFTER_ITER"))
+ {
+ return;
+ }
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFDoublePipelineIterationBytesGenerator gen = new KDFDoublePipelineIterationBytesGenerator(prf);
+
+
+ Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN"));
+ if (!matcherForR.matches())
+ {
+ throw new IllegalStateException("RLEN value should always match");
+ }
+ final int r = Integer.parseInt(matcherForR.group(1));
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFDoublePipelineIterationParameters params = KDFDoublePipelineIterationParameters.createWithCounter(ki, fixedInputData, r);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFDblPipelineCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java
new file mode 100644
index 00000000..3923f9a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator;
+import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFDoublePipelineIterationNoCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFDoublePipelineIterationBytesGenerator gen = new KDFDoublePipelineIterationBytesGenerator(prf);
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFDoublePipelineIterationParameters params = KDFDoublePipelineIterationParameters.createWithoutCounter(ki, fixedInputData);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFDblPipelineNoCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java
new file mode 100644
index 00000000..6f8a0fde
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFFeedbackBytesGenerator;
+import org.bouncycastle.crypto.params.KDFFeedbackParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFFeedbackCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+ // out.println(" === " + name + " === ");
+ // out.println(" --- config --- ");
+ // out.println(config);
+ // out.println(" --- vectors --- ");
+ // out.println(vectors);
+
+ // always skip AFTER_FIXED
+ if (!config.getProperty("CTRLOCATION").matches("AFTER_ITER"))
+ {
+ return;
+ }
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFFeedbackBytesGenerator gen = new KDFFeedbackBytesGenerator(prf);
+
+
+ Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN"));
+ if (!matcherForR.matches())
+ {
+ throw new IllegalStateException("RLEN value should always match");
+ }
+ final int r = Integer.parseInt(matcherForR.group(1));
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] iv = Hex.decode(vectors.getProperty("IV"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFFeedbackParameters params = KDFFeedbackParameters.createWithCounter(ki, iv, fixedInputData, r);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFFeedbackCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java
new file mode 100644
index 00000000..cd7d8b80
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFFeedbackBytesGenerator;
+import org.bouncycastle.crypto.params.KDFFeedbackParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFFeedbackNoCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFFeedbackBytesGenerator gen = new KDFFeedbackBytesGenerator(prf);
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] iv = Hex.decode(vectors.getProperty("IV"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFFeedbackParameters params = KDFFeedbackParameters.createWithoutCounter(ki, iv, fixedInputData);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+// out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFFeedbackNoCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java
new file mode 100644
index 00000000..34bb035a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.InputStream;
+
+public class ByteQueueInputStream
+ extends InputStream
+{
+ private ByteQueue buffer;
+
+ public ByteQueueInputStream()
+ {
+ buffer = new ByteQueue();
+ }
+
+ public void addBytes(byte[] bytes)
+ {
+ buffer.addData(bytes, 0, bytes.length);
+ }
+
+ public int peek(byte[] buf)
+ {
+ int bytesToRead = Math.min(buffer.available(), buf.length);
+ buffer.read(buf, 0, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ public int read()
+ {
+ if (buffer.available() == 0)
+ {
+ return -1;
+ }
+ return buffer.removeData(1, 0)[0] & 0xFF;
+ }
+
+ public int read(byte[] b)
+ {
+ return read(b, 0, b.length);
+ }
+
+ public int read(byte[] b, int off, int len)
+ {
+ int bytesToRead = Math.min(buffer.available(), len);
+ buffer.removeData(b, off, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ public long skip(long n)
+ {
+ int bytesToRemove = Math.min((int)n, buffer.available());
+ buffer.removeData(bytesToRemove);
+ return bytesToRemove;
+ }
+
+ public int available()
+ {
+ return buffer.available();
+ }
+
+ public void close()
+ {
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
new file mode 100644
index 00000000..aa3b36a4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.tls.ByteQueue;
+
+public class ByteQueueOutputStream
+ extends OutputStream
+{
+ private ByteQueue buffer;
+
+ public ByteQueueOutputStream()
+ {
+ buffer = new ByteQueue();
+ }
+
+ public ByteQueue getBuffer()
+ {
+ return buffer;
+ }
+
+ public void write(int b) throws IOException
+ {
+ buffer.addData(new byte[]{ (byte)b }, 0, 1);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ buffer.addData(b, off, len);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java
new file mode 100644
index 00000000..de4d5598
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.crypto.tls.ByteQueueInputStream;
+import org.bouncycastle.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class ByteQueueInputStreamTest
+ extends TestCase
+{
+ public void testAvailable()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+
+ // buffer is empty
+ assertEquals(0, in.available());
+
+ // after adding once
+ in.addBytes(new byte[10]);
+ assertEquals(10, in.available());
+
+ // after adding more than once
+ in.addBytes(new byte[5]);
+ assertEquals(15, in.available());
+
+ // after reading a single byte
+ in.read();
+ assertEquals(14, in.available());
+
+ // after reading into a byte array
+ in.read(new byte[4]);
+ assertEquals(10, in.available());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testSkip()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+
+ // skip when buffer is empty
+ assertEquals(0, in.skip(10));
+
+ // skip equal to available
+ in.addBytes(new byte[2]);
+ assertEquals(2, in.skip(2));
+ assertEquals(0, in.available());
+
+ // skip less than available
+ in.addBytes(new byte[10]);
+ assertEquals(5, in.skip(5));
+ assertEquals(5, in.available());
+
+ // skip more than available
+ assertEquals(5, in.skip(20));
+ assertEquals(0, in.available());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testRead()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+ in.addBytes(new byte[]{ 0x01, 0x02 });
+ in.addBytes(new byte[]{ 0x03 });
+
+ assertEquals(0x01, in.read());
+ assertEquals(0x02, in.read());
+ assertEquals(0x03, in.read());
+ assertEquals(-1, in.read());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testReadArray()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+ in.addBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+
+ byte[] buffer = new byte[5];
+
+ // read less than available into specified position
+ assertEquals(1, in.read(buffer, 2, 1));
+ assertArrayEquals(new byte[]{ 0x00, 0x00, 0x01, 0x00, 0x00 }, buffer);
+
+ // read equal to available
+ assertEquals(5, in.read(buffer));
+ assertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+
+ // read more than available
+ in.addBytes(new byte[]{ 0x01, 0x02, 0x03 });
+ assertEquals(3, in.read(buffer));
+ assertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer);
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testPeek()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+
+ byte[] buffer = new byte[5];
+
+ // peek more than available
+ assertEquals(0, in.peek(buffer));
+ assertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }, buffer);
+
+ // peek less than available
+ in.addBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+ assertEquals(5, in.peek(buffer));
+ assertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05 }, buffer);
+ assertEquals(6, in.available());
+
+ // peek equal to available
+ in.read();
+ assertEquals(5, in.peek(buffer));
+ assertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+ assertEquals(5, in.available());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ private static void assertArrayEquals(byte[] a, byte[] b)
+ {
+ assertTrue(Arrays.areEqual(a, b));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java
new file mode 100644
index 00000000..84109cdb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DTLSClientProtocol;
+import org.bouncycastle.crypto.tls.DigitallySigned;
+
+class DTLSTestClientProtocol extends DTLSClientProtocol
+{
+ protected final TlsTestConfig config;
+
+ public DTLSTestClientProtocol(SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(secureRandom);
+
+ this.config = config;
+ }
+
+ protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify)
+ throws IOException
+ {
+ if (certificateVerify.getAlgorithm() != null && config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.getSignature());
+ }
+
+ return super.generateCertificateVerify(state, certificateVerify);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java
new file mode 100644
index 00000000..9244eeb8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DTLSServerProtocol;
+
+class DTLSTestServerProtocol extends DTLSServerProtocol
+{
+ protected final TlsTestConfig config;
+
+ public DTLSTestServerProtocol(SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(secureRandom);
+
+ this.config = config;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java
new file mode 100644
index 00000000..564bb742
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class TlsProtocolNonBlockingTest
+ extends TestCase
+{
+ public void testClientServerFragmented() throws IOException
+ {
+ // tests if it's really non-blocking when partial records arrive
+ testClientServer(true);
+ }
+
+ public void testClientServerNonFragmented() throws IOException
+ {
+ testClientServer(false);
+ }
+
+ private static void testClientServer(boolean fragment) throws IOException
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(secureRandom);
+
+ clientProtocol.connect(new MockTlsClient(null));
+ serverProtocol.accept(new MockTlsServer());
+
+ // pump handshake
+ boolean hadDataFromServer = true;
+ boolean hadDataFromClient = true;
+ while (hadDataFromServer || hadDataFromClient)
+ {
+ hadDataFromServer = pumpData(serverProtocol, clientProtocol, fragment);
+ hadDataFromClient = pumpData(clientProtocol, serverProtocol, fragment);
+ }
+
+ // send data in both directions
+ byte[] data = new byte[1024];
+ secureRandom.nextBytes(data);
+ writeAndRead(clientProtocol, serverProtocol, data, fragment);
+ writeAndRead(serverProtocol, clientProtocol, data, fragment);
+
+ // close the connection
+ clientProtocol.close();
+ pumpData(clientProtocol, serverProtocol, fragment);
+ checkClosed(serverProtocol);
+ checkClosed(clientProtocol);
+ }
+
+ private static void writeAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, boolean fragment)
+ throws IOException
+ {
+ int dataSize = data.length;
+ writer.offerOutput(data, 0, dataSize);
+ pumpData(writer, reader, fragment);
+
+ assertEquals(dataSize, reader.getAvailableInputBytes());
+ byte[] readData = new byte[dataSize];
+ reader.readInput(readData, 0, dataSize);
+ assertArrayEquals(data, readData);
+ }
+
+ private static boolean pumpData(TlsProtocol from, TlsProtocol to, boolean fragment) throws IOException
+ {
+ int byteCount = from.getAvailableOutputBytes();
+ if (byteCount == 0)
+ {
+ return false;
+ }
+
+ if (fragment)
+ {
+ while (from.getAvailableOutputBytes() > 0)
+ {
+ byte[] buffer = new byte[1];
+ from.readOutput(buffer, 0, 1);
+ to.offerInput(buffer);
+ }
+ }
+ else
+ {
+ byte[] buffer = new byte[byteCount];
+ from.readOutput(buffer, 0, buffer.length);
+ to.offerInput(buffer);
+ }
+
+ return true;
+ }
+
+ private static void checkClosed(TlsProtocol protocol)
+ {
+ assertTrue(protocol.isClosed());
+
+ try
+ {
+ protocol.offerInput(new byte[10]);
+ fail("Input was accepted after close");
+ }
+ catch (IOException e)
+ {
+ }
+
+ try
+ {
+ protocol.offerOutput(new byte[10], 0, 10);
+ fail("Output was accepted after close");
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ private static void assertArrayEquals(byte[] a, byte[] b)
+ {
+ assertTrue(Arrays.areEqual(a, b));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java
new file mode 100644
index 00000000..2f6f751e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DigitallySigned;
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+
+class TlsTestClientProtocol extends TlsClientProtocol
+{
+ protected final TlsTestConfig config;
+
+ public TlsTestClientProtocol(InputStream input, OutputStream output, SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(input, output, secureRandom);
+
+ this.config = config;
+ }
+
+ protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify) throws IOException
+ {
+ if (certificateVerify.getAlgorithm() != null && config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.getSignature());
+ }
+
+ super.sendCertificateVerifyMessage(certificateVerify);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java
new file mode 100644
index 00000000..0bfc985b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+
+class TlsTestServerProtocol extends TlsServerProtocol
+{
+ protected final TlsTestConfig config;
+
+ public TlsTestServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(input, output, secureRandom);
+
+ this.config = config;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java
new file mode 100644
index 00000000..d895a1fd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.crypto.util;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Builder and holder class for preparing SP 800-56A compliant MacData. Elements in the data are encoded
+ * as DER objects with empty octet strings used to represent nulls in compulsory fields.
+ */
+public final class DERMacData
+{
+ public enum Type
+ {
+ UNILATERALU("KC_1_U"),
+ UNILATERALV("KC_1_V"),
+ BILATERALU("KC_2_U"),
+ BILATERALV("KC_2_V");
+
+ private final String enc;
+
+ Type(String enc)
+ {
+ this.enc = enc;
+ }
+
+ public byte[] getHeader()
+ {
+ return Strings.toByteArray(enc);
+ }
+ }
+
+ /**
+ * Builder to create OtherInfo
+ */
+ public static final class Builder
+ {
+ private final Type type;
+
+ private ASN1OctetString idU;
+ private ASN1OctetString idV;
+ private ASN1OctetString ephemDataU;
+ private ASN1OctetString ephemDataV;
+ private byte[] text;
+
+ /**
+ * Create a basic builder with just the compulsory fields.
+ *
+ * @param type the MAC header
+ * @param idU sender party ID.
+ * @param idV receiver party ID.
+ * @param ephemDataU ephemeral data from sender.
+ * @param ephemDataV ephemeral data from receiver.
+ */
+ public Builder(Type type, byte[] idU, byte[] idV, byte[] ephemDataU, byte[] ephemDataV)
+ {
+ this.type = type;
+ this.idU = DerUtil.getOctetString(idU);
+ this.idV = DerUtil.getOctetString(idV);
+ this.ephemDataU = DerUtil.getOctetString(ephemDataU);
+ this.ephemDataV = DerUtil.getOctetString(ephemDataV);
+ }
+
+ /**
+ * Add optional text.
+ *
+ * @param text optional agreed text to add to the MAC.
+ * @return the current builder instance.
+ */
+ public Builder withText(byte[] text)
+ {
+ this.text = DerUtil.toByteArray(new DERTaggedObject(false, 0, DerUtil.getOctetString(text)));
+
+ return this;
+ }
+
+ public DERMacData build()
+ {
+ switch (type)
+ {
+ case UNILATERALU:
+ case BILATERALU:
+ return new DERMacData(concatenate(type.getHeader(),
+ DerUtil.toByteArray(idU), DerUtil.toByteArray(idV),
+ DerUtil.toByteArray(ephemDataU), DerUtil.toByteArray(ephemDataV), text));
+ case UNILATERALV:
+ case BILATERALV:
+ return new DERMacData(concatenate(type.getHeader(),
+ DerUtil.toByteArray(idV), DerUtil.toByteArray(idU),
+ DerUtil.toByteArray(ephemDataV), DerUtil.toByteArray(ephemDataU), text));
+ }
+
+ throw new IllegalStateException("Unknown type encountered in build"); // should never happen
+ }
+
+ private byte[] concatenate(byte[] header, byte[] id1, byte[] id2, byte[] ed1, byte[] ed2, byte[] text)
+ {
+ return Arrays.concatenate(Arrays.concatenate(header, id1, id2), Arrays.concatenate(ed1, ed2, text));
+ }
+ }
+
+ private final byte[] macData;
+
+ private DERMacData(byte[] macData)
+ {
+ this.macData = macData;
+ }
+
+ public byte[] getMacData()
+ {
+ return Arrays.clone(macData);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java
new file mode 100644
index 00000000..4e821f7e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Builder and holder class for preparing SP 800-56A compliant OtherInfo. The data is ultimately encoded as a DER SEQUENCE.
+ * Empty octet strings are used to represent nulls in compulsory fields.
+ */
+public class DEROtherInfo
+{
+ /**
+ * Builder to create OtherInfo
+ */
+ public static final class Builder
+ {
+ private final AlgorithmIdentifier algorithmID;
+ private final ASN1OctetString partyUVInfo;
+ private final ASN1OctetString partyVInfo;
+
+ private ASN1TaggedObject suppPubInfo;
+ private ASN1TaggedObject suppPrivInfo;
+
+ /**
+ * Create a basic builder with just the compulsory fields.
+ *
+ * @param algorithmID the algorithm associated with this invocation of the KDF.
+ * @param partyUInfo sender party info.
+ * @param partyVInfo receiver party info.
+ */
+ public Builder(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo)
+ {
+ this.algorithmID = algorithmID;
+ this.partyUVInfo = DerUtil.getOctetString(partyUInfo);
+ this.partyVInfo = DerUtil.getOctetString(partyVInfo);
+ }
+
+ /**
+ * Add optional supplementary public info (DER tagged, implicit, 0).
+ *
+ * @param suppPubInfo supplementary public info.
+ * @return the current builder instance.
+ */
+ public Builder withSuppPubInfo(byte[] suppPubInfo)
+ {
+ this.suppPubInfo = new DERTaggedObject(false, 0, DerUtil.getOctetString(suppPubInfo));
+
+ return this;
+ }
+
+ /**
+ * Add optional supplementary private info (DER tagged, implicit, 1).
+ *
+ * @param suppPrivInfo supplementary private info.
+ * @return the current builder instance.
+ */
+ public Builder withSuppPrivInfo(byte[] suppPrivInfo)
+ {
+ this.suppPrivInfo = new DERTaggedObject(false, 1, DerUtil.getOctetString(suppPrivInfo));
+
+ return this;
+ }
+
+ /**
+ * Build the KTSOtherInfo.
+ *
+ * @return an KTSOtherInfo containing the data.
+ */
+ public DEROtherInfo build()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algorithmID);
+ v.add(partyUVInfo);
+ v.add(partyVInfo);
+
+ if (suppPubInfo != null)
+ {
+ v.add(suppPubInfo);
+ }
+
+ if (suppPrivInfo != null)
+ {
+ v.add(suppPrivInfo);
+ }
+
+ return new DEROtherInfo(new DERSequence(v));
+ }
+ }
+
+ private final DERSequence sequence;
+
+ private DEROtherInfo(DERSequence sequence)
+ {
+ this.sequence = sequence;
+ }
+
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return sequence.getEncoded();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java
new file mode 100644
index 00000000..324c5ae4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.util.Arrays;
+
+class DerUtil
+{
+ static ASN1OctetString getOctetString(byte[] data)
+ {
+ if (data == null)
+ {
+ return new DEROctetString(new byte[0]);
+ }
+
+ return new DEROctetString(Arrays.clone(data));
+ }
+
+ static byte[] toByteArray(ASN1Primitive primitive)
+ {
+ try
+ {
+ return primitive.getEncoded();
+ }
+ catch (final IOException e)
+ {
+ throw new IllegalStateException("Cannot get encoding: " + e.getMessage())
+ {
+ public Throwable getCause()
+ {
+ return e;
+ }
+ };
+ }
+ }
+}