diff --git a/libraries/spongycastle/.gitattributes b/libraries/spongycastle/.gitattributes new file mode 100644 index 000000000..9992aa9fd --- /dev/null +++ b/libraries/spongycastle/.gitattributes @@ -0,0 +1,14 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.gradle text diff=groovy +*.html text diff=html +*.java text diff=java +*.pem text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.data binary diff --git a/libraries/spongycastle/.gitignore b/libraries/spongycastle/.gitignore new file mode 100644 index 000000000..327b42fd5 --- /dev/null +++ b/libraries/spongycastle/.gitignore @@ -0,0 +1,5 @@ +*.gen +build +.gradle +.idea +*.iml diff --git a/libraries/spongycastle/.travis.yml b/libraries/spongycastle/.travis.yml new file mode 100644 index 000000000..1dfaf1800 --- /dev/null +++ b/libraries/spongycastle/.travis.yml @@ -0,0 +1,14 @@ +language: java + +jdk: + - openjdk7 + +install: + - TERM=dumb gradle assemble + +script: + - TERM=dumb gradle check --info + +notifications: + email: + - roberto.tyley@gmail.com diff --git a/libraries/spongycastle/CONTRIBUTORS.html b/libraries/spongycastle/CONTRIBUTORS.html new file mode 100644 index 000000000..a1545b236 --- /dev/null +++ b/libraries/spongycastle/CONTRIBUTORS.html @@ -0,0 +1,346 @@ + +
+The following organisations and people have contributed to the Bouncy Castle Cryptography Package. ++Thanks, may your castles never deflate! +
+Organisations +
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +
+The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. +
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
diff --git a/libraries/spongycastle/ant/bc+-build.xml b/libraries/spongycastle/ant/bc+-build.xml
new file mode 100644
index 000000000..8936f3db3
--- /dev/null
+++ b/libraries/spongycastle/ant/bc+-build.xml
@@ -0,0 +1,993 @@
+
+
+
+ * From Knuth Vol 2, pg 395.
+ */
+ public boolean isProbablePrime(int certainty)
+ {
+ if (certainty <= 0)
+ return true;
+
+ if (sign == 0)
+ return false;
+
+ BigInteger n = this.abs();
+
+ if (!n.testBit(0))
+ return n.equals(TWO);
+
+ if (n.equals(ONE))
+ return false;
+
+ // Try to reduce the penalty for really small numbers
+ int numLists = Math.min(n.bitLength() - 1, primeLists.length);
+
+ for (int i = 0; i < numLists; ++i)
+ {
+ int test = n.remainder(primeProducts[i]);
+
+ int[] primeList = primeLists[i];
+ for (int j = 0; j < primeList.length; ++j)
+ {
+ int prime = primeList[j];
+ int qRem = test % prime;
+ if (qRem == 0)
+ {
+ // We may find small numbers in the list
+ return n.bitLength() < 16 && n.intValue() == prime;
+ }
+ }
+ }
+
+ //
+ // let n = 1 + 2^kq
+ //
+ int s = n.getLowestSetBitMaskFirst(-1 << 1);
+ BigInteger r = n.shiftRight(s);
+
+ Random random = new Random();
+
+ // NOTE: Avoid conversion to/from Montgomery form and check for R/-R as result instead
+
+ BigInteger montRadix = ONE.shiftLeft(32 * n.magnitude.length).remainder(n);
+ BigInteger minusMontRadix = n.subtract(montRadix);
+
+ do
+ {
+ BigInteger a;
+
+ do
+ {
+ a = new BigInteger(n.bitLength(), random);
+ }
+ while (a.sign == 0 || a.compareTo(n) >= 0
+ || a.isEqualMagnitude(montRadix) || a.isEqualMagnitude(minusMontRadix));
+
+ BigInteger y = modPowMonty(a, r, n, false);
+
+ if (!y.equals(montRadix))
+ {
+ int j = 0;
+ while (!y.equals(minusMontRadix))
+ {
+ if (++j == s)
+ {
+ return false;
+ }
+
+ y = modPowMonty(y, TWO, n, false);
+
+ if (y.equals(montRadix))
+ {
+ return false;
+ }
+ }
+ }
+
+ certainty -= 2; // composites pass for only 1/4 possible 'a'
+ }
+ while (certainty > 0);
+
+ return true;
+ }
+
+ public long longValue()
+ {
+ if (sign == 0)
+ {
+ return 0;
+ }
+
+ int n = magnitude.length;
+
+ long val = magnitude[n - 1] & IMASK;
+ if (n > 1)
+ {
+ val |= (magnitude[n - 2] & IMASK) << 32;
+ }
+
+ return sign < 0 ? -val : val;
+ }
+
+ public BigInteger max(BigInteger val)
+ {
+ return (compareTo(val) > 0) ? this : val;
+ }
+
+ public BigInteger min(BigInteger val)
+ {
+ return (compareTo(val) < 0) ? this : val;
+ }
+
+ public BigInteger mod(BigInteger m) throws ArithmeticException
+ {
+ if (m.sign <= 0)
+ {
+ throw new ArithmeticException("BigInteger: modulus is not positive");
+ }
+
+ BigInteger biggie = this.remainder(m);
+
+ return (biggie.sign >= 0 ? biggie : biggie.add(m));
+ }
+
+ public BigInteger modInverse(BigInteger m) throws ArithmeticException
+ {
+ if (m.sign < 1)
+ {
+ throw new ArithmeticException("Modulus must be positive");
+ }
+
+ if (m.quickPow2Check())
+ {
+ return modInversePow2(m);
+ }
+
+ BigInteger d = this.remainder(m);
+ BigInteger x = new BigInteger();
+ BigInteger gcd = BigInteger.extEuclid(d, m, x, null);
+
+ if (!gcd.equals(BigInteger.ONE))
+ {
+ throw new ArithmeticException("Numbers not relatively prime.");
+ }
+
+ if (x.compareTo(BigInteger.ZERO) < 0)
+ {
+ x = x.add(m);
+ }
+
+ return x;
+ }
+
+ private BigInteger modInversePow2(BigInteger m)
+ {
+// assert m.signum() > 0;
+// assert m.bitCount() == 1;
+
+ if (!testBit(0))
+ {
+ throw new ArithmeticException("Numbers not relatively prime.");
+ }
+
+ int pow = m.bitLength() - 1;
+
+ long inv64 = modInverse64(longValue());
+ if (pow < 64)
+ {
+ inv64 &= ((1L << pow) - 1);
+ }
+
+ BigInteger x = BigInteger.valueOf(inv64);
+
+ if (pow > 64)
+ {
+ BigInteger d = this.remainder(m);
+ int bitsCorrect = 64;
+
+ do
+ {
+ BigInteger t = x.multiply(d).remainder(m);
+ x = x.multiply(TWO.subtract(t)).remainder(m);
+ bitsCorrect <<= 1;
+ }
+ while (bitsCorrect < pow);
+
+ if (x.sign < 0)
+ {
+ x = x.add(m);
+ }
+ }
+
+ return x;
+ }
+
+ private static int modInverse32(int d)
+ {
+ // Newton-Raphson division (roughly)
+ int x = d; // d.x == 1 mod 2**3
+ x *= 2 - d * x; // d.x == 1 mod 2**6
+ x *= 2 - d * x; // d.x == 1 mod 2**12
+ x *= 2 - d * x; // d.x == 1 mod 2**24
+ x *= 2 - d * x; // d.x == 1 mod 2**48
+// assert d * x == 1;
+ return x;
+ }
+
+ private static long modInverse64(long d)
+ {
+ // Newton's method with initial estimate "correct to 4 bits"
+ long x = d + (((d + 1L) & 4L) << 1); // d.x == 1 mod 2**4
+ x *= 2 - d * x; // d.x == 1 mod 2**8
+ x *= 2 - d * x; // d.x == 1 mod 2**16
+ x *= 2 - d * x; // d.x == 1 mod 2**32
+ x *= 2 - d * x; // d.x == 1 mod 2**64
+// assert d * x == 1L;
+ return x;
+ }
+
+ /**
+ * Calculate the numbers u1, u2, and u3 such that:
+ *
+ * u1 * a + u2 * b = u3
+ *
+ * where u3 is the greatest common divider of a and b.
+ * a and b using the extended Euclid algorithm (refer p. 323
+ * of The Art of Computer Programming vol 2, 2nd ed).
+ * This also seems to have the side effect of calculating
+ * some form of multiplicative inverse.
+ *
+ * @param a First number to calculate gcd for
+ * @param b Second number to calculate gcd for
+ * @param u1Out the return object for the u1 value
+ * @param u2Out the return object for the u2 value
+ * @return The greatest common divisor of a and b
+ */
+ private static BigInteger extEuclid(BigInteger a, BigInteger b, BigInteger u1Out,
+ BigInteger u2Out)
+ {
+ BigInteger u1 = BigInteger.ONE;
+ BigInteger u3 = a;
+ BigInteger v1 = BigInteger.ZERO;
+ BigInteger v3 = b;
+
+ while (v3.sign > 0)
+ {
+ BigInteger[] q = u3.divideAndRemainder(v3);
+
+ BigInteger tn = u1.subtract(v1.multiply(q[0]));
+ u1 = v1;
+ v1 = tn;
+
+ u3 = v3;
+ v3 = q[1];
+ }
+
+ if (u1Out != null)
+ {
+ u1Out.sign = u1.sign;
+ u1Out.magnitude = u1.magnitude;
+ }
+
+ if (u2Out != null)
+ {
+ BigInteger res = u3.subtract(u1.multiply(a)).divide(b);
+ u2Out.sign = res.sign;
+ u2Out.magnitude = res.magnitude;
+ }
+
+ return u3;
+ }
+
+ /**
+ * zero out the array x
+ */
+ private static void zero(int[] x)
+ {
+ for (int i = 0; i != x.length; i++)
+ {
+ x[i] = 0;
+ }
+ }
+
+ public BigInteger modPow(BigInteger e, BigInteger m)
+ {
+ if (m.sign < 1)
+ {
+ throw new ArithmeticException("Modulus must be positive");
+ }
+
+ if (m.equals(ONE))
+ {
+ return ZERO;
+ }
+
+ if (e.sign == 0)
+ {
+ return ONE;
+ }
+
+ if (sign == 0)
+ {
+ return ZERO;
+ }
+
+ boolean negExp = e.sign < 0;
+ if (negExp)
+ {
+ e = e.negate();
+ }
+
+ BigInteger result = this.mod(m);
+ if (!e.equals(ONE))
+ {
+ if ((m.magnitude[m.magnitude.length - 1] & 1) == 0)
+ {
+ result = modPowBarrett(result, e, m);
+ }
+ else
+ {
+ result = modPowMonty(result, e, m, true);
+ }
+ }
+
+ if (negExp)
+ {
+ result = result.modInverse(m);
+ }
+
+ return result;
+ }
+
+ private static BigInteger modPowBarrett(BigInteger b, BigInteger e, BigInteger m)
+ {
+ int k = m.magnitude.length;
+ BigInteger mr = ONE.shiftLeft((k + 1) << 5);
+ BigInteger yu = ONE.shiftLeft(k << 6).divide(m);
+
+ // Sliding window from MSW to LSW
+ int extraBits = 0, expLength = e.bitLength();
+ while (expLength > EXP_WINDOW_THRESHOLDS[extraBits])
+ {
+ ++extraBits;
+ }
+
+ int numPowers = 1 << extraBits;
+ BigInteger[] oddPowers = new BigInteger[numPowers];
+ oddPowers[0] = b;
+
+ BigInteger b2 = reduceBarrett(b.square(), m, mr, yu);
+
+ for (int i = 1; i < numPowers; ++i)
+ {
+ oddPowers[i] = reduceBarrett(oddPowers[i - 1].multiply(b2), m, mr, yu);
+ }
+
+ int[] windowList = getWindowList(e.magnitude, extraBits);
+// assert windowList.size() > 0;
+
+ int window = windowList[0];
+ int mult = window & 0xFF, lastZeroes = window >>> 8;
+
+ BigInteger y;
+ if (mult == 1)
+ {
+ y = b2;
+ --lastZeroes;
+ }
+ else
+ {
+ y = oddPowers[mult >>> 1];
+ }
+
+ int windowPos = 1;
+ while ((window = windowList[windowPos++]) != -1)
+ {
+ mult = window & 0xFF;
+
+ int bits = lastZeroes + bitLengths[mult];
+ for (int j = 0; j < bits; ++j)
+ {
+ y = reduceBarrett(y.square(), m, mr, yu);
+ }
+
+ y = reduceBarrett(y.multiply(oddPowers[mult >>> 1]), m, mr, yu);
+
+ lastZeroes = window >>> 8;
+ }
+
+ for (int i = 0; i < lastZeroes; ++i)
+ {
+ y = reduceBarrett(y.square(), m, mr, yu);
+ }
+
+ return y;
+ }
+
+ private static BigInteger reduceBarrett(BigInteger x, BigInteger m, BigInteger mr, BigInteger yu)
+ {
+ int xLen = x.bitLength(), mLen = m.bitLength();
+ if (xLen < mLen)
+ {
+ return x;
+ }
+
+ if (xLen - mLen > 1)
+ {
+ int k = m.magnitude.length;
+
+ BigInteger q1 = x.divideWords(k - 1);
+ BigInteger q2 = q1.multiply(yu); // TODO Only need partial multiplication here
+ BigInteger q3 = q2.divideWords(k + 1);
+
+ BigInteger r1 = x.remainderWords(k + 1);
+ BigInteger r2 = q3.multiply(m); // TODO Only need partial multiplication here
+ BigInteger r3 = r2.remainderWords(k + 1);
+
+ x = r1.subtract(r3);
+ if (x.sign < 0)
+ {
+ x = x.add(mr);
+ }
+ }
+
+ while (x.compareTo(m) >= 0)
+ {
+ x = x.subtract(m);
+ }
+
+ return x;
+ }
+
+ private static BigInteger modPowMonty(BigInteger b, BigInteger e, BigInteger m, boolean convert)
+ {
+ int n = m.magnitude.length;
+ int powR = 32 * n;
+ boolean smallMontyModulus = m.bitLength() + 2 <= powR;
+ int mDash = m.getMQuote();
+
+ // tmp = this * R mod m
+ if (convert)
+ {
+ b = b.shiftLeft(powR).remainder(m);
+ }
+
+ int[] yAccum = new int[n + 1];
+
+ int[] zVal = b.magnitude;
+// assert zVal.length <= n;
+ if (zVal.length < n)
+ {
+ int[] tmp = new int[n];
+ System.arraycopy(zVal, 0, tmp, n - zVal.length, zVal.length);
+ zVal = tmp;
+ }
+
+ // Sliding window from MSW to LSW
+
+ int extraBits = 0;
+
+ // Filter the common case of small RSA exponents with few bits set
+ if (e.magnitude.length > 1 || e.bitCount() > 2)
+ {
+ int expLength = e.bitLength();
+ while (expLength > EXP_WINDOW_THRESHOLDS[extraBits])
+ {
+ ++extraBits;
+ }
+ }
+
+ int numPowers = 1 << extraBits;
+ int[][] oddPowers = new int[numPowers][];
+ oddPowers[0] = zVal;
+
+ int[] zSquared = Arrays.clone(zVal);
+ squareMonty(yAccum, zSquared, m.magnitude, mDash, smallMontyModulus);
+
+ for (int i = 1; i < numPowers; ++i)
+ {
+ oddPowers[i] = Arrays.clone(oddPowers[i - 1]);
+ multiplyMonty(yAccum, oddPowers[i], zSquared, m.magnitude, mDash, smallMontyModulus);
+ }
+
+ int[] windowList = getWindowList(e.magnitude, extraBits);
+// assert windowList.size() > 0;
+
+ int window = windowList[0];
+ int mult = window & 0xFF, lastZeroes = window >>> 8;
+
+ int[] yVal;
+ if (mult == 1)
+ {
+ yVal = zSquared;
+ --lastZeroes;
+ }
+ else
+ {
+ yVal = Arrays.clone(oddPowers[mult >>> 1]);
+ }
+
+ int windowPos = 1;
+ while ((window = windowList[windowPos++]) != -1)
+ {
+ mult = window & 0xFF;
+
+ int bits = lastZeroes + bitLengths[mult];
+ for (int j = 0; j < bits; ++j)
+ {
+ squareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
+ }
+
+ multiplyMonty(yAccum, yVal, oddPowers[mult >>> 1], m.magnitude, mDash, smallMontyModulus);
+
+ lastZeroes = window >>> 8;
+ }
+
+ for (int i = 0; i < lastZeroes; ++i)
+ {
+ squareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
+ }
+
+ if (convert)
+ {
+ // Return y * R^(-1) mod m
+ reduceMonty(yVal, m.magnitude, mDash);
+ }
+ else if (smallMontyModulus && compareTo(0, yVal, 0, m.magnitude) >= 0)
+ {
+ subtract(0, yVal, 0, m.magnitude);
+ }
+
+ return new BigInteger(1, yVal);
+ }
+
+ private static int[] getWindowList(int[] mag, int extraBits)
+ {
+ int v = mag[0];
+// assert v != 0;
+ int leadingBits = bitLen(v);
+
+ int resultSize = (((mag.length - 1) << 5) + leadingBits) / (1 + extraBits) + 2;
+ int[] result = new int[resultSize];
+ int resultPos = 0;
+
+ int bitPos = 33 - leadingBits;
+ v <<= bitPos;
+
+ int mult = 1, multLimit = 1 << extraBits;
+ int zeroes = 0;
+
+ int i = 0;
+ for (; ; )
+ {
+ for (; bitPos < 32; ++bitPos)
+ {
+ if (mult < multLimit)
+ {
+ mult = (mult << 1) | (v >>> 31);
+ }
+ else if (v < 0)
+ {
+ result[resultPos++] = createWindowEntry(mult, zeroes);
+ mult = 1;
+ zeroes = 0;
+ }
+ else
+ {
+ ++zeroes;
+ }
+
+ v <<= 1;
+ }
+
+ if (++i == mag.length)
+ {
+ result[resultPos++] = createWindowEntry(mult, zeroes);
+ break;
+ }
+
+ v = mag[i];
+ bitPos = 0;
+ }
+
+ result[resultPos] = -1;
+ return result;
+ }
+
+ private static int createWindowEntry(int mult, int zeroes)
+ {
+ while ((mult & 1) == 0)
+ {
+ mult >>>= 1;
+ ++zeroes;
+ }
+
+ return mult | (zeroes << 8);
+ }
+
+ /**
+ * return w with w = x * x - w is assumed to have enough space.
+ */
+ private static int[] square(int[] w, int[] x)
+ {
+ // Note: this method allows w to be only (2 * x.Length - 1) words if result will fit
+// if (w.length != 2 * x.length)
+// {
+// throw new IllegalArgumentException("no I don't think so...");
+// }
+
+ long c;
+
+ int wBase = w.length - 1;
+
+ for (int i = x.length - 1; i != 0; --i)
+ {
+ long v = x[i] & IMASK;
+
+ c = v * v + (w[wBase] & IMASK);
+ w[wBase] = (int)c;
+ c >>>= 32;
+
+ for (int j = i - 1; j >= 0; --j)
+ {
+ long prod = v * (x[j] & IMASK);
+
+ c += (w[--wBase] & IMASK) + ((prod << 1) & IMASK);
+ w[wBase] = (int)c;
+ c = (c >>> 32) + (prod >>> 31);
+ }
+
+ c += w[--wBase] & IMASK;
+ w[wBase] = (int)c;
+
+ if (--wBase >= 0)
+ {
+ w[wBase] = (int)(c >> 32);
+ }
+ wBase += i;
+ }
+
+ c = x[0] & IMASK;
+
+ c = c * c + (w[wBase] & IMASK);
+ w[wBase] = (int)c;
+
+ if (--wBase >= 0)
+ {
+ w[wBase] += (int)(c >> 32);
+ }
+
+ return w;
+ }
+
+ /**
+ * return x with x = y * z - x is assumed to have enough space.
+ */
+ private static int[] multiply(int[] x, int[] y, int[] z)
+ {
+ int i = z.length;
+
+ if (i < 1)
+ {
+ return x;
+ }
+
+ int xBase = x.length - y.length;
+
+ for (;;)
+ {
+ long a = z[--i] & IMASK;
+ long val = 0;
+
+ for (int j = y.length - 1; j >= 0; j--)
+ {
+ val += a * (y[j] & IMASK) + (x[xBase + j] & IMASK);
+
+ x[xBase + j] = (int)val;
+
+ val >>>= 32;
+ }
+
+ --xBase;
+
+ if (i < 1)
+ {
+ if (xBase >= 0)
+ {
+ x[xBase] = (int)val;
+ }
+ break;
+ }
+
+ x[xBase] = (int)val;
+ }
+
+ return x;
+ }
+
+ /**
+ * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size)
+ */
+ private int getMQuote()
+ {
+ if (mQuote != 0)
+ {
+ return mQuote; // already calculated
+ }
+
+// assert this.sign > 0;
+
+ int d = -magnitude[magnitude.length - 1];
+
+// assert (d & 1) != 0;
+
+ return mQuote = modInverse32(d);
+ }
+
+ private static void reduceMonty(int[] x, int[] m, int mDash) // mDash = -m^(-1) mod b
+ {
+ // NOTE: Not a general purpose reduction (which would allow x up to twice the bitlength of m)
+// assert x.length == m.length;
+
+ int n = m.length;
+
+ for (int i = n - 1; i >= 0; --i)
+ {
+ int x0 = x[n - 1];
+
+ long t = (x0 * mDash) & IMASK;
+
+ long carry = t * (m[n - 1] & IMASK) + (x0 & IMASK);
+// assert (int)carry == 0;
+ carry >>>= 32;
+
+ for (int j = n - 2; j >= 0; --j)
+ {
+ carry += t * (m[j] & IMASK) + (x[j] & IMASK);
+ x[j + 1] = (int)carry;
+ carry >>>= 32;
+ }
+
+ x[0] = (int)carry;
+// assert carry >>> 32 == 0;
+ }
+
+ if (compareTo(0, x, 0, m) >= 0)
+ {
+ subtract(0, x, 0, m);
+ }
+ }
+
+ /**
+ * Montgomery multiplication: a = x * y * R^(-1) mod m
+ *
+ * Based algorithm 14.36 of Handbook of Applied Cryptography.
+ *
+ *
+ * Do not use this class without calling setSeed at least once! There + * are some example seed generators in the org.spongycastle.prng package. + */ +public class SecureRandom extends java.util.Random +{ + // Note: all objects of this class should be deriving their random data from + // a single generator appropriate to the digest being used. + private static final RandomGenerator sha1Generator = new DigestRandomGenerator(new SHA1Digest()); + private static final RandomGenerator sha256Generator = new DigestRandomGenerator(new SHA256Digest()); + + protected RandomGenerator generator; + + // public constructors + public SecureRandom() + { + this(sha1Generator); + setSeed(System.currentTimeMillis()); + } + + public SecureRandom(byte[] inSeed) + { + this(sha1Generator); + setSeed(inSeed); + } + + protected SecureRandom( + RandomGenerator generator) + { + super(0); + this.generator = generator; + } + + // protected constructors + // protected SecureRandom(SecureRandomSpi srs, Provider provider); + + // public class methods + public static SecureRandom getInstance(String algorithm) + { + if (algorithm.equals("SHA1PRNG")) + { + return new SecureRandom(sha1Generator); + } + if (algorithm.equals("SHA256PRNG")) + { + return new SecureRandom(sha256Generator); + } + return new SecureRandom(); // follow old behaviour + } + + public static SecureRandom getInstance(String algorithm, String provider) + { + return getInstance(algorithm); + } + + public static byte[] getSeed(int numBytes) + { + byte[] rv = new byte[numBytes]; + + sha1Generator.addSeedMaterial(System.currentTimeMillis()); + sha1Generator.nextBytes(rv); + + return rv; + } + + // public instance methods + public byte[] generateSeed(int numBytes) + { + byte[] rv = new byte[numBytes]; + + nextBytes(rv); + + return rv; + } + + // public final Provider getProvider(); + public void setSeed(byte[] inSeed) + { + generator.addSeedMaterial(inSeed); + } + + // public methods overriding random + public void nextBytes(byte[] bytes) + { + generator.nextBytes(bytes); + } + + public void setSeed(long rSeed) + { + if (rSeed != 0) // to avoid problems with Random calling setSeed in construction + { + generator.addSeedMaterial(rSeed); + } + } + + public int nextInt() + { + byte[] intBytes = new byte[4]; + + nextBytes(intBytes); + + int result = 0; + + for (int i = 0; i < 4; i++) + { + result = (result << 8) + (intBytes[i] & 0xff); + } + + return result; + } + + protected final int next(int numBits) + { + int size = (numBits + 7) / 8; + byte[] bytes = new byte[size]; + + nextBytes(bytes); + + int result = 0; + + for (int i = 0; i < size; i++) + { + result = (result << 8) + (bytes[i] & 0xff); + } + + return result & ((1 << numBits) - 1); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/AbstractCollection.java b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractCollection.java new file mode 100644 index 000000000..44c85ee05 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractCollection.java @@ -0,0 +1,261 @@ +package java.util; + +public abstract class AbstractCollection + implements Collection +{ + protected AbstractCollection() + { + } + + public abstract Iterator iterator(); + + public abstract int size(); + + public boolean isEmpty() + { + return size() == 0; + } + + public boolean contains(Object o) + { + Iterator it = iterator(); + while (it.hasNext()) + { + Object e = it.next(); + if (o == null) + { + if (e == null) + { + return true; + } + } + else + { + if (o.equals(e)) + { + return true; + } + } + } + return false; + } + + public Object[] toArray() + { + Object[] arObjects = new Object[size()]; + Iterator it = iterator(); + int i = 0; + while (it.hasNext()) + { + arObjects[i++] = it.next(); + } + return arObjects; + } + + public Object[] toArray(Object[] a) + throws NullPointerException, ArrayStoreException + //TODO: Check if this is realy compatible to SUN!!! + { + if (a == null) + { + throw new NullPointerException(); + } + + if (isEmpty()) + { + return a; + } + Object[] arObjects = null; + int size = size(); + if (a.length < size) + { + Iterator it = iterator(); + Object o = it.next(); + if (o == null) //no object or object is null + { + throw new ArrayStoreException(); //correct ? + } + throw new ArrayStoreException("please pass array of correct size"); + } + else + { + arObjects = a; + if (a.length > size) + { + arObjects[size] = null; + } + + } + + Iterator it = iterator(); + int i = 0; + while (it.hasNext()) + { + Object o = it.next(); + arObjects[i++] = o; + } + return arObjects; + } + + public boolean add(Object o) + throws RuntimeException, NullPointerException, ClassCastException, IllegalArgumentException + { + throw new RuntimeException(); + } + + public boolean remove(Object o) + throws RuntimeException + { + Iterator it = iterator(); + while (it.hasNext()) + { + Object e = it.next(); + if (o == null) + { + if (e == null) + { + try + { + it.remove(); + } + catch (RuntimeException ue) + { + throw ue; + } + return true; + } + } + else + { + if (o.equals(e)) + { + try + { + it.remove(); + } + catch (RuntimeException ue) + { + throw ue; + } + return true; + } + } + } + return false; + } + + public boolean containsAll(Collection c) + { + Iterator it = c.iterator(); + while (it.hasNext()) + { + if (!contains(it.next())) + { + return false; + } + } + return true; + } + + public boolean addAll(Collection c) + throws RuntimeException + { + Iterator it = c.iterator(); + boolean ret = false; + while (it.hasNext()) + { + try + { + ret |= add(it.next()); + } + catch (RuntimeException ue) + { + throw ue; + } + } + return ret; + } + + public boolean removeAll(Collection c) + throws RuntimeException + { + Iterator it = iterator(); + boolean ret = false; + while (it.hasNext()) + { + if (c.contains(it.next())) + { + try + { + it.remove(); + ret = true; + } + catch (RuntimeException ue) + { + throw ue; + } + } + } + return ret; + } + + public boolean retainAll(Collection c) + throws RuntimeException + { + Iterator it = iterator(); + boolean ret = false; + while (it.hasNext()) + { + if (!c.contains(it.next())) + { + try + { + it.remove(); + ret = true; + } + catch (RuntimeException ue) + { + throw ue; + } + } + } + return ret; + } + + public void clear() + throws RuntimeException + { + Iterator it = iterator(); + while (it.hasNext()) + { + try + { + it.next(); + it.remove(); + } + catch (RuntimeException ue) + { + throw ue; + } + } + } + + public String toString() + { + StringBuffer ret = new StringBuffer("["); + Iterator it = iterator(); + if (it.hasNext()) + { + ret.append(String.valueOf(it.next())); + } + while (it.hasNext()) + { + ret.append(", "); + ret.append(String.valueOf(it.next())); + + } + ret.append("]"); + return ret.toString(); + } + +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/AbstractList.java b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractList.java new file mode 100644 index 000000000..dbb1dffc4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractList.java @@ -0,0 +1,304 @@ +package java.util; + +public abstract class AbstractList + extends AbstractCollection + implements List +{ + protected AbstractList al = this; + + + protected AbstractList() + { + } + + public boolean add(Object o) + throws RuntimeException, ClassCastException, IllegalArgumentException + { + try + { + add(size(), o); + return true; + } + catch (RuntimeException ue) + { + throw ue; + } + } + + public abstract Object get(int index) + throws IndexOutOfBoundsException; + + public Object set(int index, Object element) + throws RuntimeException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException + { + throw new RuntimeException(); + } + + public void add(int index, Object element) + throws RuntimeException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException + { + throw new RuntimeException(); + } + + public Object remove(int index) + throws RuntimeException, IndexOutOfBoundsException + { + Object o = get(index); + + removeRange(index, index + 1); + return o; + } + + public int indexOf(Object o) + { + ListIterator li = listIterator(); + Object e; + while (li.hasNext()) + { + int index = li.nextIndex(); + e = li.next(); + + if (o == null) + { + if (e == null) + { + return index; + } + } + else + { + if (o.equals(e)) + { + return index; + } + } + } + return -1; + } + + public int lastIndexOf(Object o) + { + ListIterator li = listIterator(size()); + while (li.hasPrevious()) + { + int index = li.previousIndex(); + Object e = li.previous(); + if (o == null) + { + if (e == null) + { + return index; + } + } + else + { + if (o.equals(e)) + { + return index; + } + } + } + return -1; + } + + public void clear() + throws RuntimeException + { + try + { + removeRange(0, size()); + } + catch (RuntimeException ue) + { + throw ue; + } + } + + public boolean addAll(int index, Collection c) + throws RuntimeException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException + { + Iterator it = c.iterator(); + boolean ret = false; + while (it.hasNext()) + { + try + { + add(index++, it.next()); + ret = true; + } + catch (RuntimeException ue) + { + throw ue; + } + } + return ret; + } + + public Iterator iterator() + { + return new AbstractListIterator(this, 0); + } + + public ListIterator listIterator() + { + return listIterator(0); + } + + public ListIterator listIterator(int index) + throws IndexOutOfBoundsException + { + if (index < 0 || index > size()) + { + throw new IndexOutOfBoundsException(); + } + return new AbstractListListIterator(this, index); + } + + public List subList(int fromIndex, int toIndex) + throws IndexOutOfBoundsException, IllegalArgumentException + { + if (fromIndex < 0 || toIndex > size()) + { + throw new IndexOutOfBoundsException(); + } + if (fromIndex > toIndex) + { + throw new IllegalArgumentException(); + } + return (List)new Sublist(this, fromIndex, toIndex); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + if (!(o instanceof List)) + { + return false; + } + Iterator it1 = iterator(); + Iterator it2 = ((List)o).iterator(); + while (it1.hasNext()) + { + if (!it2.hasNext()) + { + return false; + } + Object e1 = it1.next(); + Object e2 = it2.next(); + if (e1 == null) + { + if (e2 != null) + { + return false; + } + } + if (!e1.equals(e2)) + { + return false; + } + } + return true; + } + + public int hashCode() + { + int hashCode = 1; + Iterator it = iterator(); + while (it.hasNext()) + { + Object o = it.next(); + hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); + } + return hashCode; + } + + protected void removeRange(int fromIndex, int toIndex) + { + if (fromIndex == toIndex) + { + return; + } + + ListIterator li = listIterator(fromIndex); + + int i = fromIndex; + do + { + li.next(); + li.remove(); + i++; + } + while (li.hasNext() && i < toIndex); + } + + private class AbstractListIterator + implements Iterator + { + AbstractList m_al = null; + int m_nextIndex = 0; + + public AbstractListIterator(AbstractList al, int index) + { + m_al = al; + m_nextIndex = index; + } + + public boolean hasNext() + { + return m_nextIndex < m_al.size(); + } + + public Object next() + { + return m_al.get(m_nextIndex++); + } + + public void remove() + { + m_al.remove(m_nextIndex - 1); + } + } + + private class AbstractListListIterator + extends AbstractListIterator + implements ListIterator + { + public AbstractListListIterator(AbstractList al, int index) + { + super(al, index); + } + + public boolean hasPrevious() + { + return m_nextIndex > 0; + } + + public Object previous()// throws NoSuchElementException; + { + return m_al.get(--m_nextIndex); + } + + public int nextIndex() + { + return m_nextIndex; + } + + public int previousIndex() + { + return m_nextIndex - 1; + } + + public void set(Object o) //throws RuntimeException, ClassCastException, IllegalArgumentException,IllegalStateException; + { + m_al.set(m_nextIndex - 1, o); + } + + public void add(Object o)// throws RuntimeException, ClassCastException, IllegalArgumentException; + { + m_al.add(m_nextIndex - 1, o); + } + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/AbstractMap.java b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractMap.java new file mode 100644 index 000000000..13c61ecfa --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractMap.java @@ -0,0 +1,173 @@ +package java.util; + +public abstract class AbstractMap + implements Map +{ + + protected AbstractMap() + { + } + + public int size() + { + return entrySet().size(); + } + + public boolean isEmpty() + { + return size() == 0; + } + + public boolean containsValue(Object value) + { + Iterator it = entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry v = (Map.Entry)it.next(); + if (value == null) + { + if (v.getValue() == null) + { + return true; + } + } + else + { + if (value.equals(v.getValue())) + { + return true; + } + } + } + return false; + } + + public boolean containsKey(Object key) + throws ClassCastException, NullPointerException + { + Iterator it = entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry v = (Map.Entry)it.next(); + if (key == null) + { + if (v.getKey() == null) + { + return true; + } + } + else + { + if (key.equals(v.getKey())) + { + return true; + } + } + } + return false; + } + + public Object get(Object key) + throws ClassCastException, NullPointerException + { + Iterator it = entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry v = (Map.Entry)it.next(); + if (key == null) + { + if (v.getKey() == null) + { + return v.getValue(); + } + } + else + { + if (key.equals(v.getKey())) + { + return v.getValue(); + } + } + } + return null; + } + + public Object put(Object key, Object value) + throws RuntimeException + { + throw new RuntimeException(); + } + + public Object remove(Object key) + { + Iterator it = entrySet().iterator(); + Object o = null; + while (it.hasNext()) + { + Map.Entry v = (Map.Entry)it.next(); + if (key == null) + { + if (v.getKey() == null) + { + o = v.getValue(); + it.remove(); + return o; + } + } + else + { + if (key.equals(v.getKey())) + { + o = v.getValue(); + it.remove(); + return o; + } + } + } + return null; + } + + public void putAll(Map t) + { + Iterator it = t.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry v = (Map.Entry)it.next(); + put(v.getKey(), v.getValue()); + } + } + + public void clear() + { + entrySet().clear(); + } + + public Set keySet() + { + throw new RuntimeException("no keySet in AbstractMap()"); + } + + public Collection values() + { + throw new RuntimeException("no values in AbstractMap()"); + } + + public abstract Set entrySet(); + + public boolean equals(Object o) + { + throw new RuntimeException("no equals in AbstractMap()"); + } + + public int hashCode() + { + throw new RuntimeException("no hashCode in AbstractMap()"); + } + + public String toString() + { + throw new RuntimeException("no toString in AbstractMap()"); + } + + +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/AbstractSet.java b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractSet.java new file mode 100644 index 000000000..2a91c4766 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/AbstractSet.java @@ -0,0 +1,46 @@ +package java.util; + +public abstract class AbstractSet + extends AbstractCollection + implements Set +{ + protected AbstractSet() + { + } + + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null) + { + return false; + } + if (!(o instanceof Set)) + { + return false; + } + if (((Set)o).size() != size()) + { + return false; + } + return containsAll((Collection)o); + } + + public int hashCode() + { + int hashCode = 0; + Iterator it = iterator(); + while (it.hasNext()) + { + Object o = it.next(); + if (o != null) + { + hashCode += o.hashCode(); + } + } + return hashCode; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/ArrayList.java b/libraries/spongycastle/core/src/main/j2me/java/util/ArrayList.java new file mode 100644 index 000000000..80adf4785 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/ArrayList.java @@ -0,0 +1,107 @@ +package java.util; + +public class ArrayList extends AbstractList + implements List + { + Vector m_Vector=null; + + public ArrayList() + { + m_Vector=new Vector(); + } + + public ArrayList(Collection c) + { + m_Vector=new Vector((int)(c.size()*1.1)); + addAll(c); + } + + public ArrayList(int initialCapacity) + { + m_Vector=new Vector(initialCapacity); + } + + public void trimToSize() + { + m_Vector.trimToSize(); + } + + public void ensureCapacity(int minCapacity) + { + m_Vector.ensureCapacity(minCapacity); + } + + public int size() + { + return m_Vector.size(); + } + + public boolean contains(Object elem) + { + return m_Vector.contains(elem); + } + + public int indexOf(Object elem) + { + return m_Vector.indexOf(elem); + } + + public int lastIndexOf(Object elem) + { + return m_Vector.lastIndexOf(elem); + } + + public Object clone() + { + ArrayList al=new ArrayList(this); + + return al; + } + + public Object[] toArray() + { + Object[] o=new Object[m_Vector.size()]; + m_Vector.copyInto(o); + return o; + } + + public Object get(int index) + { + return m_Vector.elementAt(index); + } + + public Object set(int index,Object elem) + { + Object o=m_Vector.elementAt(index); + m_Vector.setElementAt(elem,index); + return o; + } + + public boolean add(Object o) + { + m_Vector.addElement(o); + return true; + } + + public void add(int index,Object elem) + { + m_Vector.insertElementAt(elem,index); + } + + public Object remove(int index) + { + Object o=m_Vector.elementAt(index); + m_Vector.removeElementAt(index); + return o; + } + + public void clear() + { + m_Vector.removeAllElements(); + } + + + + + + } diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Arrays.java b/libraries/spongycastle/core/src/main/j2me/java/util/Arrays.java new file mode 100644 index 000000000..8cd74daae --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Arrays.java @@ -0,0 +1,118 @@ +package java.util; + +public class Arrays +{ + + private Arrays() + { + } + + public static void fill(byte[] ret, byte v) + { + for (int i = 0; i != ret.length; i++) + { + ret[i] = v; + } + } + + public static boolean equals(byte[] a, byte[] a2) + { + if (a == a2) + { + return true; + } + if (a == null || a2 == null) + { + return false; + } + + int length = a.length; + if (a2.length != length) + { + return false; + } + + for (int i = 0; i < length; i++) + { + if (a[i] != a2[i]) + { + return false; + } + } + + return true; + } + + public static List asList(Object[] a) + { + return new ArrayList(a); + } + + private static class ArrayList + extends AbstractList + { + private Object[] a; + + ArrayList(Object[] array) + { + a = array; + } + + public int size() + { + return a.length; + } + + public Object[] toArray() + { + Object[] tmp = new Object[a.length]; + + System.arraycopy(a, 0, tmp, 0, tmp.length); + + return tmp; + } + + public Object get(int index) + { + return a[index]; + } + + public Object set(int index, Object element) + { + Object oldValue = a[index]; + a[index] = element; + return oldValue; + } + + public int indexOf(Object o) + { + if (o == null) + { + for (int i = 0; i < a.length; i++) + { + if (a[i] == null) + { + return i; + } + } + } + else + { + for (int i = 0; i < a.length; i++) + { + if (o.equals(a[i])) + { + return i; + } + } + } + return -1; + } + + public boolean contains(Object o) + { + return indexOf(o) != -1; + } + } + +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Collection.java b/libraries/spongycastle/core/src/main/j2me/java/util/Collection.java new file mode 100644 index 000000000..a911dcd63 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Collection.java @@ -0,0 +1,21 @@ + +package java.util; + +public interface Collection + { + public boolean add(Object o) throws RuntimeException,ClassCastException,IllegalArgumentException; + public boolean addAll(Collection c) throws RuntimeException,ClassCastException,IllegalArgumentException; + public void clear() throws RuntimeException; + public boolean contains(Object o); + public boolean containsAll(Collection c); + public boolean equals(Object o); + public int hashCode(); + public boolean isEmpty(); + public Iterator iterator(); + public /*SK13*/boolean remove(Object o) throws RuntimeException; + public boolean removeAll(Collection c) throws RuntimeException; + public boolean retainAll(Collection c) throws RuntimeException; + public int size(); + public Object[] toArray(); + public Object[] toArray(Object[] a) throws ArrayStoreException; + } diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Collections.java b/libraries/spongycastle/core/src/main/j2me/java/util/Collections.java new file mode 100644 index 000000000..d611e5e3e --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Collections.java @@ -0,0 +1,365 @@ +package java.util; + +public class Collections +{ + public static List EMPTY_LIST = new ArrayList(); + + private Collections() + { + } + + public static Collection unmodifiableCollection(Collection c) + { + return new UnmodifiableCollection(c); + } + + static class UnmodifiableCollection + implements Collection + { + Collection c; + + UnmodifiableCollection(Collection c) + { + this.c = c; + } + + public int size() + { + return c.size(); + } + + public boolean isEmpty() + { + return c.isEmpty(); + } + + public boolean contains(Object o) + { + return c.contains(o); + } + + public Object[] toArray() + { + return c.toArray(); + } + + public Object[] toArray(Object[] a) + { + return c.toArray(a); + } + + public Iterator iterator() + { + return new Iterator() + { + Iterator i = c.iterator(); + + public boolean hasNext() + { + return i.hasNext(); + } + + public Object next() + { + return i.next(); + } + + public void remove() + { + throw new RuntimeException(); + } + }; + } + + public boolean add(Object o) + { + throw new RuntimeException(); + } + + public boolean remove(Object o) + { + throw new RuntimeException(); + } + + public boolean containsAll(Collection coll) + { + return c.containsAll(coll); + } + + public boolean addAll(Collection coll) + { + throw new RuntimeException(); + } + + public boolean removeAll(Collection coll) + { + throw new RuntimeException(); + } + + public boolean retainAll(Collection coll) + { + throw new RuntimeException(); + } + + public void clear() + { + throw new RuntimeException(); + } + + public String toString() + { + return c.toString(); + } + } + + public static Set unmodifiableSet(Set s) + { + return new UnmodifiableSet(s); + } + + static class UnmodifiableSet + extends UnmodifiableCollection + implements Set + { + UnmodifiableSet(Set s) + { + super(s); + } + + public boolean equals(Object o) + { + return c.equals(o); + } + + public int hashCode() + { + return c.hashCode(); + } + } + + public static Map unmodifiableMap(Map map) + { + return new UnmodifiableMap(map); + } + + static class UnmodifiableMap + implements Map + { + private Map map; + + UnmodifiableMap(Map map) + { + this.map = map; + } + + public int size() + { + return map.size(); + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public boolean containsKey(Object key) + throws ClassCastException, NullPointerException + { + return map.containsKey(key); + } + + public boolean containsValue(Object value) + { + return map.containsValue(value); + } + + public Object get(Object key) + throws ClassCastException, NullPointerException + { + return map.get(key); + } + + public Object put(Object key, Object value) + throws RuntimeException, ClassCastException, IllegalArgumentException, NullPointerException + { + throw new RuntimeException("unsupported operation - map unmodifiable"); + } + + public Object remove(Object key) + throws RuntimeException + { + throw new RuntimeException("unsupported operation - map unmodifiable"); + } + + public void putAll(Map t) + throws RuntimeException, ClassCastException, IllegalArgumentException, NullPointerException + { + throw new RuntimeException("unsupported operation - map unmodifiable"); + } + + public void clear() + throws RuntimeException + { + throw new RuntimeException("unsupported operation - map unmodifiable"); + } + + public Set keySet() + { + return map.keySet(); + } + + public Collection values() + { + return map.values(); + } + + public Set entrySet() + { + return map.entrySet(); + } + } + + public static List unmodifiableList(List list) + { + return new UnmodifiableList(list); + } + + static class UnmodifiableList + extends UnmodifiableCollection + implements List + { + private List list; + + UnmodifiableList(List list) + { + super(list); + this.list = list; + } + + public boolean equals(Object o) + { + return list.equals(o); + } + + public int hashCode() + { + return list.hashCode(); + } + + public Object get(int index) + { + return list.get(index); + } + + public Object set(int index, Object element) + { + throw new RuntimeException(); + } + + public void add(int index, Object element) + { + throw new RuntimeException(); + } + + public Object remove(int index) + { + throw new RuntimeException(); + } + + public int indexOf(Object o) + { + return list.indexOf(o); + } + + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + public boolean addAll(int index, Collection c) + { + throw new RuntimeException(); + } + + public ListIterator listIterator() + { + return listIterator(0); + } + + public ListIterator listIterator(final int index) + { + return new ListIterator() + { + ListIterator i = list.listIterator(index); + + public boolean hasNext() + { + return i.hasNext(); + } + + public Object next() + { + return i.next(); + } + + public boolean hasPrevious() + { + return i.hasPrevious(); + } + + public Object previous() + { + return i.previous(); + } + + public int nextIndex() + { + return i.nextIndex(); + } + + public int previousIndex() + { + return i.previousIndex(); + } + + public void remove() + { + throw new RuntimeException(); + } + + public void set(Object o) + { + throw new RuntimeException(); + } + + public void add(Object o) + { + throw new RuntimeException(); + } + }; + } + + public List subList(int fromIndex, int toIndex) + { + return new UnmodifiableList(list.subList(fromIndex, toIndex)); + } + } + + public static Enumeration enumeration(final Collection c) + { + return new Enumeration() + { + Iterator i = c.iterator(); + + public boolean hasMoreElements() + { + return i.hasNext(); + } + + public Object nextElement() + { + return i.next(); + } + }; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/HashMap.java b/libraries/spongycastle/core/src/main/j2me/java/util/HashMap.java new file mode 100644 index 000000000..0bcd75ddd --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/HashMap.java @@ -0,0 +1,279 @@ +package java.util; + + +public class HashMap extends AbstractMap{ + + ////////////////////////////////////////////////////////////// + ///// innere Klasse Null //////////////////////////////////// + ////////////////////////////////////////////////////////////// +public class Null extends Object + { + public Null() + { + + } + + public String toString() + { + return "Nullobject"; + } + } + + + ////////////////////////////////////////////////////////////// + ///// innere Klasse innerSet //////////////////////////////////// + ////////////////////////////////////////////////////////////// + + class ISet extends AbstractSet implements java.util.Set + { + + Vector vec = null; + + public ISet() + { + + vec = new Vector(); + + } + + public boolean add(Object o) + { + vec.addElement(o); + return true; + } + + public int size() + { + return vec.size(); + } + + public Iterator iterator() + { + return new IIterator(vec); + } + } + + ////////////////////////////////////////////////////////////// + ///// innere Klasse Iterator //////////////////////////////////// + ////////////////////////////////////////////////////////////// + class IIterator implements java.util.Iterator + { + int index = 0; + Vector vec = null; + public IIterator(Vector ve) + { + vec = ve; + } + + public boolean hasNext() + { + if (vec.size() > index) return true; + return false; + } + + public Object next() + { + Object o = vec.elementAt(index); + if (o==Nullobject) o=null; + index++; + return o; + + } + + public void remove() + { + index--; + vec.removeElementAt(index); + } + + } + + ////////////////////////////////////////////////////////////// + ///// innere Klasse Entry //////////////////////////////////// + ////////////////////////////////////////////////////////////// + + + class Entry implements Map.Entry + { + public Object key=null; + public Object value=null; + + public Entry(Object ke,Object valu) + { + key = ke; + value = valu; + } + public boolean equals(Object o) + { + if (value == ((Entry)o).value && key == ((Entry)o).key ) return true; + else return false; + + } + + public Object getValue() + { + return value; + } + + public Object getKey() + { + return (Object)key; + } + + public int hashCode() + { + return value.hashCode() + key.hashCode(); + + } + + public Object setValue(Object valu) + { + value = (String)valu; + return this; + } + } + + //////////////////////////////////////////////////////////////////// + + private Hashtable m_HashTable=null; + private Null Nullobject = null; + + public HashMap() + { + Nullobject = new Null(); + m_HashTable=new Hashtable(); + } + + public HashMap(int initialCapacity) + { + Nullobject = new Null(); + m_HashTable=new Hashtable(initialCapacity); + } + + public HashMap(Map t) + { + Nullobject = new Null(); + m_HashTable=new Hashtable(); + this.putAll(t); + } + + public void clear() + { + m_HashTable.clear(); + } + + public Object clone() + { + HashMap hm=new HashMap(this); + + return hm; + } + + public boolean containsKey(Object key) + { + if (key == null) key = Nullobject; + boolean b = m_HashTable.containsKey(key); + return b; + + } + + public boolean containsValue(Object value) + { + if (value == null ) value = Nullobject; + boolean b = m_HashTable.contains(value); + return b; + } + + public Set entrySet() + { + + Object Key = null; + ISet s = new ISet(); + Enumeration en = m_HashTable.keys(); + while (en.hasMoreElements()) + { + Key = en.nextElement(); + s.add(new Entry(Key,m_HashTable.get(Key))); + } + return s; + } + + public Object get(Object key) + { + + if (key==null) key= Nullobject; + + Object o = m_HashTable.get(key); + + if (o == Nullobject) o=null; + + return o; + } + + public boolean isEmpty() + { + return m_HashTable.isEmpty(); + } + + public Set keySet() + { + ISet s=new ISet(); + Enumeration en = m_HashTable.keys(); + + while (en.hasMoreElements()) + { + s.add(en.nextElement()); + } + + return s; + } + + public Object put(Object key, Object value) + { + if (key==null) key=Nullobject; + if (value==null) value = Nullobject; + return m_HashTable.put(key,value); + } + + public void putAll(Map m) + { + Iterator it = m.entrySet().iterator(); + Object key=null; + Object value=null; + + while (it.hasNext()) + { + Map.Entry me = (Map.Entry)it.next(); + if (me.getKey() == null) key = Nullobject; + else key= me.getKey(); + if (me.getValue()==null) value = Nullobject; + else value = me.getValue(); + m_HashTable.put(key,value); + } + } + + public Object remove(Object key) + { + return m_HashTable.remove(key); + } + + public int size() + { + return m_HashTable.size(); + } + + public Collection values() + { + + ISet s=new ISet(); + Enumeration en = m_HashTable.keys(); + + while (en.hasMoreElements()) + { + Object Key = en.nextElement(); + //s.add(((Map.Entry)m_HashTable.get(Key)).getValue()); + s.add(m_HashTable.get(Key)); + } + return s; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/HashSet.java b/libraries/spongycastle/core/src/main/j2me/java/util/HashSet.java new file mode 100644 index 000000000..e37cb7c8d --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/HashSet.java @@ -0,0 +1,71 @@ +package java.util; + +public class HashSet + extends AbstractSet +{ + private HashMap m_HashMap = null; + + public HashSet() + { + m_HashMap = new HashMap(); + } + + public HashSet(Collection c) + { + m_HashMap = new HashMap(Math.max(11, c.size() * 2)); + addAll(c); + } + + public HashSet(int initialCapacity) + { + m_HashMap = new HashMap(initialCapacity); + + } + + public Iterator iterator() + { + return (m_HashMap.keySet()).iterator(); + } + + public int size() + { + return m_HashMap.size(); + } + + public boolean contains(Object o) + { + return m_HashMap.containsKey(o); + } + + public boolean add(Object o) + { + if (!m_HashMap.containsValue(o)) + { + m_HashMap.put((Object)o, (Object)o); + + return true; + + } + + return false; + } + + public boolean remove(Object o) + { + return (m_HashMap.remove(o) != null); + } + + public void clear() + { + m_HashMap.clear(); + } + + + public Object clone() + { + HashSet hs = new HashSet(); + hs.m_HashMap = (HashMap)m_HashMap.clone(); + return hs; + } + +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Iterator.java b/libraries/spongycastle/core/src/main/j2me/java/util/Iterator.java new file mode 100644 index 000000000..f1b9c05fc --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Iterator.java @@ -0,0 +1,9 @@ + +package java.util; + +public interface Iterator +{ + public abstract boolean hasNext(); + public abstract Object next() throws NoSuchElementException; + public abstract void remove() throws RuntimeException,IllegalStateException; +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/List.java b/libraries/spongycastle/core/src/main/j2me/java/util/List.java new file mode 100644 index 000000000..d9df616fd --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/List.java @@ -0,0 +1,32 @@ +package java.util; + +public interface List + extends Collection +{ + void add(int index, Object element) + throws RuntimeException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException; + + boolean addAll(int index, Collection c) + throws RuntimeException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException; + + Object get(int index) + throws IndexOutOfBoundsException; + + int indexOf(Object o); + + int lastIndexOf(Object o); + + ListIterator listIterator(); + + ListIterator listIterator(int index) + throws IndexOutOfBoundsException; + + Object remove(int index) + throws RuntimeException, IndexOutOfBoundsException; + + Object set(int index, Object element) + throws RuntimeException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException; + + List subList(int fromIndex, int toIndex) + throws IndexOutOfBoundsException; +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/ListIterator.java b/libraries/spongycastle/core/src/main/j2me/java/util/ListIterator.java new file mode 100644 index 000000000..3e08d959c --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/ListIterator.java @@ -0,0 +1,20 @@ +package java.util; + +public interface ListIterator + extends Iterator +{ + public boolean hasPrevious(); + + public Object previous() + throws NoSuchElementException; + + public int nextIndex(); + + public int previousIndex(); + + public void set(Object o) + throws RuntimeException, ClassCastException, IllegalArgumentException, IllegalStateException; + + public void add(Object o) + throws RuntimeException, ClassCastException, IllegalArgumentException; +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Map.java b/libraries/spongycastle/core/src/main/j2me/java/util/Map.java new file mode 100644 index 000000000..cf496f89d --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Map.java @@ -0,0 +1,54 @@ +package java.util; + +public interface Map +{ + + public static interface Entry + { + public Object getKey(); + + public Object getValue(); + + public Object setValue(Object value) + throws RuntimeException, ClassCastException, IllegalArgumentException, NullPointerException; + + public boolean equals(Object o); + + public int hashCode(); + } + + public int size(); + + public boolean isEmpty(); + + public boolean containsKey(Object Key) + throws ClassCastException, NullPointerException; + + public boolean containsValue(Object value); + + public Object get(Object key) + throws ClassCastException, NullPointerException; + + public Object put(Object key, Object value) + throws RuntimeException, ClassCastException, IllegalArgumentException, NullPointerException; + + public Object remove(Object key) + throws RuntimeException; + + public void putAll(Map t) + throws RuntimeException, ClassCastException, IllegalArgumentException, NullPointerException; + + public void clear() + throws RuntimeException; + + public Set keySet(); + + public Collection values(); + + public Set entrySet(); + + public boolean equals(Object o); + + public int hashCode(); + +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Set.java b/libraries/spongycastle/core/src/main/j2me/java/util/Set.java new file mode 100644 index 000000000..6ec45c748 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Set.java @@ -0,0 +1,38 @@ +package java.util; + +public interface Set + extends Collection +{ + + public int size(); + + public boolean isEmpty(); + + public boolean contains(Object o); + + public Iterator iterator(); + + public Object[] toArray(); + + public Object[] toArray(Object[] a); + + public boolean add(Object o); + + public boolean remove(Object o); + + public boolean containsAll(Collection c); + + public boolean addAll(Collection c); + + public boolean retainAll(Collection c); + + public boolean removeAll(Collection c); + + public void clear(); + + public boolean equals(Object o); + + public int hashCode(); + + +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/StringTokenizer.java b/libraries/spongycastle/core/src/main/j2me/java/util/StringTokenizer.java new file mode 100644 index 000000000..5ff7c7060 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/StringTokenizer.java @@ -0,0 +1,115 @@ +package java.util; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +public class StringTokenizer + implements Enumeration +{ + private String s; + private String delims; + private boolean retDelims; + private int maxPos; + + private int pos; + + public StringTokenizer(String s, String delims) + { + this(s, delims, false); + } + + public StringTokenizer(String s, String delims, boolean retDelims) + { + this.s = s; + this.delims = delims; + this.retDelims = retDelims; + this.maxPos = s.length(); + } + + public boolean hasMoreTokens() + { + if (retDelims) + { + return pos < maxPos; + } + else + { + int next = pos; + while (next < maxPos && isDelim(next)) + { + next++; + } + + return next < maxPos; + } + } + + public String nextToken() + { + String tok; + + if (pos == maxPos) + { + throw new NoSuchElementException("no more tokens"); + } + + if (retDelims) + { + if (isDelim(pos)) + { + tok = s.substring(pos, pos + 1); + pos++; + + return tok; + } + } + + while (pos < maxPos && isDelim(pos)) + { + pos++; + } + + int start = pos; + + while (pos < maxPos && !isDelim(pos)) + { + pos++; + } + + if (pos < maxPos) + { + tok = s.substring(start, pos); + } + else + { + tok = s.substring(start); + } + + return tok; + } + + public boolean hasMoreElements() + { + return hasMoreTokens(); + } + + public Object nextElement() + { + return nextToken(); + } + + private boolean isDelim(int index) + { + char c = s.charAt(index); + + for (int i = 0; i != delims.length(); i++) + { + if (delims.charAt(i) == c) + { + return true; + } + } + + return false; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/java/util/Sublist.java b/libraries/spongycastle/core/src/main/j2me/java/util/Sublist.java new file mode 100644 index 000000000..48d8d8e89 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/java/util/Sublist.java @@ -0,0 +1,142 @@ +package java.util; + +public class Sublist + extends AbstractList +{ + AbstractList m_al = null; + int m_fromIndex = 0; + int m_toIndex = 0; + int size = 0; + + public Sublist(AbstractList ali, int fromIndex, int toIndex) + { + m_al = ali; + m_toIndex = toIndex; + m_fromIndex = fromIndex; + size = size(); + } + + public Object set(int index, Object o) + { + if (index < size) + { + o = m_al.set(index + m_fromIndex, o); + if (o != null) + { + size++; + m_toIndex++; + } + return o; + } + else + { + throw new IndexOutOfBoundsException(); + } + } + + public Object get(int index) + throws IndexOutOfBoundsException + { + if (index < size) + { + return m_al.get(index + m_fromIndex); + } + else + { + throw new IndexOutOfBoundsException(); + } + } + + public void add(int index, Object o) + { + + if (index <= size) + { + m_al.add(index + m_fromIndex, o); + m_toIndex++; + size++; + + } + else + { + throw new IndexOutOfBoundsException(); + } + + } + + public Object remove(int index, Object o) + { + if (index < size) + { + Object ob = m_al.remove(index + m_fromIndex); + if (ob != null) + { + m_toIndex--; + size--; + } + return ob; + } + else + { + throw new IndexOutOfBoundsException(); + } + } + + public boolean addAll(int index, Collection c) + { + if (index < size) + { + boolean bool = m_al.addAll(index + m_fromIndex, c); + if (bool) + { + int lange = c.size(); + m_toIndex = m_toIndex + lange; + size = size + lange; + } + return bool; + } + else + { + throw new IndexOutOfBoundsException(); + } + } + + public boolean addAll(Collection c) + { + boolean bool = m_al.addAll(m_toIndex, c); + if (bool) + { + int lange = c.size(); + m_toIndex = m_toIndex + lange; + size = size + lange; + } + return bool; + } + + public void removeRange(int from, int to) + { + if ((from <= to) && (from <= size) && (to <= size)) + { + m_al.removeRange(from, to); + int lange = to - from; + m_toIndex = m_toIndex - lange; + size = size - lange; + } + else + { + if (from > to) + { + throw new IllegalArgumentException(); + } + else + { + throw new IndexOutOfBoundsException(); + } + } + } + + public int size() + { + return (m_toIndex - m_fromIndex); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/ASN1GeneralizedTime.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/ASN1GeneralizedTime.java new file mode 100644 index 000000000..0fa3440a7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/ASN1GeneralizedTime.java @@ -0,0 +1,27 @@ +package org.spongycastle.asn1; + +import java.util.Date; + +public class ASN1GeneralizedTime + extends DERGeneralizedTime +{ + ASN1GeneralizedTime(byte[] bytes) + { + super(bytes); + } + + public ASN1GeneralizedTime(Date date) + { + super(date); + } + + public ASN1GeneralizedTime(Date date, boolean includeMillis) + { + super(date, includeMillis); + } + + public ASN1GeneralizedTime(String time) + { + super(time); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/ASN1UTCTime.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/ASN1UTCTime.java new file mode 100644 index 000000000..d682a182f --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/ASN1UTCTime.java @@ -0,0 +1,22 @@ +package org.spongycastle.asn1; + +import java.util.Date; + +public class ASN1UTCTime + extends DERUTCTime +{ + ASN1UTCTime(byte[] bytes) + { + super(bytes); + } + + public ASN1UTCTime(Date date) + { + super(date); + } + + public ASN1UTCTime(String time) + { + super(time); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERFactory.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERFactory.java new file mode 100644 index 000000000..c002ef6dd --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERFactory.java @@ -0,0 +1,31 @@ +package org.spongycastle.asn1; + +class DERFactory +{ + static final ASN1Sequence EMPTY_SEQUENCE = new DERSequence(); + static final ASN1Set EMPTY_SET = new DERSet(); + + static ASN1Sequence createSequence(ASN1EncodableVector v) + { + if (v.size() < 1) + { + return EMPTY_SEQUENCE; + } + else + { + return new DLSequence(v); + } + } + + static ASN1Set createSet(ASN1EncodableVector v) + { + if (v.size() < 1) + { + return EMPTY_SET; + } + else + { + return new DLSet(v); + } + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERGeneralizedTime.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERGeneralizedTime.java new file mode 100644 index 000000000..82b780375 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERGeneralizedTime.java @@ -0,0 +1,260 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Date; +import java.util.TimeZone; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * Generalized time object. + */ +public class DERGeneralizedTime + extends ASN1Primitive +{ + private byte[] time; + + /** + * return a generalized time from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1GeneralizedTime getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1GeneralizedTime) + { + return (ASN1GeneralizedTime)obj; + } + + if (obj instanceof DERGeneralizedTime) + { + return new ASN1GeneralizedTime(((DERGeneralizedTime)obj).time); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Generalized Time object from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1GeneralizedTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERGeneralizedTime) + { + return getInstance(o); + } + else + { + return new ASN1GeneralizedTime(((ASN1OctetString)o).getOctets()); + } + } + + /** + * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z + * for local time, or Z|[+|-]HHMM on the end, for difference between local + * time and UTC time. The fractional second amount f must consist of at + * least one number with trailing zeroes removed. + * + * @param time the time string. + * @exception IllegalArgumentException if String is an illegal format. + */ + public DERGeneralizedTime( + String time) + { + char last = time.charAt(time.length() - 1); + if (last != 'Z' && !(last >= 0 && last <= '9')) + { + if (time.indexOf('-') < 0 && time.indexOf('+') < 0) + { + throw new IllegalArgumentException("time needs to be in format YYYYMMDDHHMMSS[.f]Z or YYYYMMDDHHMMSS[.f][+-]HHMM"); + } + } + + this.time = Strings.toByteArray(time); + } + + /** + * base constructer from a java.util.date object + */ + public DERGeneralizedTime( + Date time) + { + this.time = Strings.toByteArray(DateFormatter.getGeneralizedTimeDateString(time, false)); + } + + protected DERGeneralizedTime(Date date, boolean includeMillis) + { + this.time = Strings.toByteArray(DateFormatter.getGeneralizedTimeDateString(date, true)); + } + + DERGeneralizedTime( + byte[] bytes) + { + this.time = bytes; + } + + /** + * Return the time. + * @return The time string as it appeared in the encoded object. + */ + public String getTimeString() + { + return Strings.fromByteArray(time); + } + + /** + * return the time - always in the form of + * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *
+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *
+ * dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + *+ * To read in the time and get a date which is compatible with our local + * time zone. + */ + public String getTime() + { + String stime = Strings.fromByteArray(time); + + // + // standardise the format. + // + if (stime.charAt(stime.length() - 1) == 'Z') + { + return stime.substring(0, stime.length() - 1) + "GMT+00:00"; + } + else + { + int signPos = stime.length() - 5; + char sign = stime.charAt(signPos); + if (sign == '-' || sign == '+') + { + return stime.substring(0, signPos) + + "GMT" + + stime.substring(signPos, signPos + 3) + + ":" + + stime.substring(signPos + 3); + } + else + { + signPos = stime.length() - 3; + sign = stime.charAt(signPos); + if (sign == '-' || sign == '+') + { + return stime.substring(0, signPos) + + "GMT" + + stime.substring(signPos) + + ":00"; + } + } + } + return stime + calculateGMTOffset(); + } + + private String calculateGMTOffset() + { + String sign = "+"; + TimeZone timeZone = TimeZone.getDefault(); + int offset = timeZone.getRawOffset(); + if (offset < 0) + { + sign = "-"; + offset = -offset; + } + int hours = offset / (60 * 60 * 1000); + int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000); + +// try +// { +// if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate())) +// { +// hours += sign.equals("+") ? 1 : -1; +// } +// } +// catch (ParseException e) +// { +// // we'll do our best and ignore daylight savings +// } + + return "GMT" + sign + convert(hours) + ":" + convert(minutes); + } + + private String convert(int time) + { + if (time < 10) + { + return "0" + time; + } + + return Integer.toString(time); + } + + public Date getDate() + { + return DateFormatter.fromGeneralizedTimeString(time); + } + + private boolean hasFractionalSeconds() + { + for (int i = 0; i != time.length; i++) + { + if (time[i] == '.') + { + if (i == 14) + { + return true; + } + } + } + return false; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + int length = time.length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.GENERALIZED_TIME, time); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERGeneralizedTime)) + { + return false; + } + + return Arrays.areEqual(time, ((DERGeneralizedTime)o).time); + } + + public int hashCode() + { + return Arrays.hashCode(time); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERUTCTime.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERUTCTime.java new file mode 100644 index 000000000..bc0411f76 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DERUTCTime.java @@ -0,0 +1,259 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Date; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * UTC time object. + */ +public class DERUTCTime + extends ASN1Primitive +{ + private byte[] time; + + /** + * return an UTC Time from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1UTCTime getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1UTCTime) + { + return (ASN1UTCTime)obj; + } + + if (obj instanceof DERUTCTime) + { + return new ASN1UTCTime(((DERUTCTime)obj).time); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an UTC Time from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1UTCTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Object o = obj.getObject(); + + if (explicit || o instanceof ASN1UTCTime) + { + return getInstance(o); + } + else + { + return new ASN1UTCTime(((ASN1OctetString)o).getOctets()); + } + } + + /** + * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were + * never encoded. When you're creating one of these objects from scratch, that's + * what you want to use, otherwise we'll try to deal with whatever gets read from + * the input stream... (this is why the input format is different from the getTime() + * method output). + *
+ * + * @param time the time string. + */ + public DERUTCTime( + String time) + { + if (time.charAt(time.length() - 1) != 'Z') + { + // we accept this as a variation + if (time.indexOf('-') < 0 && time.indexOf('+') < 0) + { + throw new IllegalArgumentException("time needs to be in format YYMMDDHHMMSSZ"); + } + } + + this.time = Strings.toByteArray(time); + } + + /** + * base constructor from a java.util.date object + */ + public DERUTCTime( + Date time) + { + this.time = Strings.toByteArray(DateFormatter.toUTCDateString(time)); + } + + DERUTCTime( + byte[] time) + { + this.time = time; + } + + /** + * return the time as a date based on whatever a 2 digit year will return. For + * standardised processing use getAdjustedDate(). + * + * @return the resulting date + */ + public Date getDate() + { + return DateFormatter.adjustedFromUTCDateString(time); + } + + /** + * return the time as an adjusted date + * in the range of 1950 - 2049. + * + * @return a date in the range of 1950 to 2049. + */ + public Date getAdjustedDate() + { + return DateFormatter.adjustedFromUTCDateString(time); + } + + /** + * return the time - always in the form of + * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *
+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *
+ * dateF = new SimpleDateFormat("yyMMddHHmmssz"); + *+ * To read in the time and get a date which is compatible with our local + * time zone. + *
+ * Note: In some cases, due to the local date processing, this + * may lead to unexpected results. If you want to stick the normal + * convention of 1950 to 2049 use the getAdjustedTime() method. + */ + public String getTime() + { + String stime = Strings.fromByteArray(time); + + // + // standardise the format. + // + if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0) + { + if (stime.length() == 11) + { + return stime.substring(0, 10) + "00GMT+00:00"; + } + else + { + return stime.substring(0, 12) + "GMT+00:00"; + } + } + else + { + int index = stime.indexOf('-'); + if (index < 0) + { + index = stime.indexOf('+'); + } + String d = stime; + + if (index == stime.length() - 3) + { + d += "00"; + } + + if (index == 10) + { + return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); + } + else + { + return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); + } + } + } + + /** + * return a time string as an adjusted date with a 4 digit year. This goes + * in the range of 1950 - 2049. + */ + public String getAdjustedTime() + { + String d = this.getTime(); + + if (d.charAt(0) < '5') + { + return "20" + d; + } + else + { + return "19" + d; + } + } + + /** + * Return the time. + * @return The time string as it appeared in the encoded object. + */ + public String getTimeString() + { + return Strings.fromByteArray(time); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + int length = time.length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.write(BERTags.UTC_TIME); + + int length = time.length; + + out.writeLength(length); + + for (int i = 0; i != length; i++) + { + out.write((byte)time[i]); + } + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERUTCTime)) + { + return false; + } + + return Arrays.areEqual(time, ((DERUTCTime)o).time); + } + + public int hashCode() + { + return Arrays.hashCode(time); + } + + public String toString() + { + return Strings.fromByteArray(time); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DateFormatter.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DateFormatter.java new file mode 100644 index 000000000..7201a2a4c --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/DateFormatter.java @@ -0,0 +1,272 @@ +package org.spongycastle.asn1; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +class DateFormatter +{ + // YYMMDDHHMMSSZ + static String toUTCDateString(Date date) + { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + calendar.setTime(date); + + return format2Year(calendar.get(Calendar.YEAR)) + format2(calendar.get(Calendar.MONTH) + 1) + format2(calendar.get(Calendar.DAY_OF_MONTH)) + + format2(calendar.get(Calendar.HOUR_OF_DAY)) + format2(calendar.get(Calendar.MINUTE)) + format2(calendar.get(Calendar.SECOND)) + "Z"; + } + + static Date adjustedFromUTCDateString(byte[] date) + { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + int year = toInt2(date, 0); + + if (year < 50) + { + year += 2000; + } + else + { + year += 1900; + } + + calendar.setTimeZone(TimeZone.getTimeZone("GMT")); + + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, toInt2(date, 2) - 1); + calendar.set(Calendar.DAY_OF_MONTH, toInt2(date, 4)); + calendar.set(Calendar.HOUR_OF_DAY, toInt2(date, 6)); + calendar.set(Calendar.MINUTE, toInt2(date, 8)); + + int tzChar = 10; + + if (isNumber(date, tzChar)) + { + calendar.set(Calendar.SECOND, toInt2(date, 10)); + tzChar = 12; + } + else + { + calendar.set(Calendar.SECOND, 0); + } + + calendar.set(Calendar.MILLISECOND, 0); + + if (date[tzChar] != 'Z') + { + int hoursOff = 0; + int minutesOff = 0; + + hoursOff = toInt2(date, tzChar + 1) * 60 * 60 * 1000; + + if (date.length > tzChar + 3) + { + minutesOff = toInt2(date, tzChar + 3) * 60 * 1000; + } + + if (date[tzChar] == '-') + { + return new Date(calendar.getTime().getTime() + hoursOff + minutesOff); + } + else + { + return new Date(calendar.getTime().getTime() - (hoursOff + minutesOff)); + } + } + + return calendar.getTime(); + } + + static String getGeneralizedTimeDateString(Date date, boolean includeMillis) + { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + calendar.setTime(date); + + String time = format4Year(calendar.get(Calendar.YEAR)) + format2(calendar.get(Calendar.MONTH) + 1) + format2(calendar.get(Calendar.DAY_OF_MONTH)) + + format2(calendar.get(Calendar.HOUR_OF_DAY)) + format2(calendar.get(Calendar.MINUTE)) + format2(calendar.get(Calendar.SECOND)); + + if (includeMillis) + { + time += "." + format3(calendar.get(Calendar.MILLISECOND)); + } + + return time + "Z"; + } + + static Date fromGeneralizedTimeString(byte[] date) + { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + int year = toInt4(date, 0); + + if (isLocalTime(date)) + { + calendar.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, toInt2(date, 4) - 1); + calendar.set(Calendar.DAY_OF_MONTH, toInt2(date, 6)); + calendar.set(Calendar.HOUR_OF_DAY, toInt2(date, 8)); + calendar.set(Calendar.MINUTE, toInt2(date, 10)); + + int tzChar = 12; + + if (isNumber(date, tzChar)) + { + calendar.set(Calendar.SECOND, toInt2(date, 12)); + tzChar = 14; + } + else + { + calendar.set(Calendar.SECOND, 0); + } + + if (tzChar != date.length && date[tzChar] == '.') + { + int millis = 0; + tzChar++; + if (isNumber(date, tzChar)) + { + millis = (date[tzChar] - '0') * 100; + tzChar++; + } + if (tzChar != date.length && isNumber(date, tzChar)) + { + millis += (date[tzChar] - '0') * 10; + tzChar++; + } + if (tzChar != date.length && isNumber(date, tzChar)) + { + millis += (date[tzChar] - '0'); + tzChar++; + } + calendar.set(Calendar.MILLISECOND, millis); + } + else + { + calendar.set(Calendar.MILLISECOND, 0); + } + + // skip nano-seconds + while (tzChar != date.length && isNumber(date, tzChar)) + { + tzChar++; + } + + if (tzChar != date.length && date[tzChar] != 'Z') + { + int hoursOff = 0; + int minutesOff = 0; + + hoursOff = toInt2(date, tzChar + 1) * 60 * 60 * 1000; + + if (date.length > tzChar + 3) + { + minutesOff = toInt2(date, tzChar + 3) * 60 * 1000; + } + + if (date[tzChar] == '-') + { + return new Date(calendar.getTime().getTime() + hoursOff + minutesOff); + } + else + { + return new Date(calendar.getTime().getTime() - (hoursOff + minutesOff)); + } + } + + return calendar.getTime(); + } + + private static String format2(int v) + { + if (v < 10) + { + return "0" + v; + } + + return Integer.toString(v); + } + + private static String format2Year(int v) + { + if (v > 2000) + { + v = v - 2000; + } + else + { + v = v - 1900; + } + + return format2(v); + } + + private static String format3(int v) + { + if (v < 10) + { + return "00" + v; + } + + if (v < 100) + { + return "0" + v; + } + + return Integer.toString(v); + } + + private static String format4Year(int v) + { + if (v < 10) + { + return "000" + v; + } + + if (v < 100) + { + return "00" + v; + } + + if (v < 1000) + { + return "0" + v; + } + + return Integer.toString(v); + } + + private static boolean isNumber(byte[] input, int off) + { + byte b = input[off]; + return (b >= '0') && (b <= '9'); + } + + private static boolean isLocalTime(byte[] date) + { + for (int i = date.length - 1; i > date.length - 6; i--) + { + if (date[i] == 'Z' || date[i] == '-' || date[i] == '+') + { + return false; + } + } + + return true; + } + + private static int toInt2(byte[] input, int off) + { + return (input[off] - '0') * 10 + (input[off + 1] - '0'); + } + + private static int toInt4(byte[] input, int off) + { + return toInt2(input, off) * 100 + toInt2(input, off + 2) ; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/StreamUtil.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/StreamUtil.java new file mode 100644 index 000000000..4f798ffa5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/StreamUtil.java @@ -0,0 +1,88 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +class StreamUtil +{ + /** + * Find out possible longest length... + * + * @param in input stream of interest + * @return length calculation or MAX_VALUE. + */ + static int findLimit(InputStream in) + { + if (in instanceof LimitedInputStream) + { + return ((LimitedInputStream)in).getRemaining(); + } + else if (in instanceof ASN1InputStream) + { + return ((ASN1InputStream)in).getLimit(); + } + else if (in instanceof ByteArrayInputStream) + { + return ((ByteArrayInputStream)in).available(); + } + + return Integer.MAX_VALUE; + } + + static int calculateBodyLength( + int length) + { + int count = 1; + + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + count++; + } + } + + return count; + } + + static int calculateTagLength(int tagNo) + throws IOException + { + int length = 1; + + if (tagNo >= 31) + { + if (tagNo < 128) + { + length++; + } + else + { + byte[] stack = new byte[5]; + int pos = stack.length; + + stack[--pos] = (byte)(tagNo & 0x7F); + + do + { + tagNo >>= 7; + stack[--pos] = (byte)(tagNo & 0x7F | 0x80); + } + while (tagNo > 127); + + length += stack.length - pos; + } + } + + return length; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/cms/Time.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/cms/Time.java new file mode 100644 index 000000000..f9ddfc2ed --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/cms/Time.java @@ -0,0 +1,122 @@ +package org.spongycastle.asn1.cms; + +import java.util.Calendar; +import java.util.Date; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERUTCTime; + +public class Time + extends ASN1Object + implements ASN1Choice +{ + ASN1Primitive time; + + public static Time getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + public Time( + ASN1Primitive time) + { + if (!(time instanceof DERUTCTime) + && !(time instanceof DERGeneralizedTime)) + { + throw new IllegalArgumentException("unknown object passed to Time"); + } + + this.time = time; + } + + /** + * creates a time object from a given date - if the date is between 1950 + * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime + * is used. + */ + public Time( + Date date) + { + Calendar calendar = Calendar.getInstance(); + + calendar.setTime(date); + + int year = calendar.get(Calendar.YEAR); + + if (year < 1950 || year > 2049) + { + time = new DERGeneralizedTime(date); + } + else + { + time = new DERUTCTime(date); + } + } + + public static Time getInstance( + Object obj) + { + if (obj == null || obj instanceof Time) + { + return (Time)obj; + } + else if (obj instanceof DERUTCTime) + { + return new Time((DERUTCTime)obj); + } + else if (obj instanceof DERGeneralizedTime) + { + return new Time((DERGeneralizedTime)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public String getTime() + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedTime(); + } + else + { + return ((DERGeneralizedTime)time).getTime(); + } + } + + public Date getDate() + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedDate(); + } + else + { + return ((DERGeneralizedTime)time).getDate(); + } + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return time; + } + + public String toString() + { + return getTime(); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/eac/PackedDate.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/eac/PackedDate.java new file mode 100644 index 000000000..e2472251c --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/eac/PackedDate.java @@ -0,0 +1,70 @@ +package org.spongycastle.asn1.eac; + +import org.spongycastle.util.Arrays; + +/** + * EAC encoding date object + */ +public class PackedDate +{ + private byte[] time; + + public PackedDate( + String time) + { + this.time = convert(time); + } + + private byte[] convert(String sTime) + { + char[] digs = sTime.toCharArray(); + byte[] date = new byte[6]; + + for (int i = 0; i != 6; i++) + { + date[i] = (byte)(digs[i] - '0'); + } + + return date; + } + + PackedDate( + byte[] bytes) + { + this.time = bytes; + } + + public int hashCode() + { + return Arrays.hashCode(time); + } + + public boolean equals(Object o) + { + if (!(o instanceof PackedDate)) + { + return false; + } + + PackedDate other = (PackedDate)o; + + return Arrays.areEqual(time, other.time); + } + + public String toString() + { + char[] dateC = new char[time.length]; + + for (int i = 0; i != dateC.length; i++) + { + dateC[i] = (char)((time[i] & 0xff) + '0'); + } + + return new String(dateC); + } + + public byte[] getEncoding() + { + return time; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/x509/Time.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/x509/Time.java new file mode 100644 index 000000000..0fe22ddc7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/asn1/x509/Time.java @@ -0,0 +1,122 @@ +package org.spongycastle.asn1.x509; + +import java.util.Calendar; +import java.util.Date; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERUTCTime; + +public class Time + extends ASN1Object + implements ASN1Choice +{ + ASN1Primitive time; + + public static Time getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + public Time( + ASN1Primitive time) + { + if (!(time instanceof DERUTCTime) + && !(time instanceof DERGeneralizedTime)) + { + throw new IllegalArgumentException("unknown object passed to Time"); + } + + this.time = time; + } + + /** + * creates a time object from a given date - if the date is between 1950 + * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime + * is used. + */ + public Time( + Date date) + { + Calendar calendar = Calendar.getInstance(); + + calendar.setTime(date); + + int year = calendar.get(Calendar.YEAR); + + if (year < 1950 || year > 2049) + { + time = new DERGeneralizedTime(date); + } + else + { + time = new DERUTCTime(date); + } + } + + public static Time getInstance( + Object obj) + { + if (obj == null || obj instanceof Time) + { + return (Time)obj; + } + else if (obj instanceof DERUTCTime) + { + return new Time((DERUTCTime)obj); + } + else if (obj instanceof DERGeneralizedTime) + { + return new Time((DERGeneralizedTime)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public String getTime() + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedTime(); + } + else + { + return ((DERGeneralizedTime)time).getTime(); + } + } + + public Date getDate() + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedDate(); + } + else + { + return ((DERGeneralizedTime)time).getDate(); + } + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return time; + } + + public String toString() + { + return getTime(); + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java new file mode 100644 index 000000000..3149a1f37 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java @@ -0,0 +1,238 @@ +package org.spongycastle.crypto.encodings; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; + +import java.security.SecureRandom; + +/** + * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this + * depends on your application - see PKCS1 Version 2 for details. + */ +public class PKCS1Encoding + implements AsymmetricBlockCipher +{ + /** + * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to + * work with one of these set the system property org.spongycastle.pkcs1.strict to false. + *
+ * The system property is checked during construction of the encoding object, it is set to + * true by default. + *
+ */ + public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.spongycastle.pkcs1.strict"; + + private static final int HEADER_LENGTH = 10; + + private SecureRandom random; + private AsymmetricBlockCipher engine; + private boolean forEncryption; + private boolean forPrivateKey; + private boolean useStrictLength; + + /** + * Basic constructor. + * @param cipher + */ + public PKCS1Encoding( + AsymmetricBlockCipher cipher) + { + this.engine = cipher; + this.useStrictLength = useStrict(); + } + + // + // for J2ME compatibility + // + private boolean useStrict() + { + String strict = System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY); + + return strict == null || strict.equals("true"); + } + + public AsymmetricBlockCipher getUnderlyingCipher() + { + return engine; + } + + public void init( + boolean forEncryption, + CipherParameters param) + { + AsymmetricKeyParameter kParam; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + this.random = rParam.getRandom(); + kParam = (AsymmetricKeyParameter)rParam.getParameters(); + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)param; + } + + engine.init(forEncryption, param); + + this.forPrivateKey = kParam.isPrivate(); + this.forEncryption = forEncryption; + } + + public int getInputBlockSize() + { + int baseBlockSize = engine.getInputBlockSize(); + + if (forEncryption) + { + return baseBlockSize - HEADER_LENGTH; + } + else + { + return baseBlockSize; + } + } + + public int getOutputBlockSize() + { + int baseBlockSize = engine.getOutputBlockSize(); + + if (forEncryption) + { + return baseBlockSize; + } + else + { + return baseBlockSize - HEADER_LENGTH; + } + } + + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forEncryption) + { + return encodeBlock(in, inOff, inLen); + } + else + { + return decodeBlock(in, inOff, inLen); + } + } + + private byte[] encodeBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (inLen > getInputBlockSize()) + { + throw new IllegalArgumentException("input data too large"); + } + + byte[] block = new byte[engine.getInputBlockSize()]; + + if (forPrivateKey) + { + block[0] = 0x01; // type code 1 + + for (int i = 1; i != block.length - inLen - 1; i++) + { + block[i] = (byte)0xFF; + } + } + else + { + random.nextBytes(block); // random fill + + block[0] = 0x02; // type code 2 + + // + // a zero byte marks the end of the padding, so all + // the pad bytes must be non-zero. + // + for (int i = 1; i != block.length - inLen - 1; i++) + { + while (block[i] == 0) + { + block[i] = (byte)random.nextInt(); + } + } + } + + block[block.length - inLen - 1] = 0x00; // mark the end of the padding + System.arraycopy(in, inOff, block, block.length - inLen, inLen); + + return engine.processBlock(block, 0, block.length); + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format. + */ + private byte[] decodeBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] block = engine.processBlock(in, inOff, inLen); + + if (block.length < getOutputBlockSize()) + { + throw new InvalidCipherTextException("block truncated"); + } + + byte type = block[0]; + + if (type != 1 && type != 2) + { + throw new InvalidCipherTextException("unknown block type"); + } + + if (useStrictLength && block.length != engine.getOutputBlockSize()) + { + throw new InvalidCipherTextException("block incorrect size"); + } + + // + // find and extract the message block. + // + int start; + + for (start = 1; start != block.length; start++) + { + byte pad = block[start]; + + if (pad == 0) + { + break; + } + if (type == 1 && pad != (byte)0xff) + { + throw new InvalidCipherTextException("block padding incorrect"); + } + } + + start++; // data should start at the next byte + + if (start > block.length || start < HEADER_LENGTH) + { + throw new InvalidCipherTextException("no data in block"); + } + + byte[] result = new byte[block.length - start]; + + System.arraycopy(block, start, result, 0, result.length); + + return result; + } +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java new file mode 100644 index 000000000..32908a67e --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java @@ -0,0 +1,177 @@ +package org.spongycastle.crypto.examples; + +import java.io.*; +import java.lang.*; + +import javax.microedition.midlet.MIDlet; +import javax.microedition.lcdui.*; + +import org.spongycastle.util.test.*; +import org.spongycastle.util.encoders.*; + +import org.spongycastle.crypto.*; +import org.spongycastle.crypto.paddings.*; +import org.spongycastle.crypto.engines.*; +import org.spongycastle.crypto.modes.*; +import org.spongycastle.crypto.params.*; + +/** + * MIDP is a simple graphics application for the J2ME CLDC/MIDP. + * + * It has hardcoded values for the key and plain text. It also performs the + * standard testing for the chosen cipher, and displays the results. + * + * This example shows how to use the light-weight API and a symmetric cipher. + * + */ +public class MIDPTest extends MIDlet +{ + private Display d = null; + + private boolean doneEncrypt = false; + + private String key = "0123456789abcdef0123456789abcdef"; + private String plainText = "www.bouncycastle.org"; + private byte[] keyBytes = null; + private byte[] cipherText = null; + private BufferedBlockCipher cipher = null; + + private String[] cipherNames = {"DES", "DESede", "IDEA", "Rijndael", "Twofish"}; + + private Form output = null; + + public void startApp() + { + Display.getDisplay(this).setCurrent(output); + } + + public void pauseApp() + { + + } + + public void destroyApp(boolean unconditional) + { + + } + + public MIDPTest() + { + output = new Form("BouncyCastle"); + output.append("Key: " + key.substring(0, 7) + "...\n"); + output.append("In : " + plainText.substring(0, 7) + "...\n"); + + cipherText = performEncrypt(Hex.decode(key.getBytes()), plainText); + String ctS = new String(Hex.encode(cipherText)); + + output.append("\nCT : " + ctS.substring(0, 7) + "...\n"); + + String decryptText = performDecrypt(Hex.decode(key.getBytes()), cipherText); + + output.append("PT : " + decryptText.substring(0, 7) + "...\n"); + + if (decryptText.compareTo(plainText) == 0) + { + output.append("Success"); + } + else + { + output.append("Failure"); + message("[" + plainText + "]"); + message("[" + decryptText + "]"); + } + + } + + private byte[] performEncrypt(byte[] key, String plainText) + { + byte[] ptBytes = plainText.getBytes(); + + cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(getEngineInstance())); + + String name = cipher.getUnderlyingCipher().getAlgorithmName(); + message("Using " + name); + + cipher.init(true, new KeyParameter(key)); + + byte[] rv = new byte[cipher.getOutputSize(ptBytes.length)]; + + int oLen = cipher.processBytes(ptBytes, 0, ptBytes.length, rv, 0); + try + { + cipher.doFinal(rv, oLen); + } + catch (CryptoException ce) + { + message("Ooops, encrypt exception"); + status(ce.toString()); + } + return rv; + } + + private String performDecrypt(byte[] key, byte[] cipherText) + { + cipher.init(false, new KeyParameter(key)); + + byte[] rv = new byte[cipher.getOutputSize(cipherText.length)]; + + int oLen = cipher.processBytes(cipherText, 0, cipherText.length, rv, 0); + try + { + cipher.doFinal(rv, oLen); + } + catch (CryptoException ce) + { + message("Ooops, decrypt exception"); + status(ce.toString()); + } + return new String(rv).trim(); + } + + private int whichCipher() + { + return 4; // DES + } + + private BlockCipher getEngineInstance() + { + // returns a block cipher according to the current + // state of the radio button lists. This is only + // done prior to encryption. + BlockCipher rv = null; + + switch (whichCipher()) + { + case 0 : + rv = new DESEngine(); + break; + case 1 : + rv = new DESedeEngine(); + break; + case 2 : + rv = new IDEAEngine(); + break; + case 3 : + rv = new RijndaelEngine(); + break; + case 4 : + rv = new TwofishEngine(); + break; + default : + rv = new DESEngine(); + break; + } + return rv; + } + + public void message(String s) + { + System.out.println("M:" + s); + } + + public void status(String s) + { + System.out.println("S:" + s); + } + +} diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad new file mode 100644 index 000000000..584f3156c --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad @@ -0,0 +1,6 @@ +MIDlet-1: MIDPTest, , org.spongycastle.crypto.examples.MIDPTest +MIDlet-Name: MIDPTest +MIDlet-Jar-Size: 300000 +MIDlet-Jar-URL: midp_test.jar +MIDlet-Vendor: The Legion of the Bouncy Castle +MIDlet-Version: 1.0.0 diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf new file mode 100644 index 000000000..273521093 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf @@ -0,0 +1,7 @@ +MIDlet-1: MIDPTTest, , org.spongycastle.crypto.examples.MIDPTest +MIDlet-Name: MIDPTest +MIDlet-Version: 1.0.0 +MIDlet-Vendor: Jon Eaves +Created-By: 1.3.1 (Sun Microsystems Inc.) +MicroEdition-Configuration: CLDC-1.0 +MicroEdition-Profile: MIDP-1.0 diff --git a/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java new file mode 100644 index 000000000..b29cd4418 --- /dev/null +++ b/libraries/spongycastle/core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java @@ -0,0 +1,258 @@ +package org.spongycastle.crypto.params; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.digests.SkeinDigest; +import org.spongycastle.crypto.digests.SkeinEngine; +import org.spongycastle.crypto.macs.SkeinMac; +import org.spongycastle.util.Integers; + +/** + * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags. + * + * Parameterised Skein can be used for: + *null
if not
+ * set.
+ */
+ public byte[] getKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+ * null
if not set.
+ */
+ public byte[] getPersonalisation()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+ * null
if not set.
+ */
+ public byte[] getPublicKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+ * null
if not set.
+ */
+ public byte[] getKeyIdentifier()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or null
if
+ * not set.
+ */
+ public byte[] getNonce()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE));
+ }
+
+ /**
+ * A builder for {@link SkeinParameters}.
+ */
+ public static class Builder
+ {
+ private Hashtable parameters = new Hashtable();
+
+ public Builder()
+ {
+ }
+
+ public Builder(Hashtable paramsMap)
+ {
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, paramsMap.get(key));
+ }
+ }
+
+ public Builder(SkeinParameters params)
+ {
+ Enumeration keys = params.parameters.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, params.parameters.get(key));
+ }
+ }
+
+ /**
+ * Sets a parameters to apply to the Skein hash function.+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +public class LICENSE +{ + public static String licenseText = + "Copyright (c) 2000-2013 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) " + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software " + + System.getProperty("line.separator") + + "and associated documentation files (the \"Software\"), to deal in the Software without restriction, " + + System.getProperty("line.separator") + + "including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, " + + System.getProperty("line.separator") + + "and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so," + + System.getProperty("line.separator") + + "subject to the following conditions:" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "The above copyright notice and this permission notice shall be included in all copies or substantial" + + System.getProperty("line.separator") + + "portions of the Software." + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED," + + System.getProperty("line.separator") + + "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR" + + System.getProperty("line.separator") + + "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE" + + System.getProperty("line.separator") + + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR" + + System.getProperty("line.separator") + + "OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER" + + System.getProperty("line.separator") + + "DEALINGS IN THE SOFTWARE."; + + public static void main( + String[] args) + { + System.out.println(licenseText); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ApplicationSpecificParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ApplicationSpecificParser.java new file mode 100644 index 000000000..5a48fbf5a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ApplicationSpecificParser.java @@ -0,0 +1,10 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public interface ASN1ApplicationSpecificParser + extends ASN1Encodable, InMemoryRepresentable +{ + ASN1Encodable readObject() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Boolean.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Boolean.java new file mode 100644 index 000000000..9a9c3a084 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Boolean.java @@ -0,0 +1,15 @@ +package org.spongycastle.asn1; + +public class ASN1Boolean + extends DERBoolean +{ + public ASN1Boolean(boolean value) + { + super(value); + } + + ASN1Boolean(byte[] value) + { + super(value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Choice.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Choice.java new file mode 100644 index 000000000..92e15c08e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Choice.java @@ -0,0 +1,14 @@ +package org.spongycastle.asn1; + +/** + * Marker interface for CHOICE objects - if you implement this in a role your + * own object any attempt to tag the object implicitly will convert the tag to + * an explicit one as the encoding rules require. + *
+ * If you use this interface your class should also implement the getInstance + * pattern which takes a tag object and the tagging mode used. + */ +public interface ASN1Choice +{ + // marker interface +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Encodable.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Encodable.java new file mode 100644 index 000000000..ec7607d5e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Encodable.java @@ -0,0 +1,6 @@ +package org.spongycastle.asn1; + +public interface ASN1Encodable +{ + ASN1Primitive toASN1Primitive(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1EncodableVector.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1EncodableVector.java new file mode 100644 index 000000000..0ef3af173 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1EncodableVector.java @@ -0,0 +1,36 @@ +package org.spongycastle.asn1; + +import java.util.Enumeration; +import java.util.Vector; + +public class ASN1EncodableVector +{ + Vector v = new Vector(); + + public ASN1EncodableVector() + { + } + + public void add(ASN1Encodable obj) + { + v.addElement(obj); + } + + public void addAll(ASN1EncodableVector other) + { + for (Enumeration en = other.v.elements(); en.hasMoreElements();) + { + v.addElement(en.nextElement()); + } + } + + public ASN1Encodable get(int i) + { + return (ASN1Encodable)v.elementAt(i); + } + + public int size() + { + return v.size(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Encoding.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Encoding.java new file mode 100644 index 000000000..7f7178d4c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Encoding.java @@ -0,0 +1,8 @@ +package org.spongycastle.asn1; + +public interface ASN1Encoding +{ + static final String DER = "DER"; + static final String DL = "DL"; + static final String BER = "BER"; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Enumerated.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Enumerated.java new file mode 100644 index 000000000..02d1a308c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Enumerated.java @@ -0,0 +1,22 @@ +package org.spongycastle.asn1; + +import java.math.BigInteger; + +public class ASN1Enumerated + extends DEREnumerated +{ + ASN1Enumerated(byte[] bytes) + { + super(bytes); + } + + public ASN1Enumerated(BigInteger value) + { + super(value); + } + + public ASN1Enumerated(int value) + { + super(value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Exception.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Exception.java new file mode 100644 index 000000000..20c8a934c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Exception.java @@ -0,0 +1,25 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public class ASN1Exception + extends IOException +{ + private Throwable cause; + + ASN1Exception(String message) + { + super(message); + } + + ASN1Exception(String message, Throwable cause) + { + super(message); + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1GeneralizedTime.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1GeneralizedTime.java new file mode 100644 index 000000000..d507decd9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1GeneralizedTime.java @@ -0,0 +1,22 @@ +package org.spongycastle.asn1; + +import java.util.Date; + +public class ASN1GeneralizedTime + extends DERGeneralizedTime +{ + ASN1GeneralizedTime(byte[] bytes) + { + super(bytes); + } + + public ASN1GeneralizedTime(Date time) + { + super(time); + } + + public ASN1GeneralizedTime(String time) + { + super(time); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Generator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Generator.java new file mode 100644 index 000000000..c5c087f61 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Generator.java @@ -0,0 +1,15 @@ +package org.spongycastle.asn1; + +import java.io.OutputStream; + +public abstract class ASN1Generator +{ + protected OutputStream _out; + + public ASN1Generator(OutputStream out) + { + _out = out; + } + + public abstract OutputStream getRawOutputStream(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1InputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1InputStream.java new file mode 100644 index 000000000..19166659c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1InputStream.java @@ -0,0 +1,466 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.util.io.Streams; + +/** + * a general purpose ASN.1 decoder - note: this class differs from the + * others in that it returns null after it has read the last object in + * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is + * returned. + */ +public class ASN1InputStream + extends FilterInputStream + implements BERTags +{ + private final int limit; + private final boolean lazyEvaluate; + + private final byte[][] tmpBuffers; + + public ASN1InputStream( + InputStream is) + { + this(is, StreamUtil.findLimit(is)); + } + + /** + * Create an ASN1InputStream based on the input byte array. The length of DER objects in + * the stream is automatically limited to the length of the input array. + * + * @param input array containing ASN.1 encoded data. + */ + public ASN1InputStream( + byte[] input) + { + this(new ByteArrayInputStream(input), input.length); + } + + /** + * Create an ASN1InputStream based on the input byte array. The length of DER objects in + * the stream is automatically limited to the length of the input array. + * + * @param input array containing ASN.1 encoded data. + * @param lazyEvaluate true if parsing inside constructed objects can be delayed. + */ + public ASN1InputStream( + byte[] input, + boolean lazyEvaluate) + { + this(new ByteArrayInputStream(input), input.length, lazyEvaluate); + } + + /** + * Create an ASN1InputStream where no DER object will be longer than limit. + * + * @param input stream containing ASN.1 encoded data. + * @param limit maximum size of a DER encoded object. + */ + public ASN1InputStream( + InputStream input, + int limit) + { + this(input, limit, false); + } + + /** + * Create an ASN1InputStream where no DER object will be longer than limit, and constructed + * objects such as sequences will be parsed lazily. + * + * @param input stream containing ASN.1 encoded data. + * @param lazyEvaluate true if parsing inside constructed objects can be delayed. + */ + public ASN1InputStream( + InputStream input, + boolean lazyEvaluate) + { + this(input, StreamUtil.findLimit(input), lazyEvaluate); + } + + /** + * Create an ASN1InputStream where no DER object will be longer than limit, and constructed + * objects such as sequences will be parsed lazily. + * + * @param input stream containing ASN.1 encoded data. + * @param limit maximum size of a DER encoded object. + * @param lazyEvaluate true if parsing inside constructed objects can be delayed. + */ + public ASN1InputStream( + InputStream input, + int limit, + boolean lazyEvaluate) + { + super(input); + this.limit = limit; + this.lazyEvaluate = lazyEvaluate; + this.tmpBuffers = new byte[11][]; + } + + int getLimit() + { + return limit; + } + + protected int readLength() + throws IOException + { + return readLength(this, limit); + } + + protected void readFully( + byte[] bytes) + throws IOException + { + if (Streams.readFully(this, bytes) != bytes.length) + { + throw new EOFException("EOF encountered in middle of object"); + } + } + + /** + * build an object given its tag and the number of bytes to construct it from. + */ + protected ASN1Primitive buildObject( + int tag, + int tagNo, + int length) + throws IOException + { + boolean isConstructed = (tag & CONSTRUCTED) != 0; + + DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length); + + if ((tag & APPLICATION) != 0) + { + return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); + } + + if ((tag & TAGGED) != 0) + { + return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo); + } + + if (isConstructed) + { + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case OCTET_STRING: + // + // yes, people actually do this... + // + ASN1EncodableVector v = buildDEREncodableVector(defIn); + ASN1OctetString[] strings = new ASN1OctetString[v.size()]; + + for (int i = 0; i != strings.length; i++) + { + strings[i] = (ASN1OctetString)v.get(i); + } + + return new BEROctetString(strings); + case SEQUENCE: + if (lazyEvaluate) + { + return new LazyEncodedSequence(defIn.toByteArray()); + } + else + { + return DERFactory.createSequence(buildDEREncodableVector(defIn)); + } + case SET: + return DERFactory.createSet(buildDEREncodableVector(defIn)); + case EXTERNAL: + return new DERExternal(buildDEREncodableVector(defIn)); + default: + throw new IOException("unknown tag " + tagNo + " encountered"); + } + } + + return createPrimitiveDERObject(tagNo, defIn, tmpBuffers); + } + + ASN1EncodableVector buildEncodableVector() + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + ASN1Primitive o; + + while ((o = readObject()) != null) + { + v.add(o); + } + + return v; + } + + ASN1EncodableVector buildDEREncodableVector( + DefiniteLengthInputStream dIn) throws IOException + { + return new ASN1InputStream(dIn).buildEncodableVector(); + } + + public ASN1Primitive readObject() + throws IOException + { + int tag = read(); + if (tag <= 0) + { + if (tag == 0) + { + throw new IOException("unexpected end-of-contents marker"); + } + + return null; + } + + // + // calculate tag number + // + int tagNo = readTagNumber(this, tag); + + boolean isConstructed = (tag & CONSTRUCTED) != 0; + + // + // calculate length + // + int length = readLength(); + + if (length < 0) // indefinite length method + { + if (!isConstructed) + { + throw new IOException("indefinite length primitive encoding encountered"); + } + + IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit); + ASN1StreamParser sp = new ASN1StreamParser(indIn, limit); + + if ((tag & APPLICATION) != 0) + { + return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject(); + } + + if ((tag & TAGGED) != 0) + { + return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject(); + } + + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case OCTET_STRING: + return new BEROctetStringParser(sp).getLoadedObject(); + case SEQUENCE: + return new BERSequenceParser(sp).getLoadedObject(); + case SET: + return new BERSetParser(sp).getLoadedObject(); + case EXTERNAL: + return new DERExternalParser(sp).getLoadedObject(); + default: + throw new IOException("unknown BER object encountered"); + } + } + else + { + try + { + return buildObject(tag, tagNo, length); + } + catch (IllegalArgumentException e) + { + throw new ASN1Exception("corrupted stream detected", e); + } + } + } + + static int readTagNumber(InputStream s, int tag) + throws IOException + { + int tagNo = tag & 0x1f; + + // + // with tagged object tag number is bottom 5 bits, or stored at the start of the content + // + if (tagNo == 0x1f) + { + tagNo = 0; + + int b = s.read(); + + // X.690-0207 8.1.2.4.2 + // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." + if ((b & 0x7f) == 0) // Note: -1 will pass + { + throw new IOException("corrupted stream - invalid high tag number found"); + } + + while ((b >= 0) && ((b & 0x80) != 0)) + { + tagNo |= (b & 0x7f); + tagNo <<= 7; + b = s.read(); + } + + if (b < 0) + { + throw new EOFException("EOF found inside tag value."); + } + + tagNo |= (b & 0x7f); + } + + return tagNo; + } + + static int readLength(InputStream s, int limit) + throws IOException + { + int length = s.read(); + if (length < 0) + { + throw new EOFException("EOF found when length expected"); + } + + if (length == 0x80) + { + return -1; // indefinite-length encoding + } + + if (length > 127) + { + int size = length & 0x7f; + + // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here + if (size > 4) + { + throw new IOException("DER length more than 4 bytes: " + size); + } + + length = 0; + for (int i = 0; i < size; i++) + { + int next = s.read(); + + if (next < 0) + { + throw new EOFException("EOF found reading length"); + } + + length = (length << 8) + next; + } + + if (length < 0) + { + throw new IOException("corrupted stream - negative length found"); + } + + if (length >= limit) // after all we must have read at least 1 byte + { + throw new IOException("corrupted stream - out of bounds length found"); + } + } + + return length; + } + + private static byte[] getBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers) + throws IOException + { + int len = defIn.getRemaining(); + if (defIn.getRemaining() < tmpBuffers.length) + { + byte[] buf = tmpBuffers[len]; + + if (buf == null) + { + buf = tmpBuffers[len] = new byte[len]; + } + + Streams.readFully(defIn, buf); + + return buf; + } + else + { + return defIn.toByteArray(); + } + } + + private static char[] getBMPCharBuffer(DefiniteLengthInputStream defIn) + throws IOException + { + int len = defIn.getRemaining() / 2; + char[] buf = new char[len]; + int totalRead = 0; + while (totalRead < len) + { + int ch1 = defIn.read(); + if (ch1 < 0) + { + break; + } + int ch2 = defIn.read(); + if (ch2 < 0) + { + break; + } + buf[totalRead++] = (char)((ch1 << 8) | (ch2 & 0xff)); + } + + return buf; + } + + static ASN1Primitive createPrimitiveDERObject( + int tagNo, + DefiniteLengthInputStream defIn, + byte[][] tmpBuffers) + throws IOException + { + switch (tagNo) + { + case BIT_STRING: + return DERBitString.fromInputStream(defIn.getRemaining(), defIn); + case BMP_STRING: + return new DERBMPString(getBMPCharBuffer(defIn)); + case BOOLEAN: + return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers)); + case ENUMERATED: + return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers)); + case GENERALIZED_TIME: + return new ASN1GeneralizedTime(defIn.toByteArray()); + case GENERAL_STRING: + return new DERGeneralString(defIn.toByteArray()); + case IA5_STRING: + return new DERIA5String(defIn.toByteArray()); + case INTEGER: + return new ASN1Integer(defIn.toByteArray()); + case NULL: + return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?) + case NUMERIC_STRING: + return new DERNumericString(defIn.toByteArray()); + case OBJECT_IDENTIFIER: + return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers)); + case OCTET_STRING: + return new DEROctetString(defIn.toByteArray()); + case PRINTABLE_STRING: + return new DERPrintableString(defIn.toByteArray()); + case T61_STRING: + return new DERT61String(defIn.toByteArray()); + case UNIVERSAL_STRING: + return new DERUniversalString(defIn.toByteArray()); + case UTC_TIME: + return new ASN1UTCTime(defIn.toByteArray()); + case UTF8_STRING: + return new DERUTF8String(defIn.toByteArray()); + case VISIBLE_STRING: + return new DERVisibleString(defIn.toByteArray()); + default: + throw new IOException("unknown tag " + tagNo + " encountered"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Integer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Integer.java new file mode 100644 index 000000000..d6820c345 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Integer.java @@ -0,0 +1,22 @@ +package org.spongycastle.asn1; + +import java.math.BigInteger; + +public class ASN1Integer + extends DERInteger +{ + ASN1Integer(byte[] bytes) + { + super(bytes); + } + + public ASN1Integer(BigInteger value) + { + super(value); + } + + public ASN1Integer(long value) + { + super(value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Null.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Null.java new file mode 100644 index 000000000..647060c1f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Null.java @@ -0,0 +1,67 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +/** + * A NULL object. + */ +public abstract class ASN1Null + extends ASN1Primitive +{ + /** + * @deprecated use DERNull.INSTANCE + */ + public ASN1Null() + { + } + + public static ASN1Null getInstance(Object o) + { + if (o instanceof ASN1Null) + { + return (ASN1Null)o; + } + + if (o != null) + { + try + { + return ASN1Null.getInstance(ASN1Primitive.fromByteArray((byte[])o)); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct NULL from byte[]: " + e.getMessage()); + } + catch (ClassCastException e) + { + throw new IllegalArgumentException("unknown object in getInstance(): " + o.getClass().getName()); + } + } + + return null; + } + + public int hashCode() + { + return -1; + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1Null)) + { + return false; + } + + return true; + } + + abstract void encode(ASN1OutputStream out) + throws IOException; + + public String toString() + { + return "NULL"; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Object.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Object.java new file mode 100644 index 000000000..fdf0970a0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Object.java @@ -0,0 +1,97 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public abstract class ASN1Object + implements ASN1Encodable +{ + /** + * Return the default BER or DER encoding for this object. + * + * @return BER/DER byte encoded object. + * @throws java.io.IOException on encoding error. + */ + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + aOut.writeObject(this); + + return bOut.toByteArray(); + } + + /** + * Return either the default for "BER" or a DER encoding if "DER" is specified. + * + * @param encoding name of encoding to use. + * @return byte encoded object. + * @throws IOException on encoding error. + */ + public byte[] getEncoded( + String encoding) + throws IOException + { + if (encoding.equals(ASN1Encoding.DER)) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + dOut.writeObject(this); + + return bOut.toByteArray(); + } + else if (encoding.equals(ASN1Encoding.DL)) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DLOutputStream dOut = new DLOutputStream(bOut); + + dOut.writeObject(this); + + return bOut.toByteArray(); + } + + return this.getEncoded(); + } + + public int hashCode() + { + return this.toASN1Primitive().hashCode(); + } + + public boolean equals( + Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof ASN1Encodable)) + { + return false; + } + + ASN1Encodable other = (ASN1Encodable)o; + + return this.toASN1Primitive().equals(other.toASN1Primitive()); + } + + /** + * @deprecated use toASN1Primitive() + * @return the underlying primitive type. + */ + public ASN1Primitive toASN1Object() + { + return this.toASN1Primitive(); + } + + protected static boolean hasEncodedTagValue(Object obj, int tagValue) + { + return (obj instanceof byte[]) && ((byte[])obj)[0] == tagValue; + } + + public abstract ASN1Primitive toASN1Primitive(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ObjectIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ObjectIdentifier.java new file mode 100644 index 000000000..d9c0fa9bc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ObjectIdentifier.java @@ -0,0 +1,42 @@ +package org.spongycastle.asn1; + +public class ASN1ObjectIdentifier + extends DERObjectIdentifier +{ + public ASN1ObjectIdentifier(String identifier) + { + super(identifier); + } + + ASN1ObjectIdentifier(byte[] bytes) + { + super(bytes); + } + + ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branch) + { + super(oid, branch); + } + + /** + * Return an OID that creates a branch under the current one. + * + * @param branchID node numbers for the new branch. + * @return the OID for the new created branch. + */ + public ASN1ObjectIdentifier branch(String branchID) + { + return new ASN1ObjectIdentifier(this, branchID); + } + + /** + * Return true if this oid is an extension of the passed in branch, stem. + * @param stem the arc or branch that is a possible parent. + * @return true if the branch is on the passed in stem, false otherwise. + */ + public boolean on(ASN1ObjectIdentifier stem) + { + String id = getId(), stemId = stem.getId(); + return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OctetString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OctetString.java new file mode 100644 index 000000000..1e67ad8fd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OctetString.java @@ -0,0 +1,146 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Hex; + +public abstract class ASN1OctetString + extends ASN1Primitive + implements ASN1OctetStringParser +{ + byte[] string; + + /** + * return an Octet String from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1OctetString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof ASN1OctetString) + { + return getInstance(o); + } + else + { + return BEROctetString.fromSequence(ASN1Sequence.getInstance(o)); + } + } + + /** + * return an Octet String from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1OctetString getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1OctetString) + { + return (ASN1OctetString)obj; + } + else if (obj instanceof byte[]) + { + try + { + return ASN1OctetString.getInstance(ASN1Primitive.fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct OCTET STRING from byte[]: " + e.getMessage()); + } + } + else if (obj instanceof ASN1Encodable) + { + ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive(); + + if (primitive instanceof ASN1OctetString) + { + return (ASN1OctetString)primitive; + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * @param string the octets making up the octet string. + */ + public ASN1OctetString( + byte[] string) + { + if (string == null) + { + throw new NullPointerException("string cannot be null"); + } + this.string = string; + } + + public InputStream getOctetStream() + { + return new ByteArrayInputStream(string); + } + + public ASN1OctetStringParser parser() + { + return this; + } + + public byte[] getOctets() + { + return string; + } + + public int hashCode() + { + return Arrays.hashCode(this.getOctets()); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1OctetString)) + { + return false; + } + + ASN1OctetString other = (ASN1OctetString)o; + + return Arrays.areEqual(string, other.string); + } + + public ASN1Primitive getLoadedObject() + { + return this.toASN1Primitive(); + } + + ASN1Primitive toDERObject() + { + return new DEROctetString(string); + } + + ASN1Primitive toDLObject() + { + return new DEROctetString(string); + } + + abstract void encode(ASN1OutputStream out) + throws IOException; + + public String toString() + { + return "#"+new String(Hex.encode(string)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OctetStringParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OctetStringParser.java new file mode 100644 index 000000000..6b51608ec --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OctetStringParser.java @@ -0,0 +1,9 @@ +package org.spongycastle.asn1; + +import java.io.InputStream; + +public interface ASN1OctetStringParser + extends ASN1Encodable, InMemoryRepresentable +{ + public InputStream getOctetStream(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OutputStream.java new file mode 100644 index 000000000..8e663ac1b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1OutputStream.java @@ -0,0 +1,194 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Stream that produces output based on the default encoding for the passed in objects. + */ +public class ASN1OutputStream +{ + private OutputStream os; + + public ASN1OutputStream( + OutputStream os) + { + this.os = os; + } + + void writeLength( + int length) + throws IOException + { + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + write((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + write((byte)(length >> i)); + } + } + else + { + write((byte)length); + } + } + + void write(int b) + throws IOException + { + os.write(b); + } + + void write(byte[] bytes) + throws IOException + { + os.write(bytes); + } + + void write(byte[] bytes, int off, int len) + throws IOException + { + os.write(bytes, off, len); + } + + void writeEncoded( + int tag, + byte[] bytes) + throws IOException + { + write(tag); + writeLength(bytes.length); + write(bytes); + } + + void writeTag(int flags, int tagNo) + throws IOException + { + if (tagNo < 31) + { + write(flags | tagNo); + } + else + { + write(flags | 0x1f); + if (tagNo < 128) + { + write(tagNo); + } + else + { + byte[] stack = new byte[5]; + int pos = stack.length; + + stack[--pos] = (byte)(tagNo & 0x7F); + + do + { + tagNo >>= 7; + stack[--pos] = (byte)(tagNo & 0x7F | 0x80); + } + while (tagNo > 127); + + write(stack, pos, stack.length - pos); + } + } + } + + void writeEncoded(int flags, int tagNo, byte[] bytes) + throws IOException + { + writeTag(flags, tagNo); + writeLength(bytes.length); + write(bytes); + } + + protected void writeNull() + throws IOException + { + os.write(BERTags.NULL); + os.write(0x00); + } + + public void writeObject( + ASN1Encodable obj) + throws IOException + { + if (obj != null) + { + obj.toASN1Primitive().encode(this); + } + else + { + throw new IOException("null object detected"); + } + } + + void writeImplicitObject(ASN1Primitive obj) + throws IOException + { + if (obj != null) + { + obj.encode(new ImplicitOutputStream(os)); + } + else + { + throw new IOException("null object detected"); + } + } + + public void close() + throws IOException + { + os.close(); + } + + public void flush() + throws IOException + { + os.flush(); + } + + ASN1OutputStream getDERSubStream() + { + return new DEROutputStream(os); + } + + ASN1OutputStream getDLSubStream() + { + return new DLOutputStream(os); + } + + private class ImplicitOutputStream + extends ASN1OutputStream + { + private boolean first = true; + + public ImplicitOutputStream(OutputStream os) + { + super(os); + } + + public void write(int b) + throws IOException + { + if (first) + { + first = false; + } + else + { + super.write(b); + } + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ParsingException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ParsingException.java new file mode 100644 index 000000000..77e5cf21f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1ParsingException.java @@ -0,0 +1,23 @@ +package org.spongycastle.asn1; + +public class ASN1ParsingException + extends IllegalStateException +{ + private Throwable cause; + + public ASN1ParsingException(String message) + { + super(message); + } + + public ASN1ParsingException(String message, Throwable cause) + { + super(message); + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Primitive.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Primitive.java new file mode 100644 index 000000000..230bc059b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Primitive.java @@ -0,0 +1,69 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public abstract class ASN1Primitive + extends ASN1Object +{ + ASN1Primitive() + { + + } + + /** + * Create a base ASN.1 object from a byte stream. + * + * @param data the byte stream to parse. + * @return the base ASN.1 object represented by the byte stream. + * @exception IOException if there is a problem parsing the data. + */ + public static ASN1Primitive fromByteArray(byte[] data) + throws IOException + { + ASN1InputStream aIn = new ASN1InputStream(data); + + try + { + return aIn.readObject(); + } + catch (ClassCastException e) + { + throw new IOException("cannot recognise object in stream"); + } + } + + public final boolean equals(Object o) + { + if (this == o) + { + return true; + } + + return (o instanceof ASN1Encodable) && asn1Equals(((ASN1Encodable)o).toASN1Primitive()); + } + + public ASN1Primitive toASN1Primitive() + { + return this; + } + + ASN1Primitive toDERObject() + { + return this; + } + + ASN1Primitive toDLObject() + { + return this; + } + + public abstract int hashCode(); + + abstract boolean isConstructed(); + + abstract int encodedLength() throws IOException; + + abstract void encode(ASN1OutputStream out) throws IOException; + + abstract boolean asn1Equals(ASN1Primitive o); +} \ No newline at end of file diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Sequence.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Sequence.java new file mode 100644 index 000000000..b2942fb1b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Sequence.java @@ -0,0 +1,323 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +public abstract class ASN1Sequence + extends ASN1Primitive +{ + protected Vector seq = new Vector(); + + /** + * return an ASN1Sequence from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Sequence getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Sequence) + { + return (ASN1Sequence)obj; + } + else if (obj instanceof ASN1SequenceParser) + { + return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive()); + } + else if (obj instanceof byte[]) + { + try + { + return ASN1Sequence.getInstance(fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage()); + } + } + else if (obj instanceof ASN1Encodable) + { + ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive(); + + if (primitive instanceof ASN1Sequence) + { + return (ASN1Sequence)primitive; + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * Return an ASN1 sequence from a tagged object. There is a special + * case here, if an object appears to have been explicitly tagged on + * reading but we were expecting it to be implicitly tagged in the + * normal course of events it indicates that we lost the surrounding + * sequence - so we need to add it back (this will happen if the tagged + * object is a sequence that contains other sequences). If you are + * dealing with implicitly tagged sequences you really should + * be using this method. + * + * @param obj the tagged object. + * @param explicit true if the object is meant to be explicitly tagged, + * false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Sequence getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + if (!obj.isExplicit()) + { + throw new IllegalArgumentException("object implicit - explicit expected."); + } + + return ASN1Sequence.getInstance(obj.getObject().toASN1Primitive()); + } + else + { + // + // constructed object which appears to be explicitly tagged + // when it should be implicit means we have to add the + // surrounding sequence. + // + if (obj.isExplicit()) + { + if (obj instanceof BERTaggedObject) + { + return new BERSequence(obj.getObject()); + } + else + { + return new DLSequence(obj.getObject()); + } + } + else + { + if (obj.getObject() instanceof ASN1Sequence) + { + return (ASN1Sequence)obj.getObject(); + } + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * create an empty sequence + */ + protected ASN1Sequence() + { + } + + /** + * create a sequence containing one object + */ + protected ASN1Sequence( + ASN1Encodable obj) + { + seq.addElement(obj); + } + + /** + * create a sequence containing a vector of objects. + */ + protected ASN1Sequence( + ASN1EncodableVector v) + { + for (int i = 0; i != v.size(); i++) + { + seq.addElement(v.get(i)); + } + } + + /** + * create a sequence containing a vector of objects. + */ + protected ASN1Sequence( + ASN1Encodable[] array) + { + for (int i = 0; i != array.length; i++) + { + seq.addElement(array[i]); + } + } + + public ASN1Encodable[] toArray() + { + ASN1Encodable[] values = new ASN1Encodable[this.size()]; + + for (int i = 0; i != this.size(); i++) + { + values[i] = this.getObjectAt(i); + } + + return values; + } + + public Enumeration getObjects() + { + return seq.elements(); + } + + public ASN1SequenceParser parser() + { + final ASN1Sequence outer = this; + + return new ASN1SequenceParser() + { + private final int max = size(); + + private int index; + + public ASN1Encodable readObject() throws IOException + { + if (index == max) + { + return null; + } + + ASN1Encodable obj = getObjectAt(index++); + if (obj instanceof ASN1Sequence) + { + return ((ASN1Sequence)obj).parser(); + } + if (obj instanceof ASN1Set) + { + return ((ASN1Set)obj).parser(); + } + + return obj; + } + + public ASN1Primitive getLoadedObject() + { + return outer; + } + + public ASN1Primitive toASN1Primitive() + { + return outer; + } + }; + } + + /** + * return the object at the sequence position indicated by index. + * + * @param index the sequence number (starting at zero) of the object + * @return the object at the sequence position indicated by index. + */ + public ASN1Encodable getObjectAt( + int index) + { + return (ASN1Encodable)seq.elementAt(index); + } + + /** + * return the number of objects in this sequence. + * + * @return the number of objects in this sequence. + */ + public int size() + { + return seq.size(); + } + + public int hashCode() + { + Enumeration e = this.getObjects(); + int hashCode = size(); + + while (e.hasMoreElements()) + { + Object o = getNext(e); + hashCode *= 17; + + hashCode ^= o.hashCode(); + } + + return hashCode; + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1Sequence)) + { + return false; + } + + ASN1Sequence other = (ASN1Sequence)o; + + if (this.size() != other.size()) + { + return false; + } + + Enumeration s1 = this.getObjects(); + Enumeration s2 = other.getObjects(); + + while (s1.hasMoreElements()) + { + ASN1Encodable obj1 = getNext(s1); + ASN1Encodable obj2 = getNext(s2); + + ASN1Primitive o1 = obj1.toASN1Primitive(); + ASN1Primitive o2 = obj2.toASN1Primitive(); + + if (o1 == o2 || o1.equals(o2)) + { + continue; + } + + return false; + } + + return true; + } + + private ASN1Encodable getNext(Enumeration e) + { + ASN1Encodable encObj = (ASN1Encodable)e.nextElement(); + + return encObj; + } + + ASN1Primitive toDERObject() + { + ASN1Sequence derSeq = new DERSequence(); + + derSeq.seq = this.seq; + + return derSeq; + } + + ASN1Primitive toDLObject() + { + ASN1Sequence dlSeq = new DLSequence(); + + dlSeq.seq = this.seq; + + return dlSeq; + } + + boolean isConstructed() + { + return true; + } + + abstract void encode(ASN1OutputStream out) + throws IOException; + + public String toString() + { + return seq.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1SequenceParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1SequenceParser.java new file mode 100644 index 000000000..c2904a32b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1SequenceParser.java @@ -0,0 +1,10 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public interface ASN1SequenceParser + extends ASN1Encodable, InMemoryRepresentable +{ + ASN1Encodable readObject() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Set.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Set.java new file mode 100644 index 000000000..5cc1163bc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1Set.java @@ -0,0 +1,460 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +abstract public class ASN1Set + extends ASN1Primitive +{ + private Vector set = new Vector(); + private boolean isSorted = false; + + /** + * return an ASN1Set from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Set getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Set) + { + return (ASN1Set)obj; + } + else if (obj instanceof ASN1SetParser) + { + return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive()); + } + else if (obj instanceof byte[]) + { + try + { + return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage()); + } + } + else if (obj instanceof ASN1Encodable) + { + ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive(); + + if (primitive instanceof ASN1Set) + { + return (ASN1Set)primitive; + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * Return an ASN1 set from a tagged object. There is a special + * case here, if an object appears to have been explicitly tagged on + * reading but we were expecting it to be implicitly tagged in the + * normal course of events it indicates that we lost the surrounding + * set - so we need to add it back (this will happen if the tagged + * object is a sequence that contains other sequences). If you are + * dealing with implicitly tagged sets you really should + * be using this method. + * + * @param obj the tagged object. + * @param explicit true if the object is meant to be explicitly tagged + * false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Set getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + if (!obj.isExplicit()) + { + throw new IllegalArgumentException("object implicit - explicit expected."); + } + + return (ASN1Set)obj.getObject(); + } + else + { + // + // constructed object which appears to be explicitly tagged + // and it's really implicit means we have to add the + // surrounding set. + // + if (obj.isExplicit()) + { + if (obj instanceof BERTaggedObject) + { + return new BERSet(obj.getObject()); + } + else + { + return new DLSet(obj.getObject()); + } + } + else + { + if (obj.getObject() instanceof ASN1Set) + { + return (ASN1Set)obj.getObject(); + } + + // + // in this case the parser returns a sequence, convert it + // into a set. + // + if (obj.getObject() instanceof ASN1Sequence) + { + ASN1Sequence s = (ASN1Sequence)obj.getObject(); + + if (obj instanceof BERTaggedObject) + { + return new BERSet(s.toArray()); + } + else + { + return new DLSet(s.toArray()); + } + } + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + protected ASN1Set() + { + } + + /** + * create a sequence containing one object + */ + protected ASN1Set( + ASN1Encodable obj) + { + set.addElement(obj); + } + + /** + * create a sequence containing a vector of objects. + */ + protected ASN1Set( + ASN1EncodableVector v, + boolean doSort) + { + for (int i = 0; i != v.size(); i++) + { + set.addElement(v.get(i)); + } + + if (doSort) + { + this.sort(); + } + } + + /** + * create a sequence containing a vector of objects. + */ + protected ASN1Set( + ASN1Encodable[] array, + boolean doSort) + { + for (int i = 0; i != array.length; i++) + { + set.addElement(array[i]); + } + + if (doSort) + { + this.sort(); + } + } + + public Enumeration getObjects() + { + return set.elements(); + } + + /** + * return the object at the set position indicated by index. + * + * @param index the set number (starting at zero) of the object + * @return the object at the set position indicated by index. + */ + public ASN1Encodable getObjectAt( + int index) + { + return (ASN1Encodable)set.elementAt(index); + } + + /** + * return the number of objects in this set. + * + * @return the number of objects in this set. + */ + public int size() + { + return set.size(); + } + + public ASN1Encodable[] toArray() + { + ASN1Encodable[] values = new ASN1Encodable[this.size()]; + + for (int i = 0; i != this.size(); i++) + { + values[i] = this.getObjectAt(i); + } + + return values; + } + + public ASN1SetParser parser() + { + final ASN1Set outer = this; + + return new ASN1SetParser() + { + private final int max = size(); + + private int index; + + public ASN1Encodable readObject() throws IOException + { + if (index == max) + { + return null; + } + + ASN1Encodable obj = getObjectAt(index++); + if (obj instanceof ASN1Sequence) + { + return ((ASN1Sequence)obj).parser(); + } + if (obj instanceof ASN1Set) + { + return ((ASN1Set)obj).parser(); + } + + return obj; + } + + public ASN1Primitive getLoadedObject() + { + return outer; + } + + public ASN1Primitive toASN1Primitive() + { + return outer; + } + }; + } + + public int hashCode() + { + Enumeration e = this.getObjects(); + int hashCode = size(); + + while (e.hasMoreElements()) + { + Object o = getNext(e); + hashCode *= 17; + + hashCode ^= o.hashCode(); + } + + return hashCode; + } + + ASN1Primitive toDERObject() + { + if (isSorted) + { + ASN1Set derSet = new DERSet(); + + derSet.set = this.set; + + return derSet; + } + else + { + Vector v = new Vector(); + + for (int i = 0; i != set.size(); i++) + { + v.addElement(set.elementAt(i)); + } + + ASN1Set derSet = new DERSet(); + + derSet.set = v; + + derSet.sort(); + + return derSet; + } + } + + ASN1Primitive toDLObject() + { + ASN1Set derSet = new DLSet(); + + derSet.set = this.set; + + return derSet; + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1Set)) + { + return false; + } + + ASN1Set other = (ASN1Set)o; + + if (this.size() != other.size()) + { + return false; + } + + Enumeration s1 = this.getObjects(); + Enumeration s2 = other.getObjects(); + + while (s1.hasMoreElements()) + { + ASN1Encodable obj1 = getNext(s1); + ASN1Encodable obj2 = getNext(s2); + + ASN1Primitive o1 = obj1.toASN1Primitive(); + ASN1Primitive o2 = obj2.toASN1Primitive(); + + if (o1 == o2 || o1.equals(o2)) + { + continue; + } + + return false; + } + + return true; + } + + private ASN1Encodable getNext(Enumeration e) + { + ASN1Encodable encObj = (ASN1Encodable)e.nextElement(); + + // unfortunately null was allowed as a substitute for DER null + if (encObj == null) + { + return DERNull.INSTANCE; + } + + return encObj; + } + + /** + * return true if a <= b (arrays are assumed padded with zeros). + */ + private boolean lessThanOrEqual( + byte[] a, + byte[] b) + { + int len = Math.min(a.length, b.length); + for (int i = 0; i != len; ++i) + { + if (a[i] != b[i]) + { + return (a[i] & 0xff) < (b[i] & 0xff); + } + } + return len == a.length; + } + + private byte[] getEncoded( + ASN1Encodable obj) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(obj); + } + catch (IOException e) + { + throw new IllegalArgumentException("cannot encode object added to SET"); + } + + return bOut.toByteArray(); + } + + protected void sort() + { + if (!isSorted) + { + isSorted = true; + if (set.size() > 1) + { + boolean swapped = true; + int lastSwap = set.size() - 1; + + while (swapped) + { + int index = 0; + int swapIndex = 0; + byte[] a = getEncoded((ASN1Encodable)set.elementAt(0)); + + swapped = false; + + while (index != lastSwap) + { + byte[] b = getEncoded((ASN1Encodable)set.elementAt(index + 1)); + + if (lessThanOrEqual(a, b)) + { + a = b; + } + else + { + Object o = set.elementAt(index); + + set.setElementAt(set.elementAt(index + 1), index); + set.setElementAt(o, index + 1); + + swapped = true; + swapIndex = index; + } + + index++; + } + + lastSwap = swapIndex; + } + } + } + } + + boolean isConstructed() + { + return true; + } + + abstract void encode(ASN1OutputStream out) + throws IOException; + + public String toString() + { + return set.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1SetParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1SetParser.java new file mode 100644 index 000000000..adbe42069 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1SetParser.java @@ -0,0 +1,10 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public interface ASN1SetParser + extends ASN1Encodable, InMemoryRepresentable +{ + public ASN1Encodable readObject() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1StreamParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1StreamParser.java new file mode 100644 index 000000000..0b7de8f81 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1StreamParser.java @@ -0,0 +1,247 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ASN1StreamParser +{ + private final InputStream _in; + private final int _limit; + private final byte[][] tmpBuffers; + + public ASN1StreamParser( + InputStream in) + { + this(in, StreamUtil.findLimit(in)); + } + + public ASN1StreamParser( + InputStream in, + int limit) + { + this._in = in; + this._limit = limit; + + this.tmpBuffers = new byte[11][]; + } + + public ASN1StreamParser( + byte[] encoding) + { + this(new ByteArrayInputStream(encoding), encoding.length); + } + + ASN1Encodable readIndef(int tagValue) throws IOException + { + // Note: INDEF => CONSTRUCTED + + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagValue) + { + case BERTags.EXTERNAL: + return new DERExternalParser(this); + case BERTags.OCTET_STRING: + return new BEROctetStringParser(this); + case BERTags.SEQUENCE: + return new BERSequenceParser(this); + case BERTags.SET: + return new BERSetParser(this); + default: + throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue)); + } + } + + ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException + { + if (_in instanceof IndefiniteLengthInputStream) + { + if (!constructed) + { + throw new IOException("indefinite length primitive encoding encountered"); + } + + return readIndef(tag); + } + + if (constructed) + { + switch (tag) + { + case BERTags.SET: + return new DERSetParser(this); + case BERTags.SEQUENCE: + return new DERSequenceParser(this); + case BERTags.OCTET_STRING: + return new BEROctetStringParser(this); + } + } + else + { + switch (tag) + { + case BERTags.SET: + throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); + case BERTags.SEQUENCE: + throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); + case BERTags.OCTET_STRING: + return new DEROctetStringParser((DefiniteLengthInputStream)_in); + } + } + + // TODO ASN1Exception + throw new RuntimeException("implicit tagging not implemented"); + } + + ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException + { + if (!constructed) + { + // Note: !CONSTRUCTED => IMPLICIT + DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; + return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray())); + } + + ASN1EncodableVector v = readVector(); + + if (_in instanceof IndefiniteLengthInputStream) + { + return v.size() == 1 + ? new BERTaggedObject(true, tag, v.get(0)) + : new BERTaggedObject(false, tag, BERFactory.createSequence(v)); + } + + return v.size() == 1 + ? new DERTaggedObject(true, tag, v.get(0)) + : new DERTaggedObject(false, tag, DERFactory.createSequence(v)); + } + + public ASN1Encodable readObject() + throws IOException + { + int tag = _in.read(); + if (tag == -1) + { + return null; + } + + // + // turn of looking for "00" while we resolve the tag + // + set00Check(false); + + // + // calculate tag number + // + int tagNo = ASN1InputStream.readTagNumber(_in, tag); + + boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0; + + // + // calculate length + // + int length = ASN1InputStream.readLength(_in, _limit); + + if (length < 0) // indefinite length method + { + if (!isConstructed) + { + throw new IOException("indefinite length primitive encoding encountered"); + } + + IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); + ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); + + if ((tag & BERTags.APPLICATION) != 0) + { + return new BERApplicationSpecificParser(tagNo, sp); + } + + if ((tag & BERTags.TAGGED) != 0) + { + return new BERTaggedObjectParser(true, tagNo, sp); + } + + return sp.readIndef(tagNo); + } + else + { + DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); + + if ((tag & BERTags.APPLICATION) != 0) + { + return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); + } + + if ((tag & BERTags.TAGGED) != 0) + { + return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn)); + } + + if (isConstructed) + { + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case BERTags.OCTET_STRING: + // + // yes, people actually do this... + // + return new BEROctetStringParser(new ASN1StreamParser(defIn)); + case BERTags.SEQUENCE: + return new DERSequenceParser(new ASN1StreamParser(defIn)); + case BERTags.SET: + return new DERSetParser(new ASN1StreamParser(defIn)); + case BERTags.EXTERNAL: + return new DERExternalParser(new ASN1StreamParser(defIn)); + default: + throw new IOException("unknown tag " + tagNo + " encountered"); + } + } + + // Some primitive encodings can be handled by parsers too... + switch (tagNo) + { + case BERTags.OCTET_STRING: + return new DEROctetStringParser(defIn); + } + + try + { + return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers); + } + catch (IllegalArgumentException e) + { + throw new ASN1Exception("corrupted stream detected", e); + } + } + } + + private void set00Check(boolean enabled) + { + if (_in instanceof IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); + } + } + + ASN1EncodableVector readVector() throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + ASN1Encodable obj; + while ((obj = readObject()) != null) + { + if (obj instanceof InMemoryRepresentable) + { + v.add(((InMemoryRepresentable)obj).getLoadedObject()); + } + else + { + v.add(obj.toASN1Primitive()); + } + } + + return v; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1String.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1String.java new file mode 100644 index 000000000..84166d40c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1String.java @@ -0,0 +1,6 @@ +package org.spongycastle.asn1; + +public interface ASN1String +{ + public String getString(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1TaggedObject.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1TaggedObject.java new file mode 100644 index 000000000..af7788162 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1TaggedObject.java @@ -0,0 +1,236 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +/** + * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by + * a [n] where n is some number - these are assumed to follow the construction + * rules (as with sequences). + */ +public abstract class ASN1TaggedObject + extends ASN1Primitive + implements ASN1TaggedObjectParser +{ + int tagNo; + boolean empty = false; + boolean explicit = true; + ASN1Encodable obj = null; + + static public ASN1TaggedObject getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + return (ASN1TaggedObject)obj.getObject(); + } + + throw new IllegalArgumentException("implicitly tagged tagged object"); + } + + static public ASN1TaggedObject getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1TaggedObject) + { + return (ASN1TaggedObject)obj; + } + else if (obj instanceof byte[]) + { + try + { + return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage()); + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * Create a tagged object with the style given by the value of explicit. + *
+ * If the object implements ASN1Choice the tag style will always be changed + * to explicit in accordance with the ASN.1 encoding rules. + *
+ * @param explicit true if the object is explicitly tagged. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public ASN1TaggedObject( + boolean explicit, + int tagNo, + ASN1Encodable obj) + { + if (obj instanceof ASN1Choice) + { + this.explicit = true; + } + else + { + this.explicit = explicit; + } + + this.tagNo = tagNo; + + if (this.explicit) + { + this.obj = obj; + } + else + { + ASN1Primitive prim = obj.toASN1Primitive(); + + if (prim instanceof ASN1Set) + { + ASN1Set s = null; + } + + this.obj = obj; + } + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1TaggedObject)) + { + return false; + } + + ASN1TaggedObject other = (ASN1TaggedObject)o; + + if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit) + { + return false; + } + + if(obj == null) + { + if (other.obj != null) + { + return false; + } + } + else + { + if (!(obj.toASN1Primitive().equals(other.obj.toASN1Primitive()))) + { + return false; + } + } + + return true; + } + + public int hashCode() + { + int code = tagNo; + + // TODO: actually this is wrong - the problem is that a re-encoded + // object may end up with a different hashCode due to implicit + // tagging. As implicit tagging is ambiguous if a sequence is involved + // it seems the only correct method for both equals and hashCode is to + // compare the encodings... + if (obj != null) + { + code ^= obj.hashCode(); + } + + return code; + } + + public int getTagNo() + { + return tagNo; + } + + /** + * return whether or not the object may be explicitly tagged. + *+ * Note: if the object has been read from an input stream, the only + * time you can be sure if isExplicit is returning the true state of + * affairs is if it returns false. An implicitly tagged object may appear + * to be explicitly tagged, so you need to understand the context under + * which the reading was done as well, see getObject below. + */ + public boolean isExplicit() + { + return explicit; + } + + public boolean isEmpty() + { + return empty; + } + + /** + * return whatever was following the tag. + *
+ * Note: tagged objects are generally context dependent if you're
+ * trying to extract a tagged object you should be going via the
+ * appropriate getInstance method.
+ */
+ public ASN1Primitive getObject()
+ {
+ if (obj != null)
+ {
+ return obj.toASN1Primitive();
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the object held in this tagged object as a parser assuming it has
+ * the type of the passed in tag. If the object doesn't have a parser
+ * associated with it, the base object is returned.
+ */
+ public ASN1Encodable getObjectParser(
+ int tag,
+ boolean isExplicit)
+ {
+ switch (tag)
+ {
+ case BERTags.SET:
+ return ASN1Set.getInstance(this, isExplicit).parser();
+ case BERTags.SEQUENCE:
+ return ASN1Sequence.getInstance(this, isExplicit).parser();
+ case BERTags.OCTET_STRING:
+ return ASN1OctetString.getInstance(this, isExplicit).parser();
+ }
+
+ if (isExplicit)
+ {
+ return getObject();
+ }
+
+ throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ {
+ return this.toASN1Primitive();
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ return new DERTaggedObject(explicit, tagNo, obj);
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ return new DLTaggedObject(explicit, tagNo, obj);
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+
+ public String toString()
+ {
+ return "[" + tagNo + "]" + obj;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1TaggedObjectParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1TaggedObjectParser.java
new file mode 100644
index 000000000..2710954e0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1TaggedObjectParser.java
@@ -0,0 +1,12 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1TaggedObjectParser
+ extends ASN1Encodable, InMemoryRepresentable
+{
+ public int getTagNo();
+
+ public ASN1Encodable getObjectParser(int tag, boolean isExplicit)
+ throws IOException;
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1UTCTime.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1UTCTime.java
new file mode 100644
index 000000000..d011b7d92
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ASN1UTCTime.java
@@ -0,0 +1,22 @@
+package org.spongycastle.asn1;
+
+import java.util.Date;
+
+public class ASN1UTCTime
+ extends DERUTCTime
+{
+ ASN1UTCTime(byte[] bytes)
+ {
+ super(bytes);
+ }
+
+ public ASN1UTCTime(Date time)
+ {
+ super(time);
+ }
+
+ public ASN1UTCTime(String time)
+ {
+ super(time);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERApplicationSpecific.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERApplicationSpecific.java
new file mode 100644
index 000000000..743b22003
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERApplicationSpecific.java
@@ -0,0 +1,10 @@
+package org.spongycastle.asn1;
+
+public class BERApplicationSpecific
+ extends DERApplicationSpecific
+{
+ public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+ {
+ super(tagNo, vec);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERApplicationSpecificParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERApplicationSpecificParser.java
new file mode 100644
index 000000000..b77f81d06
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERApplicationSpecificParser.java
@@ -0,0 +1,41 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+public class BERApplicationSpecificParser
+ implements ASN1ApplicationSpecificParser
+{
+ private final int tag;
+ private final ASN1StreamParser parser;
+
+ BERApplicationSpecificParser(int tag, ASN1StreamParser parser)
+ {
+ this.tag = tag;
+ this.parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BERApplicationSpecific(tag, parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERConstructedOctetString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERConstructedOctetString.java
new file mode 100644
index 000000000..cd9289f26
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERConstructedOctetString.java
@@ -0,0 +1,144 @@
+package org.spongycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * @deprecated use BEROctetString
+ */
+public class BERConstructedOctetString
+ extends BEROctetString
+{
+ private static final int MAX_LENGTH = 1000;
+
+ /**
+ * convert a vector of octet strings into a single byte string
+ */
+ static private byte[] toBytes(
+ Vector octs)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != octs.size(); i++)
+ {
+ try
+ {
+ DEROctetString o = (DEROctetString)octs.elementAt(i);
+
+ bOut.write(o.getOctets());
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException(octs.elementAt(i).getClass().getName() + " found in input should only contain DEROctetString");
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("exception converting octets " + e.toString());
+ }
+ }
+
+ return bOut.toByteArray();
+ }
+
+ private Vector octs;
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public BERConstructedOctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public BERConstructedOctetString(
+ Vector octs)
+ {
+ super(toBytes(octs));
+
+ this.octs = octs;
+ }
+
+ public BERConstructedOctetString(
+ ASN1Primitive obj)
+ {
+ super(toByteArray(obj));
+ }
+
+ private static byte[] toByteArray(ASN1Primitive obj)
+ {
+ try
+ {
+ return obj.getEncoded();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Unable to encode object");
+ }
+ }
+
+ public BERConstructedOctetString(
+ ASN1Encodable obj)
+ {
+ this(obj.toASN1Primitive());
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ /**
+ * return the DER octets that make up this string.
+ */
+ public Enumeration getObjects()
+ {
+ if (octs == null)
+ {
+ return generateOcts().elements();
+ }
+
+ return octs.elements();
+ }
+
+ private Vector generateOcts()
+ {
+ Vector vec = new Vector();
+ for (int i = 0; i < string.length; i += MAX_LENGTH)
+ {
+ int end;
+
+ if (i + MAX_LENGTH > string.length)
+ {
+ end = string.length;
+ }
+ else
+ {
+ end = i + MAX_LENGTH;
+ }
+
+ byte[] nStr = new byte[end - i];
+
+ System.arraycopy(string, i, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+ }
+
+ return vec;
+ }
+
+ public static BEROctetString fromSequence(ASN1Sequence seq)
+ {
+ Vector v = new Vector();
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ v.addElement(e.nextElement());
+ }
+
+ return new BERConstructedOctetString(v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERFactory.java
new file mode 100644
index 000000000..a444eac6e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERFactory.java
@@ -0,0 +1,17 @@
+package org.spongycastle.asn1;
+
+class BERFactory
+{
+ static final BERSequence EMPTY_SEQUENCE = new BERSequence();
+ static final BERSet EMPTY_SET = new BERSet();
+
+ static BERSequence createSequence(ASN1EncodableVector v)
+ {
+ return v.size() < 1 ? EMPTY_SEQUENCE : new BERSequence(v);
+ }
+
+ static BERSet createSet(ASN1EncodableVector v)
+ {
+ return v.size() < 1 ? EMPTY_SET : new BERSet(v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERGenerator.java
new file mode 100644
index 000000000..0cca35c37
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERGenerator.java
@@ -0,0 +1,100 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BERGenerator
+ extends ASN1Generator
+{
+ private boolean _tagged = false;
+ private boolean _isExplicit;
+ private int _tagNo;
+
+ protected BERGenerator(
+ OutputStream out)
+ {
+ super(out);
+ }
+
+ public BERGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ {
+ super(out);
+
+ _tagged = true;
+ _isExplicit = isExplicit;
+ _tagNo = tagNo;
+ }
+
+ public OutputStream getRawOutputStream()
+ {
+ return _out;
+ }
+
+ private void writeHdr(
+ int tag)
+ throws IOException
+ {
+ _out.write(tag);
+ _out.write(0x80);
+ }
+
+ protected void writeBERHeader(
+ int tag)
+ throws IOException
+ {
+ if (_tagged)
+ {
+ int tagNum = _tagNo | BERTags.TAGGED;
+
+ if (_isExplicit)
+ {
+ writeHdr(tagNum | BERTags.CONSTRUCTED);
+ writeHdr(tag);
+ }
+ else
+ {
+ if ((tag & BERTags.CONSTRUCTED) != 0)
+ {
+ writeHdr(tagNum | BERTags.CONSTRUCTED);
+ }
+ else
+ {
+ writeHdr(tagNum);
+ }
+ }
+ }
+ else
+ {
+ writeHdr(tag);
+ }
+ }
+
+ protected void writeBERBody(
+ InputStream contentStream)
+ throws IOException
+ {
+ int ch;
+
+ while ((ch = contentStream.read()) >= 0)
+ {
+ _out.write(ch);
+ }
+ }
+
+ protected void writeBEREnd()
+ throws IOException
+ {
+ _out.write(0x00);
+ _out.write(0x00);
+
+ if (_tagged && _isExplicit) // write extra end for tag header
+ {
+ _out.write(0x00);
+ _out.write(0x00);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetString.java
new file mode 100644
index 000000000..b32f5c535
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetString.java
@@ -0,0 +1,168 @@
+package org.spongycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public class BEROctetString
+ extends ASN1OctetString
+{
+ private static final int MAX_LENGTH = 1000;
+
+ private ASN1OctetString[] octs;
+
+ /**
+ * convert a vector of octet strings into a single byte string
+ */
+ static private byte[] toBytes(
+ ASN1OctetString[] octs)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != octs.length; i++)
+ {
+ try
+ {
+ DEROctetString o = (DEROctetString)octs[i];
+
+ bOut.write(o.getOctets());
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException(octs[i].getClass().getName() + " found in input should only contain DEROctetString");
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("exception converting octets " + e.toString());
+ }
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public BEROctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public BEROctetString(
+ ASN1OctetString[] octs)
+ {
+ super(toBytes(octs));
+
+ this.octs = octs;
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ /**
+ * return the DER octets that make up this string.
+ */
+ public Enumeration getObjects()
+ {
+ if (octs == null)
+ {
+ return generateOcts().elements();
+ }
+
+ return new Enumeration()
+ {
+ int counter = 0;
+
+ public boolean hasMoreElements()
+ {
+ return counter < octs.length;
+ }
+
+ public Object nextElement()
+ {
+ return octs[counter++];
+ }
+ };
+ }
+
+ private Vector generateOcts()
+ {
+ Vector vec = new Vector();
+ for (int i = 0; i < string.length; i += MAX_LENGTH)
+ {
+ int end;
+
+ if (i + MAX_LENGTH > string.length)
+ {
+ end = string.length;
+ }
+ else
+ {
+ end = i + MAX_LENGTH;
+ }
+
+ byte[] nStr = new byte[end - i];
+
+ System.arraycopy(string, i, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+ }
+
+ return vec;
+ }
+
+ boolean isConstructed()
+ {
+ return true;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = 0;
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+ }
+
+ return 2 + length + 2;
+ }
+
+ public void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+
+ out.write(0x80);
+
+ //
+ // write out the octet array
+ //
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+
+ static BEROctetString fromSequence(ASN1Sequence seq)
+ {
+ ASN1OctetString[] v = new ASN1OctetString[seq.size()];
+ Enumeration e = seq.getObjects();
+ int index = 0;
+
+ while (e.hasMoreElements())
+ {
+ v[index++] = (ASN1OctetString)e.nextElement();
+ }
+
+ return new BEROctetString(v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetStringGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetStringGenerator.java
new file mode 100644
index 000000000..47d1a0ebf
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetStringGenerator.java
@@ -0,0 +1,102 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BEROctetStringGenerator
+ extends BERGenerator
+{
+ public BEROctetStringGenerator(OutputStream out)
+ throws IOException
+ {
+ super(out);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+ }
+
+ public BEROctetStringGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ throws IOException
+ {
+ super(out, tagNo, isExplicit);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+ }
+
+ public OutputStream getOctetOutputStream()
+ {
+ return getOctetOutputStream(new byte[1000]); // limit for CER encoding.
+ }
+
+ public OutputStream getOctetOutputStream(
+ byte[] buf)
+ {
+ return new BufferedBEROctetStream(buf);
+ }
+
+ private class BufferedBEROctetStream
+ extends OutputStream
+ {
+ private byte[] _buf;
+ private int _off;
+ private DEROutputStream _derOut;
+
+ BufferedBEROctetStream(
+ byte[] buf)
+ {
+ _buf = buf;
+ _off = 0;
+ _derOut = new DEROutputStream(_out);
+ }
+
+ public void write(
+ int b)
+ throws IOException
+ {
+ _buf[_off++] = (byte)b;
+
+ if (_off == _buf.length)
+ {
+ DEROctetString.encode(_derOut, _buf);
+ _off = 0;
+ }
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int numToCopy = Math.min(len, _buf.length - _off);
+ System.arraycopy(b, off, _buf, _off, numToCopy);
+
+ _off += numToCopy;
+ if (_off < _buf.length)
+ {
+ break;
+ }
+
+ DEROctetString.encode(_derOut, _buf);
+ _off = 0;
+
+ off += numToCopy;
+ len -= numToCopy;
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ if (_off != 0)
+ {
+ byte[] bytes = new byte[_off];
+ System.arraycopy(_buf, 0, bytes, 0, _off);
+
+ DEROctetString.encode(_derOut, bytes);
+ }
+
+ writeBEREnd();
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetStringParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetStringParser.java
new file mode 100644
index 000000000..6cf20fcfe
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROctetStringParser.java
@@ -0,0 +1,41 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.util.io.Streams;
+
+public class BEROctetStringParser
+ implements ASN1OctetStringParser
+{
+ private ASN1StreamParser _parser;
+
+ BEROctetStringParser(
+ ASN1StreamParser parser)
+ {
+ _parser = parser;
+ }
+
+ public InputStream getOctetStream()
+ {
+ return new ConstructedOctetStream(_parser);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BEROctetString(Streams.readAll(getOctetStream()));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROutputStream.java
new file mode 100644
index 000000000..13b20d523
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BEROutputStream.java
@@ -0,0 +1,36 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BEROutputStream
+ extends DEROutputStream
+{
+ public BEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof ASN1Primitive)
+ {
+ ((ASN1Primitive)obj).encode(this);
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ((ASN1Encodable)obj).toASN1Primitive().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not BEREncodable");
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequence.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequence.java
new file mode 100644
index 000000000..d034af1a0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequence.java
@@ -0,0 +1,73 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSequence
+ extends ASN1Sequence
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public BERSequence(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public BERSequence(
+ ASN1EncodableVector v)
+ {
+ super(v);
+ }
+
+ /**
+ * create a sequence containing an array of objects.
+ */
+ public BERSequence(
+ ASN1Encodable[] array)
+ {
+ super(array);
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = 0;
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+ }
+
+ return 2 + length + 2;
+ }
+
+ /*
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequenceGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequenceGenerator.java
new file mode 100644
index 000000000..5d759ddde
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequenceGenerator.java
@@ -0,0 +1,41 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BERSequenceGenerator
+ extends BERGenerator
+{
+ public BERSequenceGenerator(
+ OutputStream out)
+ throws IOException
+ {
+ super(out);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.SEQUENCE);
+ }
+
+ public BERSequenceGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ throws IOException
+ {
+ super(out, tagNo, isExplicit);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.SEQUENCE);
+ }
+
+ public void addObject(
+ ASN1Encodable object)
+ throws IOException
+ {
+ object.toASN1Primitive().encode(new BEROutputStream(_out));
+ }
+
+ public void close()
+ throws IOException
+ {
+ writeBEREnd();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequenceParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequenceParser.java
new file mode 100644
index 000000000..3bfadff51
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSequenceParser.java
@@ -0,0 +1,38 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+public class BERSequenceParser
+ implements ASN1SequenceParser
+{
+ private ASN1StreamParser _parser;
+
+ BERSequenceParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BERSequence(_parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSet.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSet.java
new file mode 100644
index 000000000..ed0c3f521
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSet.java
@@ -0,0 +1,73 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSet
+ extends ASN1Set
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public BERSet(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public BERSet(
+ ASN1EncodableVector v)
+ {
+ super(v, false);
+ }
+
+ /**
+ * create a set from an array of objects.
+ */
+ public BERSet(
+ ASN1Encodable[] a)
+ {
+ super(a, false);
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = 0;
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+ }
+
+ return 2 + length + 2;
+ }
+
+ /*
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.SET | BERTags.CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSetParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSetParser.java
new file mode 100644
index 000000000..0b649f29b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERSetParser.java
@@ -0,0 +1,38 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+public class BERSetParser
+ implements ASN1SetParser
+{
+ private ASN1StreamParser _parser;
+
+ BERSetParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BERSet(_parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTaggedObject.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTaggedObject.java
new file mode 100644
index 000000000..bd55e5970
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTaggedObject.java
@@ -0,0 +1,147 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * BER TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
+ * rules (as with sequences).
+ */
+public class BERTaggedObject
+ extends ASN1TaggedObject
+{
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ super(true, tagNo, obj);
+ }
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ /**
+ * create an implicitly tagged object that contains a zero
+ * length sequence.
+ */
+ public BERTaggedObject(
+ int tagNo)
+ {
+ super(false, tagNo, new BERSequence());
+ }
+
+ boolean isConstructed()
+ {
+ if (!empty)
+ {
+ if (explicit)
+ {
+ return true;
+ }
+ else
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+
+ return primitive.isConstructed();
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ if (!empty)
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive();
+ int length = primitive.encodedLength();
+
+ if (explicit)
+ {
+ return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+ }
+ else
+ {
+ // header length already in calculation
+ length = length - 1;
+
+ return StreamUtil.calculateTagLength(tagNo) + length;
+ }
+ }
+ else
+ {
+ return StreamUtil.calculateTagLength(tagNo) + 1;
+ }
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
+ out.write(0x80);
+
+ if (!empty)
+ {
+ if (!explicit)
+ {
+ Enumeration e;
+ if (obj instanceof ASN1OctetString)
+ {
+ if (obj instanceof BEROctetString)
+ {
+ e = ((BEROctetString)obj).getObjects();
+ }
+ else
+ {
+ ASN1OctetString octs = (ASN1OctetString)obj;
+ BEROctetString berO = new BEROctetString(octs.getOctets());
+ e = berO.getObjects();
+ }
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ e = ((ASN1Sequence)obj).getObjects();
+ }
+ else if (obj instanceof ASN1Set)
+ {
+ e = ((ASN1Set)obj).getObjects();
+ }
+ else
+ {
+ throw new RuntimeException("not implemented: " + obj.getClass().getName());
+ }
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+ }
+ else
+ {
+ out.writeObject(obj);
+ }
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTaggedObjectParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTaggedObjectParser.java
new file mode 100644
index 000000000..cb7f13ffa
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTaggedObjectParser.java
@@ -0,0 +1,66 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+public class BERTaggedObjectParser
+ implements ASN1TaggedObjectParser
+{
+ private boolean _constructed;
+ private int _tagNumber;
+ private ASN1StreamParser _parser;
+
+ BERTaggedObjectParser(
+ boolean constructed,
+ int tagNumber,
+ ASN1StreamParser parser)
+ {
+ _constructed = constructed;
+ _tagNumber = tagNumber;
+ _parser = parser;
+ }
+
+ public boolean isConstructed()
+ {
+ return _constructed;
+ }
+
+ public int getTagNo()
+ {
+ return _tagNumber;
+ }
+
+ public ASN1Encodable getObjectParser(
+ int tag,
+ boolean isExplicit)
+ throws IOException
+ {
+ if (isExplicit)
+ {
+ if (!_constructed)
+ {
+ throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+ }
+ return _parser.readObject();
+ }
+
+ return _parser.readImplicit(_constructed, tag);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return _parser.readTaggedObject(_constructed, _tagNumber);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return this.getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage());
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTags.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTags.java
new file mode 100644
index 000000000..4b25e7162
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/BERTags.java
@@ -0,0 +1,36 @@
+package org.spongycastle.asn1;
+
+public interface BERTags
+{
+ public static final int BOOLEAN = 0x01;
+ public static final int INTEGER = 0x02;
+ public static final int BIT_STRING = 0x03;
+ public static final int OCTET_STRING = 0x04;
+ public static final int NULL = 0x05;
+ public static final int OBJECT_IDENTIFIER = 0x06;
+ public static final int EXTERNAL = 0x08;
+ public static final int ENUMERATED = 0x0a;
+ public static final int SEQUENCE = 0x10;
+ public static final int SEQUENCE_OF = 0x10; // for completeness - used to model a SEQUENCE of the same type.
+ public static final int SET = 0x11;
+ public static final int SET_OF = 0x11; // for completeness - used to model a SET of the same type.
+
+
+ public static final int NUMERIC_STRING = 0x12;
+ public static final int PRINTABLE_STRING = 0x13;
+ public static final int T61_STRING = 0x14;
+ public static final int VIDEOTEX_STRING = 0x15;
+ public static final int IA5_STRING = 0x16;
+ public static final int UTC_TIME = 0x17;
+ public static final int GENERALIZED_TIME = 0x18;
+ public static final int GRAPHIC_STRING = 0x19;
+ public static final int VISIBLE_STRING = 0x1a;
+ public static final int GENERAL_STRING = 0x1b;
+ public static final int UNIVERSAL_STRING = 0x1c;
+ public static final int BMP_STRING = 0x1e;
+ public static final int UTF8_STRING = 0x0c;
+
+ public static final int CONSTRUCTED = 0x20;
+ public static final int APPLICATION = 0x40;
+ public static final int TAGGED = 0x80;
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ConstructedOctetStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ConstructedOctetStream.java
new file mode 100644
index 000000000..21c3ac361
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ConstructedOctetStream.java
@@ -0,0 +1,111 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class ConstructedOctetStream
+ extends InputStream
+{
+ private final ASN1StreamParser _parser;
+
+ private boolean _first = true;
+ private InputStream _currentStream;
+
+ ConstructedOctetStream(
+ ASN1StreamParser parser)
+ {
+ _parser = parser;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (_currentStream == null)
+ {
+ if (!_first)
+ {
+ return -1;
+ }
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+ if (s == null)
+ {
+ return -1;
+ }
+
+ _first = false;
+ _currentStream = s.getOctetStream();
+ }
+
+ int totalRead = 0;
+
+ for (;;)
+ {
+ int numRead = _currentStream.read(b, off + totalRead, len - totalRead);
+
+ if (numRead >= 0)
+ {
+ totalRead += numRead;
+
+ if (totalRead == len)
+ {
+ return totalRead;
+ }
+ }
+ else
+ {
+ ASN1OctetStringParser aos = (ASN1OctetStringParser)_parser.readObject();
+
+ if (aos == null)
+ {
+ _currentStream = null;
+ return totalRead < 1 ? -1 : totalRead;
+ }
+
+ _currentStream = aos.getOctetStream();
+ }
+ }
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (_currentStream == null)
+ {
+ if (!_first)
+ {
+ return -1;
+ }
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+ if (s == null)
+ {
+ return -1;
+ }
+
+ _first = false;
+ _currentStream = s.getOctetStream();
+ }
+
+ for (;;)
+ {
+ int b = _currentStream.read();
+
+ if (b >= 0)
+ {
+ return b;
+ }
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+ if (s == null)
+ {
+ _currentStream = null;
+ return -1;
+ }
+
+ _currentStream = s.getOctetStream();
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERApplicationSpecific.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERApplicationSpecific.java
new file mode 100644
index 000000000..63f99a34c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERApplicationSpecific.java
@@ -0,0 +1,276 @@
+package org.spongycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * Base class for an application specific object
+ */
+public class DERApplicationSpecific
+ extends ASN1Primitive
+{
+ private final boolean isConstructed;
+ private final int tag;
+ private final byte[] octets;
+
+ DERApplicationSpecific(
+ boolean isConstructed,
+ int tag,
+ byte[] octets)
+ {
+ this.isConstructed = isConstructed;
+ this.tag = tag;
+ this.octets = octets;
+ }
+
+ public DERApplicationSpecific(
+ int tag,
+ byte[] octets)
+ {
+ this(false, tag, octets);
+ }
+
+ public DERApplicationSpecific(
+ int tag,
+ ASN1Encodable object)
+ throws IOException
+ {
+ this(true, tag, object);
+ }
+
+ public DERApplicationSpecific(
+ boolean explicit,
+ int tag,
+ ASN1Encodable object)
+ throws IOException
+ {
+ ASN1Primitive primitive = object.toASN1Primitive();
+
+ byte[] data = primitive.getEncoded(ASN1Encoding.DER);
+
+ this.isConstructed = explicit || (primitive instanceof ASN1Set || primitive instanceof ASN1Sequence);
+ this.tag = tag;
+
+ if (explicit)
+ {
+ this.octets = data;
+ }
+ else
+ {
+ int lenBytes = getLengthOfHeader(data);
+ byte[] tmp = new byte[data.length - lenBytes];
+ System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
+ this.octets = tmp;
+ }
+ }
+
+ public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+ {
+ this.tag = tagNo;
+ this.isConstructed = true;
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != vec.size(); i++)
+ {
+ try
+ {
+ bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("malformed object: " + e, e);
+ }
+ }
+ this.octets = bOut.toByteArray();
+ }
+
+ public static DERApplicationSpecific getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DERApplicationSpecific)
+ {
+ return (DERApplicationSpecific)obj;
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return DERApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct object from byte[]: " + e.getMessage());
+ }
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (primitive instanceof ASN1Sequence)
+ {
+ return (DERApplicationSpecific)primitive;
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ private int getLengthOfHeader(byte[] data)
+ {
+ int length = data[1] & 0xff; // TODO: assumes 1 byte tag
+
+ if (length == 0x80)
+ {
+ return 2; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
+ if (size > 4)
+ {
+ throw new IllegalStateException("DER length more than 4 bytes: " + size);
+ }
+
+ return size + 2;
+ }
+
+ return 2;
+ }
+
+ public boolean isConstructed()
+ {
+ return isConstructed;
+ }
+
+ public byte[] getContents()
+ {
+ return octets;
+ }
+
+ public int getApplicationTag()
+ {
+ return tag;
+ }
+
+ /**
+ * Return the enclosed object assuming explicit tagging.
+ *
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public ASN1Primitive getObject()
+ throws IOException
+ {
+ return new ASN1InputStream(getContents()).readObject();
+ }
+
+ /**
+ * Return the enclosed object assuming implicit tagging.
+ *
+ * @param derTagNo the type tag that should be applied to the object's contents.
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public ASN1Primitive getObject(int derTagNo)
+ throws IOException
+ {
+ if (derTagNo >= 0x1f)
+ {
+ throw new IOException("unsupported tag number");
+ }
+
+ byte[] orig = this.getEncoded();
+ byte[] tmp = replaceTagNumber(derTagNo, orig);
+
+ if ((orig[0] & BERTags.CONSTRUCTED) != 0)
+ {
+ tmp[0] |= BERTags.CONSTRUCTED;
+ }
+
+ return new ASN1InputStream(tmp).readObject();
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.asn1.ASN1Primitive#encode(org.spongycastle.asn1.DEROutputStream)
+ */
+ void encode(ASN1OutputStream out) throws IOException
+ {
+ int classBits = BERTags.APPLICATION;
+ if (isConstructed)
+ {
+ classBits |= BERTags.CONSTRUCTED;
+ }
+
+ out.writeEncoded(classBits, tag, octets);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERApplicationSpecific))
+ {
+ return false;
+ }
+
+ DERApplicationSpecific other = (DERApplicationSpecific)o;
+
+ return isConstructed == other.isConstructed
+ && tag == other.tag
+ && Arrays.areEqual(octets, other.octets);
+ }
+
+ public int hashCode()
+ {
+ return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
+ }
+
+ private byte[] replaceTagNumber(int newTag, byte[] input)
+ throws IOException
+ {
+ int tagNo = input[0] & 0x1f;
+ int index = 1;
+ //
+ // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+ //
+ if (tagNo == 0x1f)
+ {
+ tagNo = 0;
+
+ int b = input[index++] & 0xff;
+
+ // X.690-0207 8.1.2.4.2
+ // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+ if ((b & 0x7f) == 0) // Note: -1 will pass
+ {
+ throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
+ }
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = input[index++] & 0xff;
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+
+ byte[] tmp = new byte[input.length - index + 1];
+
+ System.arraycopy(input, index, tmp, 1, tmp.length - 1);
+
+ tmp[0] = (byte)newTag;
+
+ return tmp;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBMPString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBMPString.java
new file mode 100644
index 000000000..58dbb36a5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBMPString.java
@@ -0,0 +1,153 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * DER BMPString object.
+ */
+public class DERBMPString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private char[] string;
+
+ /**
+ * return a BMP String from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBMPString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBMPString)
+ {
+ return (DERBMPString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERBMPString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a BMP String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBMPString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERBMPString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERBMPString(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERBMPString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length / 2];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+ }
+
+ this.string = cs;
+ }
+
+ DERBMPString(char[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERBMPString(
+ String string)
+ {
+ this.string = string.toCharArray();
+ }
+
+ public String getString()
+ {
+ return new String(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERBMPString))
+ {
+ return false;
+ }
+
+ DERBMPString s = (DERBMPString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length * 2) + (string.length * 2);
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.BMP_STRING);
+ out.writeLength(string.length * 2);
+
+ for (int i = 0; i != string.length; i++)
+ {
+ char c = string[i];
+
+ out.write((byte)(c >> 8));
+ out.write((byte)c);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBitString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBitString.java
new file mode 100644
index 000000000..6fbc2db95
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBitString.java
@@ -0,0 +1,313 @@
+package org.spongycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.io.Streams;
+
+public class DERBitString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ protected byte[] data;
+ protected int padBits;
+
+ /**
+ * return the correct number of pad bits for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected int getPadBits(
+ int bitString)
+ {
+ int val = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ //
+ // this may look a little odd, but if it isn't done like this pre jdk1.2
+ // JVM's break!
+ //
+ if (i != 0)
+ {
+ if ((bitString >> (i * 8)) != 0)
+ {
+ val = (bitString >> (i * 8)) & 0xFF;
+ break;
+ }
+ }
+ else
+ {
+ if (bitString != 0)
+ {
+ val = bitString & 0xFF;
+ break;
+ }
+ }
+ }
+
+ if (val == 0)
+ {
+ return 7;
+ }
+
+
+ int bits = 1;
+
+ while (((val <<= 1) & 0xFF) != 0)
+ {
+ bits++;
+ }
+
+ return 8 - bits;
+ }
+
+ /**
+ * return the correct number of bytes for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected byte[] getBytes(int bitString)
+ {
+ int bytes = 4;
+ for (int i = 3; i >= 1; i--)
+ {
+ if ((bitString & (0xFF << (i * 8))) != 0)
+ {
+ break;
+ }
+ bytes--;
+ }
+
+ byte[] result = new byte[bytes];
+ for (int i = 0; i < bytes; i++)
+ {
+ result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * return a Bit String from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBitString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBitString)
+ {
+ return (DERBitString)obj;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Bit String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBitString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERBitString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ protected DERBitString(
+ byte data,
+ int padBits)
+ {
+ this.data = new byte[1];
+ this.data[0] = data;
+ this.padBits = padBits;
+ }
+
+ /**
+ * @param data the octets making up the bit string.
+ * @param padBits the number of extra bits at the end of the string.
+ */
+ public DERBitString(
+ byte[] data,
+ int padBits)
+ {
+ this.data = data;
+ this.padBits = padBits;
+ }
+
+ public DERBitString(
+ byte[] data)
+ {
+ this(data, 0);
+ }
+
+ public DERBitString(
+ int value)
+ {
+ this.data = getBytes(value);
+ this.padBits = getPadBits(value);
+ }
+
+ public DERBitString(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ this.padBits = 0;
+ }
+
+ public byte[] getBytes()
+ {
+ return data;
+ }
+
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+
+ /**
+ * @return the value of the bit string as an int (truncating if necessary)
+ */
+ public int intValue()
+ {
+ int value = 0;
+
+ for (int i = 0; i != data.length && i != 4; i++)
+ {
+ value |= (data[i] & 0xff) << (8 * i);
+ }
+
+ return value;
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ byte[] bytes = new byte[getBytes().length + 1];
+
+ bytes[0] = (byte)getPadBits();
+ System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
+
+ out.writeEncoded(BERTags.BIT_STRING, bytes);
+ }
+
+ public int hashCode()
+ {
+ return padBits ^ Arrays.hashCode(data);
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERBitString))
+ {
+ return false;
+ }
+
+ DERBitString other = (DERBitString)o;
+
+ return this.padBits == other.padBits
+ && Arrays.areEqual(this.data, other.data);
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) & 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ static DERBitString fromOctetString(byte[] bytes)
+ {
+ if (bytes.length < 1)
+ {
+ throw new IllegalArgumentException("truncated BIT STRING detected");
+ }
+
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ if (data.length != 0)
+ {
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+ }
+
+ return new DERBitString(data, padBits);
+ }
+
+ static DERBitString fromInputStream(int length, InputStream stream)
+ throws IOException
+ {
+ if (length < 1)
+ {
+ throw new IllegalArgumentException("truncated BIT STRING detected");
+ }
+
+ int padBits = stream.read();
+ byte[] data = new byte[length - 1];
+
+ if (data.length != 0)
+ {
+ if (Streams.readFully(stream, data) != data.length)
+ {
+ throw new EOFException("EOF encountered in middle of BIT STRING");
+ }
+ }
+
+ return new DERBitString(data, padBits);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBoolean.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBoolean.java
new file mode 100644
index 000000000..c176bfb73
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERBoolean.java
@@ -0,0 +1,179 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+
+import org.spongycastle.util.Arrays;
+
+public class DERBoolean
+ extends ASN1Primitive
+{
+ private static final byte[] TRUE_VALUE = new byte[] { (byte)0xff };
+ private static final byte[] FALSE_VALUE = new byte[] { 0 };
+
+ private byte[] value;
+
+ public static final ASN1Boolean FALSE = new ASN1Boolean(false);
+ public static final ASN1Boolean TRUE = new ASN1Boolean(true);
+
+
+ /**
+ * return a boolean from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Boolean getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Boolean)
+ {
+ return (ASN1Boolean)obj;
+ }
+
+ if (obj instanceof DERBoolean)
+ {
+ return ((DERBoolean)obj).isTrue() ? DERBoolean.TRUE : DERBoolean.FALSE;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a ASN1Boolean from the passed in boolean.
+ */
+ public static ASN1Boolean getInstance(
+ boolean value)
+ {
+ return (value ? TRUE : FALSE);
+ }
+
+ /**
+ * return a ASN1Boolean from the passed in boolean.
+ */
+ public static ASN1Boolean getInstance(
+ int value)
+ {
+ return (value != 0 ? TRUE : FALSE);
+ }
+
+ /**
+ * return a Boolean from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static ASN1Boolean getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERBoolean)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return ASN1Boolean.fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ DERBoolean(
+ byte[] value)
+ {
+ if (value.length != 1)
+ {
+ throw new IllegalArgumentException("byte value should have 1 byte in it");
+ }
+
+ if (value[0] == 0)
+ {
+ this.value = FALSE_VALUE;
+ }
+ else if (value[0] == 0xff)
+ {
+ this.value = TRUE_VALUE;
+ }
+ else
+ {
+ this.value = Arrays.clone(value);
+ }
+ }
+
+ /**
+ * @deprecated use getInstance(boolean) method.
+ * @param value
+ */
+ public DERBoolean(
+ boolean value)
+ {
+ this.value = (value) ? TRUE_VALUE : FALSE_VALUE;
+ }
+
+ public boolean isTrue()
+ {
+ return (value[0] != 0);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 3;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.BOOLEAN, value);
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if ((o == null) || !(o instanceof DERBoolean))
+ {
+ return false;
+ }
+
+ return (value[0] == ((DERBoolean)o).value[0]);
+ }
+
+ public int hashCode()
+ {
+ return value[0];
+ }
+
+
+ public String toString()
+ {
+ return (value[0] != 0) ? "TRUE" : "FALSE";
+ }
+
+ static ASN1Boolean fromOctetString(byte[] value)
+ {
+ if (value.length != 1)
+ {
+ throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
+ }
+
+ if (value[0] == 0)
+ {
+ return FALSE;
+ }
+ else if (value[0] == 0xff)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return new ASN1Boolean(value);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEREncodableVector.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEREncodableVector.java
new file mode 100644
index 000000000..f381a6c12
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEREncodableVector.java
@@ -0,0 +1,18 @@
+package org.spongycastle.asn1;
+
+/**
+ * a general class for building up a vector of DER encodable objects -
+ * this will eventually be superceded by ASN1EncodableVector so you should
+ * use that class in preference.
+ */
+public class DEREncodableVector
+ extends ASN1EncodableVector
+{
+ /**
+ * @deprecated use ASN1EncodableVector instead.
+ */
+ public DEREncodableVector()
+ {
+
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEREnumerated.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEREnumerated.java
new file mode 100644
index 000000000..7503c8992
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEREnumerated.java
@@ -0,0 +1,170 @@
+package org.spongycastle.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * Use ASN1Enumerated instead of this.
+ */
+public class DEREnumerated
+ extends ASN1Primitive
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Enumerated getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Enumerated)
+ {
+ return (ASN1Enumerated)obj;
+ }
+
+ if (obj instanceof DEREnumerated)
+ {
+ return new ASN1Enumerated(((DEREnumerated)obj).getValue());
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (ASN1Enumerated)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Enumerated from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static ASN1Enumerated getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DEREnumerated)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ /**
+ * @deprecated use ASN1Enumerated
+ */
+ public DEREnumerated(
+ int value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ /**
+ * @deprecated use ASN1Enumerated
+ */
+ public DEREnumerated(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ /**
+ * @deprecated use ASN1Enumerated
+ */
+ public DEREnumerated(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.ENUMERATED, bytes);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DEREnumerated))
+ {
+ return false;
+ }
+
+ DEREnumerated other = (DEREnumerated)o;
+
+ return Arrays.areEqual(this.bytes, other.bytes);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(bytes);
+ }
+
+ private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
+
+ static ASN1Enumerated fromOctetString(byte[] enc)
+ {
+ if (enc.length > 1)
+ {
+ return new ASN1Enumerated(Arrays.clone(enc));
+ }
+
+ if (enc.length == 0)
+ {
+ throw new IllegalArgumentException("ENUMERATED has zero length");
+ }
+ int value = enc[0] & 0xff;
+
+ if (value >= cache.length)
+ {
+ return new ASN1Enumerated(Arrays.clone(enc));
+ }
+
+ ASN1Enumerated possibleMatch = cache[value];
+
+ if (possibleMatch == null)
+ {
+ possibleMatch = cache[value] = new ASN1Enumerated(Arrays.clone(enc));
+ }
+
+ return possibleMatch;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERExternal.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERExternal.java
new file mode 100644
index 000000000..5cf48f0ff
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERExternal.java
@@ -0,0 +1,294 @@
+package org.spongycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Class representing the DER-type External
+ */
+public class DERExternal
+ extends ASN1Primitive
+{
+ private ASN1ObjectIdentifier directReference;
+ private ASN1Integer indirectReference;
+ private ASN1Primitive dataValueDescriptor;
+ private int encoding;
+ private ASN1Primitive externalContent;
+
+ public DERExternal(ASN1EncodableVector vector)
+ {
+ int offset = 0;
+
+ ASN1Primitive enc = getObjFromVector(vector, offset);
+ if (enc instanceof ASN1ObjectIdentifier)
+ {
+ directReference = (ASN1ObjectIdentifier)enc;
+ offset++;
+ enc = getObjFromVector(vector, offset);
+ }
+ if (enc instanceof ASN1Integer)
+ {
+ indirectReference = (ASN1Integer) enc;
+ offset++;
+ enc = getObjFromVector(vector, offset);
+ }
+ if (!(enc instanceof DERTaggedObject))
+ {
+ dataValueDescriptor = (ASN1Primitive) enc;
+ offset++;
+ enc = getObjFromVector(vector, offset);
+ }
+
+ if (vector.size() != offset + 1)
+ {
+ throw new IllegalArgumentException("input vector too large");
+ }
+
+ if (!(enc instanceof DERTaggedObject))
+ {
+ throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+ }
+ DERTaggedObject obj = (DERTaggedObject)enc;
+ setEncoding(obj.getTagNo());
+ externalContent = obj.getObject();
+ }
+
+ private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
+ {
+ if (v.size() <= index)
+ {
+ throw new IllegalArgumentException("too few objects in input vector");
+ }
+
+ return v.get(index).toASN1Primitive();
+ }
+ /**
+ * Creates a new instance of DERExternal
+ * See X.690 for more informations about the meaning of these parameters
+ * @param directReference The direct reference or null
if not set.
+ * @param indirectReference The indirect reference or null
if not set.
+ * @param dataValueDescriptor The data value descriptor or null
if not set.
+ * @param externalData The external data in its encoded form.
+ */
+ public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+ {
+ this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+ }
+
+ /**
+ * Creates a new instance of DERExternal.
+ * See X.690 for more informations about the meaning of these parameters
+ * @param directReference The direct reference or null
if not set.
+ * @param indirectReference The indirect reference or null
if not set.
+ * @param dataValueDescriptor The data value descriptor or null
if not set.
+ * @param encoding The encoding to be used for the external data
+ * @param externalData The external data
+ */
+ public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+ {
+ setDirectReference(directReference);
+ setIndirectReference(indirectReference);
+ setDataValueDescriptor(dataValueDescriptor);
+ setEncoding(encoding);
+ setExternalContent(externalData.toASN1Primitive());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ int ret = 0;
+ if (directReference != null)
+ {
+ ret = directReference.hashCode();
+ }
+ if (indirectReference != null)
+ {
+ ret ^= indirectReference.hashCode();
+ }
+ if (dataValueDescriptor != null)
+ {
+ ret ^= dataValueDescriptor.hashCode();
+ }
+ ret ^= externalContent.hashCode();
+ return ret;
+ }
+
+ boolean isConstructed()
+ {
+ return true;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ return this.getEncoded().length;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.asn1.ASN1Primitive#encode(org.spongycastle.asn1.DEROutputStream)
+ */
+ void encode(ASN1OutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if (directReference != null)
+ {
+ baos.write(directReference.getEncoded(ASN1Encoding.DER));
+ }
+ if (indirectReference != null)
+ {
+ baos.write(indirectReference.getEncoded(ASN1Encoding.DER));
+ }
+ if (dataValueDescriptor != null)
+ {
+ baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DER));
+ }
+ DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
+ baos.write(obj.getEncoded(ASN1Encoding.DER));
+ out.writeEncoded(BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.asn1.ASN1Primitive#asn1Equals(org.spongycastle.asn1.ASN1Primitive)
+ */
+ boolean asn1Equals(ASN1Primitive o)
+ {
+ if (!(o instanceof DERExternal))
+ {
+ return false;
+ }
+ if (this == o)
+ {
+ return true;
+ }
+ DERExternal other = (DERExternal)o;
+ if (directReference != null)
+ {
+ if (other.directReference == null || !other.directReference.equals(directReference))
+ {
+ return false;
+ }
+ }
+ if (indirectReference != null)
+ {
+ if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
+ {
+ return false;
+ }
+ }
+ if (dataValueDescriptor != null)
+ {
+ if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
+ {
+ return false;
+ }
+ }
+ return externalContent.equals(other.externalContent);
+ }
+
+ /**
+ * Returns the data value descriptor
+ * @return The descriptor
+ */
+ public ASN1Primitive getDataValueDescriptor()
+ {
+ return dataValueDescriptor;
+ }
+
+ /**
+ * Returns the direct reference of the external element
+ * @return The reference
+ */
+ public ASN1ObjectIdentifier getDirectReference()
+ {
+ return directReference;
+ }
+
+ /**
+ * Returns the encoding of the content. Valid values are
+ *
0
single-ASN1-type1
OCTET STRING2
BIT STRING0
single-ASN1-type1
OCTET STRING2
BIT STRING+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *
+ * dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + *+ * To read in the time and get a date which is compatible with our local + * time zone. + */ + public String getTime() + { + String stime = Strings.fromByteArray(time); + + // + // standardise the format. + // + if (stime.charAt(stime.length() - 1) == 'Z') + { + return stime.substring(0, stime.length() - 1) + "GMT+00:00"; + } + else + { + int signPos = stime.length() - 5; + char sign = stime.charAt(signPos); + if (sign == '-' || sign == '+') + { + return stime.substring(0, signPos) + + "GMT" + + stime.substring(signPos, signPos + 3) + + ":" + + stime.substring(signPos + 3); + } + else + { + signPos = stime.length() - 3; + sign = stime.charAt(signPos); + if (sign == '-' || sign == '+') + { + return stime.substring(0, signPos) + + "GMT" + + stime.substring(signPos) + + ":00"; + } + } + } + return stime + calculateGMTOffset(); + } + + private String calculateGMTOffset() + { + String sign = "+"; + TimeZone timeZone = TimeZone.getDefault(); + int offset = timeZone.getRawOffset(); + if (offset < 0) + { + sign = "-"; + offset = -offset; + } + int hours = offset / (60 * 60 * 1000); + int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000); + + try + { + if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate())) + { + hours += sign.equals("+") ? 1 : -1; + } + } + catch (ParseException e) + { + // we'll do our best and ignore daylight savings + } + + return "GMT" + sign + convert(hours) + ":" + convert(minutes); + } + + private String convert(int time) + { + if (time < 10) + { + return "0" + time; + } + + return Integer.toString(time); + } + + public Date getDate() + throws ParseException + { + SimpleDateFormat dateF; + String stime = Strings.fromByteArray(time); + String d = stime; + + if (stime.endsWith("Z")) + { + if (hasFractionalSeconds()) + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'"); + } + else + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); + } + + dateF.setTimeZone(new SimpleTimeZone(0, "Z")); + } + else if (stime.indexOf('-') > 0 || stime.indexOf('+') > 0) + { + d = this.getTime(); + if (hasFractionalSeconds()) + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz"); + } + else + { + dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + } + + dateF.setTimeZone(new SimpleTimeZone(0, "Z")); + } + else + { + if (hasFractionalSeconds()) + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); + } + else + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss"); + } + + dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID())); + } + + if (hasFractionalSeconds()) + { + // java misinterprets extra digits as being milliseconds... + String frac = d.substring(14); + int index; + for (index = 1; index < frac.length(); index++) + { + char ch = frac.charAt(index); + if (!('0' <= ch && ch <= '9')) + { + break; + } + } + + if (index - 1 > 3) + { + frac = frac.substring(0, 4) + frac.substring(index); + d = d.substring(0, 14) + frac; + } + else if (index - 1 == 1) + { + frac = frac.substring(0, index) + "00" + frac.substring(index); + d = d.substring(0, 14) + frac; + } + else if (index - 1 == 2) + { + frac = frac.substring(0, index) + "0" + frac.substring(index); + d = d.substring(0, 14) + frac; + } + } + + return dateF.parse(d); + } + + private boolean hasFractionalSeconds() + { + for (int i = 0; i != time.length; i++) + { + if (time[i] == '.') + { + if (i == 14) + { + return true; + } + } + } + return false; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + int length = time.length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.GENERALIZED_TIME, time); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERGeneralizedTime)) + { + return false; + } + + return Arrays.areEqual(time, ((DERGeneralizedTime)o).time); + } + + public int hashCode() + { + return Arrays.hashCode(time); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERGenerator.java new file mode 100644 index 000000000..da6d65450 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERGenerator.java @@ -0,0 +1,119 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.util.io.Streams; + +public abstract class DERGenerator + extends ASN1Generator +{ + private boolean _tagged = false; + private boolean _isExplicit; + private int _tagNo; + + protected DERGenerator( + OutputStream out) + { + super(out); + } + + public DERGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + { + super(out); + + _tagged = true; + _isExplicit = isExplicit; + _tagNo = tagNo; + } + + private void writeLength( + OutputStream out, + int length) + throws IOException + { + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + out.write((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + out.write((byte)(length >> i)); + } + } + else + { + out.write((byte)length); + } + } + + void writeDEREncoded( + OutputStream out, + int tag, + byte[] bytes) + throws IOException + { + out.write(tag); + writeLength(out, bytes.length); + out.write(bytes); + } + + void writeDEREncoded( + int tag, + byte[] bytes) + throws IOException + { + if (_tagged) + { + int tagNum = _tagNo | BERTags.TAGGED; + + if (_isExplicit) + { + int newTag = _tagNo | BERTags.CONSTRUCTED | BERTags.TAGGED; + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + writeDEREncoded(bOut, tag, bytes); + + writeDEREncoded(_out, newTag, bOut.toByteArray()); + } + else + { + if ((tag & BERTags.CONSTRUCTED) != 0) + { + writeDEREncoded(_out, tagNum | BERTags.CONSTRUCTED, bytes); + } + else + { + writeDEREncoded(_out, tagNum, bytes); + } + } + } + else + { + writeDEREncoded(_out, tag, bytes); + } + } + + void writeDEREncoded( + OutputStream out, + int tag, + InputStream in) + throws IOException + { + writeDEREncoded(out, tag, Streams.readAll(in)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERIA5String.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERIA5String.java new file mode 100644 index 000000000..f4a5905fe --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERIA5String.java @@ -0,0 +1,183 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER IA5String object - this is an ascii string. + */ +public class DERIA5String + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return a IA5 string from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERIA5String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERIA5String) + { + return (DERIA5String)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERIA5String)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an IA5 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERIA5String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERIA5String) + { + return getInstance(o); + } + else + { + return new DERIA5String(((ASN1OctetString)o).getOctets()); + } + } + + /** + * basic constructor - with bytes. + */ + DERIA5String( + byte[] string) + { + this.string = string; + } + + /** + * basic constructor - without validation. + */ + public DERIA5String( + String string) + { + this(string, false); + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws IllegalArgumentException if validate is true and the string + * contains characters that should not be in an IA5String. + */ + public DERIA5String( + String string, + boolean validate) + { + if (string == null) + { + throw new NullPointerException("string cannot be null"); + } + if (validate && !isIA5String(string)) + { + throw new IllegalArgumentException("string contains illegal characters"); + } + + this.string = Strings.toByteArray(string); + } + + public String getString() + { + return Strings.fromByteArray(string); + } + + public String toString() + { + return getString(); + } + + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.IA5_STRING, string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERIA5String)) + { + return false; + } + + DERIA5String s = (DERIA5String)o; + + return Arrays.areEqual(string, s.string); + } + + /** + * return true if the passed in String can be represented without + * loss as an IA5String, false otherwise. + * + * @return true if in printable set, false otherwise. + */ + public static boolean isIA5String( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + char ch = str.charAt(i); + + if (ch > 0x007f) + { + return false; + } + } + + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERInteger.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERInteger.java new file mode 100644 index 000000000..77051cfbe --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERInteger.java @@ -0,0 +1,160 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.util.Arrays; + +/** + * Use ASN1Integer instead of this, + */ +public class DERInteger + extends ASN1Primitive +{ + byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Integer getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Integer) + { + return (ASN1Integer)obj; + } + if (obj instanceof DERInteger) + { + return new ASN1Integer((((DERInteger)obj).getValue())); + } + + if (obj instanceof byte[]) + { + try + { + return (ASN1Integer)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Integer from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Integer getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERInteger) + { + return getInstance(o); + } + else + { + return new ASN1Integer(ASN1OctetString.getInstance(obj.getObject()).getOctets()); + } + } + + /** + * @deprecated use ASN1Integer constructor + */ + public DERInteger( + long value) + { + bytes = BigInteger.valueOf(value).toByteArray(); + } + + /** + * @deprecated use ASN1Integer constructor + */ + public DERInteger( + BigInteger value) + { + bytes = value.toByteArray(); + } + + /** + * @deprecated use ASN1Integer constructor + */ + public DERInteger( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger getValue() + { + return new BigInteger(bytes); + } + + /** + * in some cases positive values get crammed into a space, + * that's not quite big enough... + */ + public BigInteger getPositiveValue() + { + return new BigInteger(1, bytes); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.INTEGER, bytes); + } + + public int hashCode() + { + int value = 0; + + for (int i = 0; i != bytes.length; i++) + { + value ^= (bytes[i] & 0xff) << (i % 4); + } + + return value; + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERInteger)) + { + return false; + } + + DERInteger other = (DERInteger)o; + + return Arrays.areEqual(bytes, other.bytes); + } + + public String toString() + { + return getValue().toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERNull.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERNull.java new file mode 100644 index 000000000..f093cd272 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERNull.java @@ -0,0 +1,38 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +/** + * A NULL object. + */ +public class DERNull + extends ASN1Null +{ + public static final DERNull INSTANCE = new DERNull(); + + private static final byte[] zeroBytes = new byte[0]; + + /** + * @deprecated use DERNull.INSTANCE + */ + public DERNull() + { + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 2; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.NULL, zeroBytes); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERNumericString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERNumericString.java new file mode 100644 index 000000000..d092fce0b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERNumericString.java @@ -0,0 +1,186 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }. + */ +public class DERNumericString + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return a Numeric string from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERNumericString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERNumericString) + { + return (DERNumericString)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERNumericString)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Numeric String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERNumericString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERNumericString) + { + return getInstance(o); + } + else + { + return new DERNumericString(ASN1OctetString.getInstance(o).getOctets()); + } + } + + /** + * basic constructor - with bytes. + */ + DERNumericString( + byte[] string) + { + this.string = string; + } + + /** + * basic constructor - without validation.. + */ + public DERNumericString( + String string) + { + this(string, false); + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws IllegalArgumentException if validate is true and the string + * contains characters that should not be in a NumericString. + */ + public DERNumericString( + String string, + boolean validate) + { + if (validate && !isNumericString(string)) + { + throw new IllegalArgumentException("string contains illegal characters"); + } + + this.string = Strings.toByteArray(string); + } + + public String getString() + { + return Strings.fromByteArray(string); + } + + public String toString() + { + return getString(); + } + + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.NUMERIC_STRING, string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERNumericString)) + { + return false; + } + + DERNumericString s = (DERNumericString)o; + + return Arrays.areEqual(string, s.string); + } + + /** + * Return true if the string can be represented as a NumericString ('0'..'9', ' ') + * + * @param str string to validate. + * @return true if numeric, fale otherwise. + */ + public static boolean isNumericString( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + char ch = str.charAt(i); + + if (ch > 0x007f) + { + return false; + } + + if (('0' <= ch && ch <= '9') || ch == ' ') + { + continue; + } + + return false; + } + + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERObjectIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERObjectIdentifier.java new file mode 100644 index 000000000..dd0106560 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERObjectIdentifier.java @@ -0,0 +1,446 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.util.Arrays; + +/** + * Use ASN1ObjectIdentifier instead of this, + */ +public class DERObjectIdentifier + extends ASN1Primitive +{ + String identifier; + + private byte[] body; + + /** + * return an OID from the passed in object + * + * @throws IllegalArgumentException if the object cannot be converted. + */ + public static ASN1ObjectIdentifier getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1ObjectIdentifier) + { + return (ASN1ObjectIdentifier)obj; + } + + if (obj instanceof DERObjectIdentifier) + { + return new ASN1ObjectIdentifier(((DERObjectIdentifier)obj).getId()); + } + + if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier) + { + return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive(); + } + + if (obj instanceof byte[]) + { + byte[] enc = (byte[])obj; + if (enc[0] == BERTags.OBJECT_IDENTIFIER) + { + try + { + return (ASN1ObjectIdentifier)fromByteArray(enc); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage()); + } + } + else + { // TODO: this really shouldn't be supported here... + return ASN1ObjectIdentifier.fromOctetString((byte[])obj); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Object Identifier from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1ObjectIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERObjectIdentifier) + { + return getInstance(o); + } + else + { + return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets()); + } + } + + private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f; + + DERObjectIdentifier( + byte[] bytes) + { + StringBuffer objId = new StringBuffer(); + long value = 0; + BigInteger bigValue = null; + boolean first = true; + + for (int i = 0; i != bytes.length; i++) + { + int b = bytes[i] & 0xff; + + if (value <= LONG_LIMIT) + { + value += (b & 0x7f); + if ((b & 0x80) == 0) // end of number reached + { + if (first) + { + if (value < 40) + { + objId.append('0'); + } + else if (value < 80) + { + objId.append('1'); + value -= 40; + } + else + { + objId.append('2'); + value -= 80; + } + first = false; + } + + objId.append('.'); + objId.append(value); + value = 0; + } + else + { + value <<= 7; + } + } + else + { + if (bigValue == null) + { + bigValue = BigInteger.valueOf(value); + } + bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f)); + if ((b & 0x80) == 0) + { + if (first) + { + objId.append('2'); + bigValue = bigValue.subtract(BigInteger.valueOf(80)); + first = false; + } + + objId.append('.'); + objId.append(bigValue); + bigValue = null; + value = 0; + } + else + { + bigValue = bigValue.shiftLeft(7); + } + } + } + + this.identifier = objId.toString(); + this.body = Arrays.clone(bytes); + } + + /** + * @deprecated use ASN1ObjectIdentifier constructor. + */ + public DERObjectIdentifier( + String identifier) + { + if (identifier == null) + { + throw new IllegalArgumentException("'identifier' cannot be null"); + } + if (!isValidIdentifier(identifier)) + { + throw new IllegalArgumentException("string " + identifier + " not an OID"); + } + + this.identifier = identifier; + } + + DERObjectIdentifier(DERObjectIdentifier oid, String branchID) + { + if (!isValidBranchID(branchID, 0)) + { + throw new IllegalArgumentException("string " + branchID + " not a valid OID branch"); + } + + this.identifier = oid.getId() + "." + branchID; + } + + public String getId() + { + return identifier; + } + + private void writeField( + ByteArrayOutputStream out, + long fieldValue) + { + byte[] result = new byte[9]; + int pos = 8; + result[pos] = (byte)((int)fieldValue & 0x7f); + while (fieldValue >= (1L << 7)) + { + fieldValue >>= 7; + result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80); + } + out.write(result, pos, 9 - pos); + } + + private void writeField( + ByteArrayOutputStream out, + BigInteger fieldValue) + { + int byteCount = (fieldValue.bitLength() + 6) / 7; + if (byteCount == 0) + { + out.write(0); + } + else + { + BigInteger tmpValue = fieldValue; + byte[] tmp = new byte[byteCount]; + for (int i = byteCount - 1; i >= 0; i--) + { + tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80); + tmpValue = tmpValue.shiftRight(7); + } + tmp[byteCount - 1] &= 0x7f; + out.write(tmp, 0, tmp.length); + } + } + + private void doOutput(ByteArrayOutputStream aOut) + { + OIDTokenizer tok = new OIDTokenizer(identifier); + int first = Integer.parseInt(tok.nextToken()) * 40; + + String secondToken = tok.nextToken(); + if (secondToken.length() <= 18) + { + writeField(aOut, first + Long.parseLong(secondToken)); + } + else + { + writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first))); + } + + while (tok.hasMoreTokens()) + { + String token = tok.nextToken(); + if (token.length() <= 18) + { + writeField(aOut, Long.parseLong(token)); + } + else + { + writeField(aOut, new BigInteger(token)); + } + } + } + + protected synchronized byte[] getBody() + { + if (body == null) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + doOutput(bOut); + + body = bOut.toByteArray(); + } + + return body; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + throws IOException + { + int length = getBody().length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + byte[] enc = getBody(); + + out.write(BERTags.OBJECT_IDENTIFIER); + out.writeLength(enc.length); + out.write(enc); + } + + public int hashCode() + { + return identifier.hashCode(); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERObjectIdentifier)) + { + return false; + } + + return identifier.equals(((DERObjectIdentifier)o).identifier); + } + + public String toString() + { + return getId(); + } + + private static boolean isValidBranchID( + String branchID, int start) + { + boolean periodAllowed = false; + + int pos = branchID.length(); + while (--pos >= start) + { + char ch = branchID.charAt(pos); + + // TODO Leading zeroes? + if ('0' <= ch && ch <= '9') + { + periodAllowed = true; + continue; + } + + if (ch == '.') + { + if (!periodAllowed) + { + return false; + } + + periodAllowed = false; + continue; + } + + return false; + } + + return periodAllowed; + } + + private static boolean isValidIdentifier( + String identifier) + { + if (identifier.length() < 3 || identifier.charAt(1) != '.') + { + return false; + } + + char first = identifier.charAt(0); + if (first < '0' || first > '2') + { + return false; + } + + return isValidBranchID(identifier, 2); + } + + private static ASN1ObjectIdentifier[][] cache = new ASN1ObjectIdentifier[256][]; + + static ASN1ObjectIdentifier fromOctetString(byte[] enc) + { + if (enc.length < 3) + { + return new ASN1ObjectIdentifier(enc); + } + + int idx1 = enc[enc.length - 2] & 0xff; + // in this case top bit is always zero + int idx2 = enc[enc.length - 1] & 0x7f; + + ASN1ObjectIdentifier possibleMatch; + + synchronized (cache) + { + ASN1ObjectIdentifier[] first = cache[idx1]; + if (first == null) + { + first = cache[idx1] = new ASN1ObjectIdentifier[128]; + } + + possibleMatch = first[idx2]; + if (possibleMatch == null) + { + return first[idx2] = new ASN1ObjectIdentifier(enc); + } + + if (Arrays.areEqual(enc, possibleMatch.getBody())) + { + return possibleMatch; + } + + idx1 = (idx1 + 1) & 0xff; + first = cache[idx1]; + if (first == null) + { + first = cache[idx1] = new ASN1ObjectIdentifier[128]; + } + + possibleMatch = first[idx2]; + if (possibleMatch == null) + { + return first[idx2] = new ASN1ObjectIdentifier(enc); + } + + if (Arrays.areEqual(enc, possibleMatch.getBody())) + { + return possibleMatch; + } + + idx2 = (idx2 + 1) & 0x7f; + possibleMatch = first[idx2]; + if (possibleMatch == null) + { + return first[idx2] = new ASN1ObjectIdentifier(enc); + } + } + + if (Arrays.areEqual(enc, possibleMatch.getBody())) + { + return possibleMatch; + } + + return new ASN1ObjectIdentifier(enc); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROctetString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROctetString.java new file mode 100644 index 000000000..68ae920a6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROctetString.java @@ -0,0 +1,48 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public class DEROctetString + extends ASN1OctetString +{ + /** + * @param string the octets making up the octet string. + */ + public DEROctetString( + byte[] string) + { + super(string); + } + + public DEROctetString( + ASN1Encodable obj) + throws IOException + { + super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.OCTET_STRING, string); + } + + static void encode( + DEROutputStream derOut, + byte[] bytes) + throws IOException + { + derOut.writeEncoded(BERTags.OCTET_STRING, bytes); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROctetStringParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROctetStringParser.java new file mode 100644 index 000000000..59a82cd8b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROctetStringParser.java @@ -0,0 +1,39 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.io.InputStream; + +public class DEROctetStringParser + implements ASN1OctetStringParser +{ + private DefiniteLengthInputStream stream; + + DEROctetStringParser( + DefiniteLengthInputStream stream) + { + this.stream = stream; + } + + public InputStream getOctetStream() + { + return stream; + } + + public ASN1Primitive getLoadedObject() + throws IOException + { + return new DEROctetString(stream.toByteArray()); + } + + public ASN1Primitive toASN1Primitive() + { + try + { + return getLoadedObject(); + } + catch (IOException e) + { + throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROutputStream.java new file mode 100644 index 000000000..b94fc7b08 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DEROutputStream.java @@ -0,0 +1,41 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Stream that outputs encoding based on distinguished encoding rules. + */ +public class DEROutputStream + extends ASN1OutputStream +{ + public DEROutputStream( + OutputStream os) + { + super(os); + } + + public void writeObject( + ASN1Encodable obj) + throws IOException + { + if (obj != null) + { + obj.toASN1Primitive().toDERObject().encode(this); + } + else + { + throw new IOException("null object detected"); + } + } + + ASN1OutputStream getDERSubStream() + { + return this; + } + + ASN1OutputStream getDLSubStream() + { + return this; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERPrintableString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERPrintableString.java new file mode 100644 index 000000000..08130759c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERPrintableString.java @@ -0,0 +1,213 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER PrintableString object. + */ +public class DERPrintableString + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return a printable string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERPrintableString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERPrintableString) + { + return (DERPrintableString)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERPrintableString)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Printable String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERPrintableString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERPrintableString) + { + return getInstance(o); + } + else + { + return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets()); + } + } + + /** + * basic constructor - byte encoded string. + */ + DERPrintableString( + byte[] string) + { + this.string = string; + } + + /** + * basic constructor - this does not validate the string + */ + public DERPrintableString( + String string) + { + this(string, false); + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws IllegalArgumentException if validate is true and the string + * contains characters that should not be in a PrintableString. + */ + public DERPrintableString( + String string, + boolean validate) + { + if (validate && !isPrintableString(string)) + { + throw new IllegalArgumentException("string contains illegal characters"); + } + + this.string = Strings.toByteArray(string); + } + + public String getString() + { + return Strings.fromByteArray(string); + } + + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.PRINTABLE_STRING, string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERPrintableString)) + { + return false; + } + + DERPrintableString s = (DERPrintableString)o; + + return Arrays.areEqual(string, s.string); + } + + public String toString() + { + return getString(); + } + + /** + * return true if the passed in String can be represented without + * loss as a PrintableString, false otherwise. + * + * @return true if in printable set, false otherwise. + */ + public static boolean isPrintableString( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + char ch = str.charAt(i); + + if (ch > 0x007f) + { + return false; + } + + if ('a' <= ch && ch <= 'z') + { + continue; + } + + if ('A' <= ch && ch <= 'Z') + { + continue; + } + + if ('0' <= ch && ch <= '9') + { + continue; + } + + switch (ch) + { + case ' ': + case '\'': + case '(': + case ')': + case '+': + case '-': + case '.': + case ':': + case '=': + case '?': + case '/': + case ',': + continue; + } + + return false; + } + + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequence.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequence.java new file mode 100644 index 000000000..df171ca50 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequence.java @@ -0,0 +1,98 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +public class DERSequence + extends ASN1Sequence +{ + private int bodyLength = -1; + + /** + * create an empty sequence + */ + public DERSequence() + { + } + + /** + * create a sequence containing one object + */ + public DERSequence( + ASN1Encodable obj) + { + super(obj); + } + + /** + * create a sequence containing a vector of objects. + */ + public DERSequence( + ASN1EncodableVector v) + { + super(v); + } + + /** + * create a sequence containing an array of objects. + */ + public DERSequence( + ASN1Encodable[] array) + { + super(array); + } + + private int getBodyLength() + throws IOException + { + if (bodyLength < 0) + { + int length = 0; + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + length += ((ASN1Encodable)obj).toASN1Primitive().toDERObject().encodedLength(); + } + + bodyLength = length; + } + + return bodyLength; + } + + int encodedLength() + throws IOException + { + int length = getBodyLength(); + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + /* + * A note on the implementation: + *
+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputting SEQUENCE, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + ASN1OutputStream out) + throws IOException + { + ASN1OutputStream dOut = out.getDERSubStream(); + int length = getBodyLength(); + + out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED); + out.writeLength(length); + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + dOut.writeObject((ASN1Encodable)obj); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequenceGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequenceGenerator.java new file mode 100644 index 000000000..39c430328 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequenceGenerator.java @@ -0,0 +1,45 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class DERSequenceGenerator + extends DERGenerator +{ + private final ByteArrayOutputStream _bOut = new ByteArrayOutputStream(); + + public DERSequenceGenerator( + OutputStream out) + throws IOException + { + super(out); + } + + public DERSequenceGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + throws IOException + { + super(out, tagNo, isExplicit); + } + + public void addObject( + ASN1Encodable object) + throws IOException + { + object.toASN1Primitive().encode(new DEROutputStream(_bOut)); + } + + public OutputStream getRawOutputStream() + { + return _bOut; + } + + public void close() + throws IOException + { + writeDEREncoded(BERTags.CONSTRUCTED | BERTags.SEQUENCE, _bOut.toByteArray()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequenceParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequenceParser.java new file mode 100644 index 000000000..95f66e6f7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSequenceParser.java @@ -0,0 +1,38 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public class DERSequenceParser + implements ASN1SequenceParser +{ + private ASN1StreamParser _parser; + + DERSequenceParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public ASN1Encodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public ASN1Primitive getLoadedObject() + throws IOException + { + return new DERSequence(_parser.readVector()); + } + + public ASN1Primitive toASN1Primitive() + { + try + { + return getLoadedObject(); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSet.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSet.java new file mode 100644 index 000000000..b504a8fd0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSet.java @@ -0,0 +1,108 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * A DER encoded set object + */ +public class DERSet + extends ASN1Set +{ + private int bodyLength = -1; + + /** + * create an empty set + */ + public DERSet() + { + } + + /** + * @param obj - a single object that makes up the set. + */ + public DERSet( + ASN1Encodable obj) + { + super(obj); + } + + /** + * @param v - a vector of objects making up the set. + */ + public DERSet( + ASN1EncodableVector v) + { + super(v, true); + } + + /** + * create a set from an array of objects. + */ + public DERSet( + ASN1Encodable[] a) + { + super(a, true); + } + + DERSet( + ASN1EncodableVector v, + boolean doSort) + { + super(v, doSort); + } + + private int getBodyLength() + throws IOException + { + if (bodyLength < 0) + { + int length = 0; + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + length += ((ASN1Encodable)obj).toASN1Primitive().toDERObject().encodedLength(); + } + + bodyLength = length; + } + + return bodyLength; + } + + int encodedLength() + throws IOException + { + int length = getBodyLength(); + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + /* + * A note on the implementation: + *
+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputting SET, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + ASN1OutputStream out) + throws IOException + { + ASN1OutputStream dOut = out.getDERSubStream(); + int length = getBodyLength(); + + out.write(BERTags.SET | BERTags.CONSTRUCTED); + out.writeLength(length); + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + dOut.writeObject((ASN1Encodable)obj); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSetParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSetParser.java new file mode 100644 index 000000000..adae30412 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERSetParser.java @@ -0,0 +1,38 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public class DERSetParser + implements ASN1SetParser +{ + private ASN1StreamParser _parser; + + DERSetParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public ASN1Encodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public ASN1Primitive getLoadedObject() + throws IOException + { + return new DERSet(_parser.readVector(), false); + } + + public ASN1Primitive toASN1Primitive() + { + try + { + return getLoadedObject(); + } + catch (IOException e) + { + throw new ASN1ParsingException(e.getMessage(), e); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERT61String.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERT61String.java new file mode 100644 index 000000000..f931c8b8b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERT61String.java @@ -0,0 +1,144 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for + * this has been withdrawn. + */ +public class DERT61String + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return a T61 string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERT61String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERT61String) + { + return (DERT61String)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERT61String)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an T61 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERT61String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERT61String) + { + return getInstance(o); + } + else + { + return new DERT61String(ASN1OctetString.getInstance(o).getOctets()); + } + } + + /** + * basic constructor - string encoded as a sequence of bytes. + */ + public DERT61String( + byte[] string) + { + this.string = string; + } + + /** + * basic constructor - with string 8 bit assumed. + */ + public DERT61String( + String string) + { + this(Strings.toByteArray(string)); + } + + /** + * Decode the encoded string and return it, 8 bit encoding assumed. + * @return the decoded String + */ + public String getString() + { + return Strings.fromByteArray(string); + } + + public String toString() + { + return getString(); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.T61_STRING, string); + } + + /** + * Return the encoded string as a byte array. + * @return the actual bytes making up the encoded body of the T61 string. + */ + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERT61String)) + { + return false; + } + + return Arrays.areEqual(string, ((DERT61String)o).string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERT61UTF8String.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERT61UTF8String.java new file mode 100644 index 000000000..4a240920b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERT61UTF8String.java @@ -0,0 +1,151 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER T61String (also the teletex string) - a "modern" encapsulation that uses UTF-8. If at all possible, avoid this one! It's only for emergencies. + * Use UTF8String instead. + */ +public class DERT61UTF8String + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return a T61 string from the passed in object. UTF-8 Encoding is assumed in this case. + * + * @throws IllegalArgumentException if the object cannot be converted. + */ + public static DERT61UTF8String getInstance( + Object obj) + { + if (obj instanceof DERT61String) + { + return new DERT61UTF8String(((DERT61String)obj).getOctets()); + } + + if (obj == null || obj instanceof DERT61UTF8String) + { + return (DERT61UTF8String)obj; + } + + if (obj instanceof byte[]) + { + try + { + return new DERT61UTF8String(((DERT61String)fromByteArray((byte[])obj)).getOctets()); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an T61 String from a tagged object. UTF-8 encoding is assumed in this case. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERT61UTF8String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERT61String || o instanceof DERT61UTF8String) + { + return getInstance(o); + } + else + { + return new DERT61UTF8String(ASN1OctetString.getInstance(o).getOctets()); + } + } + + /** + * basic constructor - string encoded as a sequence of bytes. + */ + public DERT61UTF8String( + byte[] string) + { + this.string = string; + } + + /** + * basic constructor - with string UTF8 conversion assumed. + */ + public DERT61UTF8String( + String string) + { + this(Strings.toUTF8ByteArray(string)); + } + + /** + * Decode the encoded string and return it, UTF8 assumed. + * + * @return the decoded String + */ + public String getString() + { + return Strings.fromUTF8ByteArray(string); + } + + public String toString() + { + return getString(); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.T61_STRING, string); + } + + /** + * Return the encoded string as a byte array. + * + * @return the actual bytes making up the encoded body of the T61 string. + */ + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERT61UTF8String)) + { + return false; + } + + return Arrays.areEqual(string, ((DERT61UTF8String)o).string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERTaggedObject.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERTaggedObject.java new file mode 100644 index 000000000..3ea7d984a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERTaggedObject.java @@ -0,0 +1,118 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +/** + * DER TaggedObject - in ASN.1 notation this is any object preceded by + * a [n] where n is some number - these are assumed to follow the construction + * rules (as with sequences). + */ +public class DERTaggedObject + extends ASN1TaggedObject +{ + private static final byte[] ZERO_BYTES = new byte[0]; + + /** + * @param explicit true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DERTaggedObject( + boolean explicit, + int tagNo, + ASN1Encodable obj) + { + super(explicit, tagNo, obj); + } + + public DERTaggedObject(int tagNo, ASN1Encodable encodable) + { + super(true, tagNo, encodable); + } + + boolean isConstructed() + { + if (!empty) + { + if (explicit) + { + return true; + } + else + { + ASN1Primitive primitive = obj.toASN1Primitive().toDERObject(); + + return primitive.isConstructed(); + } + } + else + { + return true; + } + } + + int encodedLength() + throws IOException + { + if (!empty) + { + ASN1Primitive primitive = obj.toASN1Primitive().toDERObject(); + int length = primitive.encodedLength(); + + if (explicit) + { + return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length; + } + else + { + // header length already in calculation + length = length - 1; + + return StreamUtil.calculateTagLength(tagNo) + length; + } + } + else + { + return StreamUtil.calculateTagLength(tagNo) + 1; + } + } + + void encode( + ASN1OutputStream out) + throws IOException + { + if (!empty) + { + ASN1Primitive primitive = obj.toASN1Primitive().toDERObject(); + + if (explicit) + { + out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo); + out.writeLength(primitive.encodedLength()); + out.writeObject(primitive); + } + else + { + // + // need to mark constructed types... + // + int flags; + if (primitive.isConstructed()) + { + flags = BERTags.CONSTRUCTED | BERTags.TAGGED; + } + else + { + flags = BERTags.TAGGED; + } + + out.writeTag(flags, tagNo); + out.writeImplicitObject(primitive); + } + } + else + { + out.writeEncoded(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo, ZERO_BYTES); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERTags.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERTags.java new file mode 100644 index 000000000..4a5dd6641 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERTags.java @@ -0,0 +1,9 @@ +package org.spongycastle.asn1; + +/** + * @deprecated use BERTags + */ +public interface DERTags + extends BERTags +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUTCTime.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUTCTime.java new file mode 100644 index 000000000..3f8aa31d4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUTCTime.java @@ -0,0 +1,278 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.SimpleTimeZone; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * UTC time object. + */ +public class DERUTCTime + extends ASN1Primitive +{ + private byte[] time; + + /** + * return an UTC Time from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1UTCTime getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1UTCTime) + { + return (ASN1UTCTime)obj; + } + + if (obj instanceof DERUTCTime) + { + return new ASN1UTCTime(((DERUTCTime)obj).time); + } + + if (obj instanceof byte[]) + { + try + { + return (ASN1UTCTime)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an UTC Time from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1UTCTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Object o = obj.getObject(); + + if (explicit || o instanceof ASN1UTCTime) + { + return getInstance(o); + } + else + { + return new ASN1UTCTime(((ASN1OctetString)o).getOctets()); + } + } + + /** + * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were + * never encoded. When you're creating one of these objects from scratch, that's + * what you want to use, otherwise we'll try to deal with whatever gets read from + * the input stream... (this is why the input format is different from the getTime() + * method output). + *
+ * + * @param time the time string. + */ + public DERUTCTime( + String time) + { + this.time = Strings.toByteArray(time); + try + { + this.getDate(); + } + catch (ParseException e) + { + throw new IllegalArgumentException("invalid date string: " + e.getMessage()); + } + } + + /** + * base constructer from a java.util.date object + */ + public DERUTCTime( + Date time) + { + SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'"); + + dateF.setTimeZone(new SimpleTimeZone(0,"Z")); + + this.time = Strings.toByteArray(dateF.format(time)); + } + + DERUTCTime( + byte[] time) + { + this.time = time; + } + + /** + * return the time as a date based on whatever a 2 digit year will return. For + * standardised processing use getAdjustedDate(). + * + * @return the resulting date + * @exception ParseException if the date string cannot be parsed. + */ + public Date getDate() + throws ParseException + { + SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz"); + + return dateF.parse(getTime()); + } + + /** + * return the time as an adjusted date + * in the range of 1950 - 2049. + * + * @return a date in the range of 1950 to 2049. + * @exception ParseException if the date string cannot be parsed. + */ + public Date getAdjustedDate() + throws ParseException + { + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + + dateF.setTimeZone(new SimpleTimeZone(0, "Z")); + + return dateF.parse(getAdjustedTime()); + } + + /** + * return the time - always in the form of + * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *
+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *
+ * dateF = new SimpleDateFormat("yyMMddHHmmssz"); + *+ * To read in the time and get a date which is compatible with our local + * time zone. + *
+ * Note: In some cases, due to the local date processing, this + * may lead to unexpected results. If you want to stick the normal + * convention of 1950 to 2049 use the getAdjustedTime() method. + */ + public String getTime() + { + String stime = Strings.fromByteArray(time); + + // + // standardise the format. + // + if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0) + { + if (stime.length() == 11) + { + return stime.substring(0, 10) + "00GMT+00:00"; + } + else + { + return stime.substring(0, 12) + "GMT+00:00"; + } + } + else + { + int index = stime.indexOf('-'); + if (index < 0) + { + index = stime.indexOf('+'); + } + String d = stime; + + if (index == stime.length() - 3) + { + d += "00"; + } + + if (index == 10) + { + return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); + } + else + { + return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); + } + } + } + + /** + * return a time string as an adjusted date with a 4 digit year. This goes + * in the range of 1950 - 2049. + */ + public String getAdjustedTime() + { + String d = this.getTime(); + + if (d.charAt(0) < '5') + { + return "20" + d; + } + else + { + return "19" + d; + } + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + int length = time.length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.write(BERTags.UTC_TIME); + + int length = time.length; + + out.writeLength(length); + + for (int i = 0; i != length; i++) + { + out.write((byte)time[i]); + } + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERUTCTime)) + { + return false; + } + + return Arrays.areEqual(time, ((DERUTCTime)o).time); + } + + public int hashCode() + { + return Arrays.hashCode(time); + } + + public String toString() + { + return Strings.fromByteArray(time); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUTF8String.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUTF8String.java new file mode 100644 index 000000000..ca22d16ba --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUTF8String.java @@ -0,0 +1,132 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER UTF8String object. + */ +public class DERUTF8String + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return an UTF8 string from the passed in object. + * + * @exception IllegalArgumentException + * if the object cannot be converted. + */ + public static DERUTF8String getInstance(Object obj) + { + if (obj == null || obj instanceof DERUTF8String) + { + return (DERUTF8String)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERUTF8String)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * return an UTF8 String from a tagged object. + * + * @param obj + * the tagged object holding the object we want + * @param explicit + * true if the object is meant to be explicitly tagged false + * otherwise. + * @exception IllegalArgumentException + * if the tagged object cannot be converted. + */ + public static DERUTF8String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERUTF8String) + { + return getInstance(o); + } + else + { + return new DERUTF8String(ASN1OctetString.getInstance(o).getOctets()); + } + } + + /** + * basic constructor - byte encoded string. + */ + DERUTF8String(byte[] string) + { + this.string = string; + } + + /** + * basic constructor + */ + public DERUTF8String(String string) + { + this.string = Strings.toUTF8ByteArray(string); + } + + public String getString() + { + return Strings.fromUTF8ByteArray(string); + } + + public String toString() + { + return getString(); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } + + boolean asn1Equals(ASN1Primitive o) + { + if (!(o instanceof DERUTF8String)) + { + return false; + } + + DERUTF8String s = (DERUTF8String)o; + + return Arrays.areEqual(string, s.string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + throws IOException + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode(ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.UTF8_STRING, string); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUniversalString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUniversalString.java new file mode 100644 index 000000000..d7bd6bd57 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERUniversalString.java @@ -0,0 +1,148 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.spongycastle.util.Arrays; + +/** + * DER UniversalString object. + */ +public class DERUniversalString + extends ASN1Primitive + implements ASN1String +{ + private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + private byte[] string; + + /** + * return a Universal String from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERUniversalString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERUniversalString) + { + return (DERUniversalString)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERUniversalString)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Universal String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERUniversalString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERUniversalString) + { + return getInstance(o); + } + else + { + return new DERUniversalString(((ASN1OctetString)o).getOctets()); + } + } + + /** + * basic constructor - byte encoded string. + */ + public DERUniversalString( + byte[] string) + { + this.string = string; + } + + public String getString() + { + StringBuffer buf = new StringBuffer("#"); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(this); + } + catch (IOException e) + { + throw new RuntimeException("internal error encoding BitString"); + } + + byte[] string = bOut.toByteArray(); + + for (int i = 0; i != string.length; i++) + { + buf.append(table[(string[i] >>> 4) & 0xf]); + buf.append(table[string[i] & 0xf]); + } + + return buf.toString(); + } + + public String toString() + { + return getString(); + } + + public byte[] getOctets() + { + return string; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.UNIVERSAL_STRING, this.getOctets()); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERUniversalString)) + { + return false; + } + + return Arrays.areEqual(string, ((DERUniversalString)o).string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERVisibleString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERVisibleString.java new file mode 100644 index 000000000..bf88ec969 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DERVisibleString.java @@ -0,0 +1,135 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; + +/** + * DER VisibleString object. + */ +public class DERVisibleString + extends ASN1Primitive + implements ASN1String +{ + private byte[] string; + + /** + * return a Visible String from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERVisibleString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERVisibleString) + { + return (DERVisibleString)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERVisibleString)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Visible String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERVisibleString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERVisibleString) + { + return getInstance(o); + } + else + { + return new DERVisibleString(ASN1OctetString.getInstance(o).getOctets()); + } + } + + /** + * basic constructor - byte encoded string. + */ + DERVisibleString( + byte[] string) + { + this.string = string; + } + + /** + * basic constructor + */ + public DERVisibleString( + String string) + { + this.string = Strings.toByteArray(string); + } + + public String getString() + { + return Strings.fromByteArray(string); + } + + public String toString() + { + return getString(); + } + + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.VISIBLE_STRING, this.string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERVisibleString)) + { + return false; + } + + return Arrays.areEqual(string, ((DERVisibleString)o).string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLOutputStream.java new file mode 100644 index 000000000..cb401e830 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLOutputStream.java @@ -0,0 +1,31 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Stream that outputs encoding based on definite length. + */ +public class DLOutputStream + extends ASN1OutputStream +{ + public DLOutputStream( + OutputStream os) + { + super(os); + } + + public void writeObject( + ASN1Encodable obj) + throws IOException + { + if (obj != null) + { + obj.toASN1Primitive().toDLObject().encode(this); + } + else + { + throw new IOException("null object detected"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLSequence.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLSequence.java new file mode 100644 index 000000000..1c621cba8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLSequence.java @@ -0,0 +1,101 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * The DLSequence encodes a SEQUENCE using definite length form. + */ +public class DLSequence + extends ASN1Sequence +{ + private int bodyLength = -1; + + /** + * Create an empty sequence + */ + public DLSequence() + { + } + + /** + * Create a sequence containing one object + */ + public DLSequence( + ASN1Encodable obj) + { + super(obj); + } + + /** + * Create a sequence containing a vector of objects. + */ + public DLSequence( + ASN1EncodableVector v) + { + super(v); + } + + /** + * Create a sequence containing an array of objects. + */ + public DLSequence( + ASN1Encodable[] array) + { + super(array); + } + + private int getBodyLength() + throws IOException + { + if (bodyLength < 0) + { + int length = 0; + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength(); + } + + bodyLength = length; + } + + return bodyLength; + } + + int encodedLength() + throws IOException + { + int length = getBodyLength(); + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + /** + * A note on the implementation: + *
+ * As DL requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputting SEQUENCE, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + ASN1OutputStream out) + throws IOException + { + ASN1OutputStream dOut = out.getDLSubStream(); + int length = getBodyLength(); + + out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED); + out.writeLength(length); + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + dOut.writeObject((ASN1Encodable)obj); + } + } +} \ No newline at end of file diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLSet.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLSet.java new file mode 100644 index 000000000..9c9ed8b85 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLSet.java @@ -0,0 +1,146 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * The DLSet encodes ASN.1 SET value without element ordering, + * and always using definite length form. + *
+ * NOTE — The order of data values in a set value is not significant, + * and places no constraints on the order during transfer + *+ *
+ * NOTE — Where a component of the set is an untagged choice type, + * the location of that component in the ordering will depend on + * the tag of the choice component being encoded. + *+ *
+ * As DL requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputting SET, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + ASN1OutputStream out) + throws IOException + { + ASN1OutputStream dOut = out.getDLSubStream(); + int length = getBodyLength(); + + out.write(BERTags.SET | BERTags.CONSTRUCTED); + out.writeLength(length); + + for (Enumeration e = this.getObjects(); e.hasMoreElements();) + { + Object obj = e.nextElement(); + + dOut.writeObject((ASN1Encodable)obj); + } + } +} \ No newline at end of file diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLTaggedObject.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLTaggedObject.java new file mode 100644 index 000000000..505c8ccad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DLTaggedObject.java @@ -0,0 +1,112 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +/** + * Definite Length TaggedObject - in ASN.1 notation this is any object preceded by + * a [n] where n is some number - these are assumed to follow the construction + * rules (as with sequences). + */ +public class DLTaggedObject + extends ASN1TaggedObject +{ + private static final byte[] ZERO_BYTES = new byte[0]; + + /** + * @param explicit true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DLTaggedObject( + boolean explicit, + int tagNo, + ASN1Encodable obj) + { + super(explicit, tagNo, obj); + } + + boolean isConstructed() + { + if (!empty) + { + if (explicit) + { + return true; + } + else + { + ASN1Primitive primitive = obj.toASN1Primitive().toDLObject(); + + return primitive.isConstructed(); + } + } + else + { + return true; + } + } + + int encodedLength() + throws IOException + { + if (!empty) + { + int length = obj.toASN1Primitive().toDLObject().encodedLength(); + + if (explicit) + { + return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length; + } + else + { + // header length already in calculation + length = length - 1; + + return StreamUtil.calculateTagLength(tagNo) + length; + } + } + else + { + return StreamUtil.calculateTagLength(tagNo) + 1; + } + } + + void encode( + ASN1OutputStream out) + throws IOException + { + if (!empty) + { + ASN1Primitive primitive = obj.toASN1Primitive().toDLObject(); + + if (explicit) + { + out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo); + out.writeLength(primitive.encodedLength()); + out.writeObject(primitive); + } + else + { + // + // need to mark constructed types... + // + int flags; + if (primitive.isConstructed()) + { + flags = BERTags.CONSTRUCTED | BERTags.TAGGED; + } + else + { + flags = BERTags.TAGGED; + } + + out.writeTag(flags, tagNo); + out.writeImplicitObject(primitive); + } + } + else + { + out.writeEncoded(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo, ZERO_BYTES); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DefiniteLengthInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DefiniteLengthInputStream.java new file mode 100644 index 000000000..711f173df --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/DefiniteLengthInputStream.java @@ -0,0 +1,105 @@ +package org.spongycastle.asn1; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.util.io.Streams; + +class DefiniteLengthInputStream + extends LimitedInputStream +{ + private static final byte[] EMPTY_BYTES = new byte[0]; + + private final int _originalLength; + private int _remaining; + + DefiniteLengthInputStream( + InputStream in, + int length) + { + super(in, length); + + if (length < 0) + { + throw new IllegalArgumentException("negative lengths not allowed"); + } + + this._originalLength = length; + this._remaining = length; + + if (length == 0) + { + setParentEofDetect(true); + } + } + + int getRemaining() + { + return _remaining; + } + + public int read() + throws IOException + { + if (_remaining == 0) + { + return -1; + } + + int b = _in.read(); + + if (b < 0) + { + throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining); + } + + if (--_remaining == 0) + { + setParentEofDetect(true); + } + + return b; + } + + public int read(byte[] buf, int off, int len) + throws IOException + { + if (_remaining == 0) + { + return -1; + } + + int toRead = Math.min(len, _remaining); + int numRead = _in.read(buf, off, toRead); + + if (numRead < 0) + { + throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining); + } + + if ((_remaining -= numRead) == 0) + { + setParentEofDetect(true); + } + + return numRead; + } + + byte[] toByteArray() + throws IOException + { + if (_remaining == 0) + { + return EMPTY_BYTES; + } + + byte[] bytes = new byte[_remaining]; + if ((_remaining -= Streams.readFully(_in, bytes)) != 0) + { + throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining); + } + setParentEofDetect(true); + return bytes; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/InMemoryRepresentable.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/InMemoryRepresentable.java new file mode 100644 index 000000000..0472e8893 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/InMemoryRepresentable.java @@ -0,0 +1,9 @@ +package org.spongycastle.asn1; + +import java.io.IOException; + +public interface InMemoryRepresentable +{ + ASN1Primitive getLoadedObject() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/IndefiniteLengthInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/IndefiniteLengthInputStream.java new file mode 100644 index 000000000..39e003a41 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/IndefiniteLengthInputStream.java @@ -0,0 +1,111 @@ +package org.spongycastle.asn1; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +class IndefiniteLengthInputStream + extends LimitedInputStream +{ + private int _b1; + private int _b2; + private boolean _eofReached = false; + private boolean _eofOn00 = true; + + IndefiniteLengthInputStream( + InputStream in, + int limit) + throws IOException + { + super(in, limit); + + _b1 = in.read(); + _b2 = in.read(); + + if (_b2 < 0) + { + // Corrupted stream + throw new EOFException(); + } + + checkForEof(); + } + + void setEofOn00( + boolean eofOn00) + { + _eofOn00 = eofOn00; + checkForEof(); + } + + private boolean checkForEof() + { + if (!_eofReached && _eofOn00 && (_b1 == 0x00 && _b2 == 0x00)) + { + _eofReached = true; + setParentEofDetect(true); + } + return _eofReached; + } + + public int read(byte[] b, int off, int len) + throws IOException + { + // Only use this optimisation if we aren't checking for 00 + if (_eofOn00 || len < 3) + { + return super.read(b, off, len); + } + + if (_eofReached) + { + return -1; + } + + int numRead = _in.read(b, off + 2, len - 2); + + if (numRead < 0) + { + // Corrupted stream + throw new EOFException(); + } + + b[off] = (byte)_b1; + b[off + 1] = (byte)_b2; + + _b1 = _in.read(); + _b2 = _in.read(); + + if (_b2 < 0) + { + // Corrupted stream + throw new EOFException(); + } + + return numRead + 2; + } + + public int read() + throws IOException + { + if (checkForEof()) + { + return -1; + } + + int b = _in.read(); + + if (b < 0) + { + // Corrupted stream + throw new EOFException(); + } + + int v = _b1; + + _b1 = _b2; + _b2 = b; + + return v; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LazyConstructionEnumeration.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LazyConstructionEnumeration.java new file mode 100644 index 000000000..036dbc21f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LazyConstructionEnumeration.java @@ -0,0 +1,43 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +class LazyConstructionEnumeration + implements Enumeration +{ + private ASN1InputStream aIn; + private Object nextObj; + + public LazyConstructionEnumeration(byte[] encoded) + { + aIn = new ASN1InputStream(encoded, true); + nextObj = readObject(); + } + + public boolean hasMoreElements() + { + return nextObj != null; + } + + public Object nextElement() + { + Object o = nextObj; + + nextObj = readObject(); + + return o; + } + + private Object readObject() + { + try + { + return aIn.readObject(); + } + catch (IOException e) + { + throw new ASN1ParsingException("malformed DER construction: " + e, e); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LazyEncodedSequence.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LazyEncodedSequence.java new file mode 100644 index 000000000..70f13853f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LazyEncodedSequence.java @@ -0,0 +1,109 @@ +package org.spongycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * Note: this class is for processing DER/DL encoded sequences only. + */ +class LazyEncodedSequence + extends ASN1Sequence +{ + private byte[] encoded; + + LazyEncodedSequence( + byte[] encoded) + throws IOException + { + this.encoded = encoded; + } + + private void parse() + { + Enumeration en = new LazyConstructionEnumeration(encoded); + + while (en.hasMoreElements()) + { + seq.addElement(en.nextElement()); + } + + encoded = null; + } + + public synchronized ASN1Encodable getObjectAt(int index) + { + if (encoded != null) + { + parse(); + } + + return super.getObjectAt(index); + } + + public synchronized Enumeration getObjects() + { + if (encoded == null) + { + return super.getObjects(); + } + + return new LazyConstructionEnumeration(encoded); + } + + public synchronized int size() + { + if (encoded != null) + { + parse(); + } + + return super.size(); + } + + ASN1Primitive toDERObject() + { + if (encoded != null) + { + parse(); + } + + return super.toDERObject(); + } + + ASN1Primitive toDLObject() + { + if (encoded != null) + { + parse(); + } + + return super.toDLObject(); + } + + int encodedLength() + throws IOException + { + if (encoded != null) + { + return 1 + StreamUtil.calculateBodyLength(encoded.length) + encoded.length; + } + else + { + return super.toDLObject().encodedLength(); + } + } + + void encode( + ASN1OutputStream out) + throws IOException + { + if (encoded != null) + { + out.writeEncoded(BERTags.SEQUENCE | BERTags.CONSTRUCTED, encoded); + } + else + { + super.toDLObject().encode(out); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LimitedInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LimitedInputStream.java new file mode 100644 index 000000000..cc3273bf5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/LimitedInputStream.java @@ -0,0 +1,32 @@ +package org.spongycastle.asn1; + +import java.io.InputStream; + +abstract class LimitedInputStream + extends InputStream +{ + protected final InputStream _in; + private int _limit; + + LimitedInputStream( + InputStream in, + int limit) + { + this._in = in; + this._limit = limit; + } + + int getRemaining() + { + // TODO: maybe one day this can become more accurate + return _limit; + } + + protected void setParentEofDetect(boolean on) + { + if (_in instanceof IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream)_in).setEofOn00(on); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/OIDTokenizer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/OIDTokenizer.java new file mode 100644 index 000000000..e618f4b9c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/OIDTokenizer.java @@ -0,0 +1,48 @@ +package org.spongycastle.asn1; + +/** + * class for breaking up an OID into it's component tokens, ala + * java.util.StringTokenizer. We need this class as some of the + * lightweight Java environment don't support classes like + * StringTokenizer. + */ +public class OIDTokenizer +{ + private String oid; + private int index; + + public OIDTokenizer( + String oid) + { + this.oid = oid; + this.index = 0; + } + + public boolean hasMoreTokens() + { + return (index != -1); + } + + public String nextToken() + { + if (index == -1) + { + return null; + } + + String token; + int end = oid.indexOf('.', index); + + if (end == -1) + { + token = oid.substring(index); + index = -1; + return token; + } + + token = oid.substring(index, end); + + index = end + 1; + return token; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/StreamUtil.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/StreamUtil.java new file mode 100644 index 000000000..cf92c511e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/StreamUtil.java @@ -0,0 +1,114 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.FileChannel; + +class StreamUtil +{ + private static final long MAX_MEMORY = Runtime.getRuntime().maxMemory(); + + /** + * Find out possible longest length... + * + * @param in input stream of interest + * @return length calculation or MAX_VALUE. + */ + static int findLimit(InputStream in) + { + if (in instanceof LimitedInputStream) + { + return ((LimitedInputStream)in).getRemaining(); + } + else if (in instanceof ASN1InputStream) + { + return ((ASN1InputStream)in).getLimit(); + } + else if (in instanceof ByteArrayInputStream) + { + return ((ByteArrayInputStream)in).available(); + } + else if (in instanceof FileInputStream) + { + try + { + FileChannel channel = ((FileInputStream)in).getChannel(); + long size = (channel != null) ? channel.size() : Integer.MAX_VALUE; + + if (size < Integer.MAX_VALUE) + { + return (int)size; + } + } + catch (IOException e) + { + // ignore - they'll find out soon enough! + } + } + + if (MAX_MEMORY > Integer.MAX_VALUE) + { + return Integer.MAX_VALUE; + } + + return (int)MAX_MEMORY; + } + + static int calculateBodyLength( + int length) + { + int count = 1; + + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + count++; + } + } + + return count; + } + + static int calculateTagLength(int tagNo) + throws IOException + { + int length = 1; + + if (tagNo >= 31) + { + if (tagNo < 128) + { + length++; + } + else + { + byte[] stack = new byte[5]; + int pos = stack.length; + + stack[--pos] = (byte)(tagNo & 0x7F); + + do + { + tagNo >>= 7; + stack[--pos] = (byte)(tagNo & 0x7F | 0x80); + } + while (tagNo > 127); + + length += stack.length - pos; + } + } + + return length; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/bc/BCObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/bc/BCObjectIdentifiers.java new file mode 100644 index 000000000..9e0b2848d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/bc/BCObjectIdentifiers.java @@ -0,0 +1,71 @@ +package org.spongycastle.asn1.bc; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle + *
+ * 1.3.6.1.4.1.22554 + */ +public interface BCObjectIdentifiers +{ + /** + * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle + *
+ * 1.3.6.1.4.1.22554 + */ + public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554"); + + /** + * pbe(1) algorithms + *
+ * 1.3.6.1.4.1.22554.1 + */ + public static final ASN1ObjectIdentifier bc_pbe = bc.branch("1"); + + /** + * SHA-1(1) + *
+ * 1.3.6.1.4.1.22554.1.1 + */ + public static final ASN1ObjectIdentifier bc_pbe_sha1 = bc_pbe.branch("1"); + + /** SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1 */ + public static final ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1"); + /** SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2 */ + public static final ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2"); + /** SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3 */ + public static final ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3"); + /** SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4 */ + public static final ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4"); + + /** + * PKCS-5(1)|PKCS-12(2) + */ + /** SHA-1.PKCS5; 1.3.6.1.4.1.22554.1.1.1 */ + public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = bc_pbe_sha1.branch("1"); + /** SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2 */ + public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = bc_pbe_sha1.branch("2"); + + /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.1 */ + public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = bc_pbe_sha256.branch("1"); + /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2 */ + public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2"); + + /** + * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42)) + */ + /** 1.3.6.1.4.1.22554.1.1.2.1.2 */ + public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = bc_pbe_sha1_pkcs12.branch("1.2"); + /** 1.3.6.1.4.1.22554.1.1.2.1.22 */ + public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = bc_pbe_sha1_pkcs12.branch("1.22"); + /** 1.3.6.1.4.1.22554.1.1.2.1.42 */ + public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = bc_pbe_sha1_pkcs12.branch("1.42"); + + /** 1.3.6.1.4.1.22554.1.1.2.2.2 */ + public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2"); + /** 1.3.6.1.4.1.22554.1.1.2.2.22 */ + public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22"); + /** 1.3.6.1.4.1.22554.1.1.2.2.42 */ + public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CAKeyUpdAnnContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CAKeyUpdAnnContent.java new file mode 100644 index 000000000..ddc4c2521 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CAKeyUpdAnnContent.java @@ -0,0 +1,80 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class CAKeyUpdAnnContent + extends ASN1Object +{ + private CMPCertificate oldWithNew; + private CMPCertificate newWithOld; + private CMPCertificate newWithNew; + + private CAKeyUpdAnnContent(ASN1Sequence seq) + { + oldWithNew = CMPCertificate.getInstance(seq.getObjectAt(0)); + newWithOld = CMPCertificate.getInstance(seq.getObjectAt(1)); + newWithNew = CMPCertificate.getInstance(seq.getObjectAt(2)); + } + + public static CAKeyUpdAnnContent getInstance(Object o) + { + if (o instanceof CAKeyUpdAnnContent) + { + return (CAKeyUpdAnnContent)o; + } + + if (o != null) + { + return new CAKeyUpdAnnContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CAKeyUpdAnnContent(CMPCertificate oldWithNew, CMPCertificate newWithOld, CMPCertificate newWithNew) + { + this.oldWithNew = oldWithNew; + this.newWithOld = newWithOld; + this.newWithNew = newWithNew; + } + + public CMPCertificate getOldWithNew() + { + return oldWithNew; + } + + public CMPCertificate getNewWithOld() + { + return newWithOld; + } + + public CMPCertificate getNewWithNew() + { + return newWithNew; + } + + /** + *
+ * CAKeyUpdAnnContent ::= SEQUENCE { + * oldWithNew CMPCertificate, -- old pub signed with new priv + * newWithOld CMPCertificate, -- new pub signed with old priv + * newWithNew CMPCertificate -- new pub signed with new priv + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(oldWithNew); + v.add(newWithOld); + v.add(newWithNew); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CMPCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CMPCertificate.java new file mode 100644 index 000000000..22dcb3b82 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CMPCertificate.java @@ -0,0 +1,92 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AttributeCertificate; +import org.spongycastle.asn1.x509.Certificate; + +public class CMPCertificate + extends ASN1Object + implements ASN1Choice +{ + private Certificate x509v3PKCert; + private AttributeCertificate x509v2AttrCert; + + /** + * Note: the addition of attribute certificates is a BC extension. + */ + public CMPCertificate(AttributeCertificate x509v2AttrCert) + { + this.x509v2AttrCert = x509v2AttrCert; + } + + public CMPCertificate(Certificate x509v3PKCert) + { + if (x509v3PKCert.getVersionNumber() != 3) + { + throw new IllegalArgumentException("only version 3 certificates allowed"); + } + + this.x509v3PKCert = x509v3PKCert; + } + + public static CMPCertificate getInstance(Object o) + { + if (o == null || o instanceof CMPCertificate) + { + return (CMPCertificate)o; + } + + if (o instanceof ASN1Sequence || o instanceof byte[]) + { + return new CMPCertificate(Certificate.getInstance(o)); + } + + if (o instanceof ASN1TaggedObject) + { + return new CMPCertificate(AttributeCertificate.getInstance(((ASN1TaggedObject)o).getObject())); + } + + throw new IllegalArgumentException("Invalid object: " + o.getClass().getName()); + } + + public boolean isX509v3PKCert() + { + return x509v3PKCert != null; + } + + public Certificate getX509v3PKCert() + { + return x509v3PKCert; + } + + public AttributeCertificate getX509v2AttrCert() + { + return x509v2AttrCert; + } + + /** + *
+ * CMPCertificate ::= CHOICE { + * x509v3PKCert Certificate + * x509v2AttrCert [1] AttributeCertificate + * } + *+ * Note: the addition of attribute certificates is a BC extension. + * + * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + if (x509v2AttrCert != null) + { // explicit following CMP conventions + return new DERTaggedObject(true, 1, x509v2AttrCert); + } + + return x509v3PKCert.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CMPObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CMPObjectIdentifiers.java new file mode 100644 index 000000000..c7cbc5a55 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CMPObjectIdentifiers.java @@ -0,0 +1,141 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public interface CMPObjectIdentifiers +{ + // RFC 4210 + + /** id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13} */ + static final ASN1ObjectIdentifier passwordBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13"); + + /** id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30} */ + static final ASN1ObjectIdentifier dhBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.30"); + + // Example InfoTypeAndValue contents include, but are not limited + // to, the following (un-comment in this ASN.1 module and use as + // appropriate for a given environment): + // + // id-it-caProtEncCert OBJECT IDENTIFIER ::= {id-it 1} + // CAProtEncCertValue ::= CMPCertificate + // id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2} + // SignKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier + // id-it-encKeyPairTypes OBJECT IDENTIFIER ::= {id-it 3} + // EncKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier + // id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4} + // PreferredSymmAlgValue ::= AlgorithmIdentifier + // id-it-caKeyUpdateInfo OBJECT IDENTIFIER ::= {id-it 5} + // CAKeyUpdateInfoValue ::= CAKeyUpdAnnContent + // id-it-currentCRL OBJECT IDENTIFIER ::= {id-it 6} + // CurrentCRLValue ::= CertificateList + // id-it-unsupportedOIDs OBJECT IDENTIFIER ::= {id-it 7} + // UnsupportedOIDsValue ::= SEQUENCE OF OBJECT IDENTIFIER + // id-it-keyPairParamReq OBJECT IDENTIFIER ::= {id-it 10} + // KeyPairParamReqValue ::= OBJECT IDENTIFIER + // id-it-keyPairParamRep OBJECT IDENTIFIER ::= {id-it 11} + // KeyPairParamRepValue ::= AlgorithmIdentifer + // id-it-revPassphrase OBJECT IDENTIFIER ::= {id-it 12} + // RevPassphraseValue ::= EncryptedValue + // id-it-implicitConfirm OBJECT IDENTIFIER ::= {id-it 13} + // ImplicitConfirmValue ::= NULL + // id-it-confirmWaitTime OBJECT IDENTIFIER ::= {id-it 14} + // ConfirmWaitTimeValue ::= GeneralizedTime + // id-it-origPKIMessage OBJECT IDENTIFIER ::= {id-it 15} + // OrigPKIMessageValue ::= PKIMessages + // id-it-suppLangTags OBJECT IDENTIFIER ::= {id-it 16} + // SuppLangTagsValue ::= SEQUENCE OF UTF8String + // + // where + // + // id-pkix OBJECT IDENTIFIER ::= { + // iso(1) identified-organization(3) + // dod(6) internet(1) security(5) mechanisms(5) pkix(7)} + // and + // id-it OBJECT IDENTIFIER ::= {id-pkix 4} + + /** RFC 4120: it-id: PKIX.4 = 1.3.6.1.5.5.7.4 */ + + /** RFC 4120: 1.3.6.1.5.5.7.4.1 */ + static final ASN1ObjectIdentifier it_caProtEncCert = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.1"); + /** RFC 4120: 1.3.6.1.5.5.7.4.2 */ + static final ASN1ObjectIdentifier it_signKeyPairTypes = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.2"); + /** RFC 4120: 1.3.6.1.5.5.7.4.3 */ + static final ASN1ObjectIdentifier it_encKeyPairTypes = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.3"); + /** RFC 4120: 1.3.6.1.5.5.7.4.4 */ + static final ASN1ObjectIdentifier it_preferredSymAlg = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.4"); + /** RFC 4120: 1.3.6.1.5.5.7.4.5 */ + static final ASN1ObjectIdentifier it_caKeyUpdateInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.5"); + /** RFC 4120: 1.3.6.1.5.5.7.4.6 */ + static final ASN1ObjectIdentifier it_currentCRL = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.6"); + /** RFC 4120: 1.3.6.1.5.5.7.4.7 */ + static final ASN1ObjectIdentifier it_unsupportedOIDs = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.7"); + /** RFC 4120: 1.3.6.1.5.5.7.4.10 */ + static final ASN1ObjectIdentifier it_keyPairParamReq = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.10"); + /** RFC 4120: 1.3.6.1.5.5.7.4.11 */ + static final ASN1ObjectIdentifier it_keyPairParamRep = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.11"); + /** RFC 4120: 1.3.6.1.5.5.7.4.12 */ + static final ASN1ObjectIdentifier it_revPassphrase = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.12"); + /** RFC 4120: 1.3.6.1.5.5.7.4.13 */ + static final ASN1ObjectIdentifier it_implicitConfirm = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.13"); + /** RFC 4120: 1.3.6.1.5.5.7.4.14 */ + static final ASN1ObjectIdentifier it_confirmWaitTime = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.14"); + /** RFC 4120: 1.3.6.1.5.5.7.4.15 */ + static final ASN1ObjectIdentifier it_origPKIMessage = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.15"); + /** RFC 4120: 1.3.6.1.5.5.7.4.16 */ + static final ASN1ObjectIdentifier it_suppLangTags = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.16"); + + // RFC 4211 + + // id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) + // dod(6) internet(1) security(5) mechanisms(5) pkix(7) } + // + // arc for Internet X.509 PKI protocols and their components + // id-pkip OBJECT IDENTIFIER :: { id-pkix pkip(5) } + // + // arc for Registration Controls in CRMF + // id-regCtrl OBJECT IDENTIFIER ::= { id-pkip regCtrl(1) } + // + // arc for Registration Info in CRMF + // id-regInfo OBJECT IDENTIFIER ::= { id-pkip id-regInfo(2) } + + /** RFC 4211: it-pkip: PKIX.5 = 1.3.6.1.5.5.7.5 */ + static final ASN1ObjectIdentifier id_pkip = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5"); + + /** RFC 4211: it-regCtrl: 1.3.6.1.5.5.7.5.1 */ + static final ASN1ObjectIdentifier id_regCtrl = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1"); + /** RFC 4211: it-regInfo: 1.3.6.1.5.5.7.5.2 */ + static final ASN1ObjectIdentifier id_regInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2"); + + + /** 1.3.6.1.5.5.7.5.1.1 */ + static final ASN1ObjectIdentifier regCtrl_regToken = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.1"); + /** 1.3.6.1.5.5.7.5.1.2 */ + static final ASN1ObjectIdentifier regCtrl_authenticator = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.2"); + /** 1.3.6.1.5.5.7.5.1.3 */ + static final ASN1ObjectIdentifier regCtrl_pkiPublicationInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.3"); + /** 1.3.6.1.5.5.7.5.1.4 */ + static final ASN1ObjectIdentifier regCtrl_pkiArchiveOptions = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.4"); + /** 1.3.6.1.5.5.7.5.1.5 */ + static final ASN1ObjectIdentifier regCtrl_oldCertID = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.5"); + /** 1.3.6.1.5.5.7.5.1.6 */ + static final ASN1ObjectIdentifier regCtrl_protocolEncrKey = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.6"); + + /** From RFC4210: + * id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}; 1.3.6.1.5.5.7.1.7 */ + static final ASN1ObjectIdentifier regCtrl_altCertTemplate = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.7"); + + /** RFC 4211: it-regInfo-utf8Pairs: 1.3.6.1.5.5.7.5.2.1 */ + static final ASN1ObjectIdentifier regInfo_utf8Pairs = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2.1"); + /** RFC 4211: it-regInfo-certReq: 1.3.6.1.5.5.7.5.2.1 */ + static final ASN1ObjectIdentifier regInfo_certReq = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2.2"); + + /** + * 1.2.840.113549.1.9.16.1.21 + *
+ * id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types + *
+ * id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21} + */ + static final ASN1ObjectIdentifier ct_encKeyWithID = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1.21"); + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CRLAnnContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CRLAnnContent.java new file mode 100644 index 000000000..513baeb95 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CRLAnnContent.java @@ -0,0 +1,61 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.CertificateList; + +public class CRLAnnContent + extends ASN1Object +{ + private ASN1Sequence content; + + private CRLAnnContent(ASN1Sequence seq) + { + content = seq; + } + + public static CRLAnnContent getInstance(Object o) + { + if (o instanceof CRLAnnContent) + { + return (CRLAnnContent)o; + } + + if (o != null) + { + return new CRLAnnContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CRLAnnContent(CertificateList crl) + { + this.content = new DERSequence(crl); + } + + public CertificateList[] getCertificateLists() + { + CertificateList[] result = new CertificateList[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = CertificateList.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * CRLAnnContent ::= SEQUENCE OF CertificateList + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertConfirmContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertConfirmContent.java new file mode 100644 index 000000000..974eb757d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertConfirmContent.java @@ -0,0 +1,54 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; + +public class CertConfirmContent + extends ASN1Object +{ + private ASN1Sequence content; + + private CertConfirmContent(ASN1Sequence seq) + { + content = seq; + } + + public static CertConfirmContent getInstance(Object o) + { + if (o instanceof CertConfirmContent) + { + return (CertConfirmContent)o; + } + + if (o != null) + { + return new CertConfirmContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertStatus[] toCertStatusArray() + { + CertStatus[] result = new CertStatus[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = CertStatus.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * CertConfirmContent ::= SEQUENCE OF CertStatus + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertOrEncCert.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertOrEncCert.java new file mode 100644 index 000000000..ed16a6502 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertOrEncCert.java @@ -0,0 +1,96 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.crmf.EncryptedValue; + +public class CertOrEncCert + extends ASN1Object + implements ASN1Choice +{ + private CMPCertificate certificate; + private EncryptedValue encryptedCert; + + private CertOrEncCert(ASN1TaggedObject tagged) + { + if (tagged.getTagNo() == 0) + { + certificate = CMPCertificate.getInstance(tagged.getObject()); + } + else if (tagged.getTagNo() == 1) + { + encryptedCert = EncryptedValue.getInstance(tagged.getObject()); + } + else + { + throw new IllegalArgumentException("unknown tag: " + tagged.getTagNo()); + } + } + + public static CertOrEncCert getInstance(Object o) + { + if (o instanceof CertOrEncCert) + { + return (CertOrEncCert)o; + } + + if (o instanceof ASN1TaggedObject) + { + return new CertOrEncCert((ASN1TaggedObject)o); + } + + return null; + } + + public CertOrEncCert(CMPCertificate certificate) + { + if (certificate == null) + { + throw new IllegalArgumentException("'certificate' cannot be null"); + } + + this.certificate = certificate; + } + + public CertOrEncCert(EncryptedValue encryptedCert) + { + if (encryptedCert == null) + { + throw new IllegalArgumentException("'encryptedCert' cannot be null"); + } + + this.encryptedCert = encryptedCert; + } + + public CMPCertificate getCertificate() + { + return certificate; + } + + public EncryptedValue getEncryptedCert() + { + return encryptedCert; + } + + /** + *
+ * CertOrEncCert ::= CHOICE { + * certificate [0] CMPCertificate, + * encryptedCert [1] EncryptedValue + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + if (certificate != null) + { + return new DERTaggedObject(true, 0, certificate); + } + + return new DERTaggedObject(true, 1, encryptedCert); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertRepMessage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertRepMessage.java new file mode 100644 index 000000000..3f63a4ac3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertRepMessage.java @@ -0,0 +1,123 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class CertRepMessage + extends ASN1Object +{ + private ASN1Sequence caPubs; + private ASN1Sequence response; + + private CertRepMessage(ASN1Sequence seq) + { + int index = 0; + + if (seq.size() > 1) + { + caPubs = ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(index++), true); + } + + response = ASN1Sequence.getInstance(seq.getObjectAt(index)); + } + + public static CertRepMessage getInstance(Object o) + { + if (o instanceof CertRepMessage) + { + return (CertRepMessage)o; + } + + if (o != null) + { + return new CertRepMessage(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertRepMessage(CMPCertificate[] caPubs, CertResponse[] response) + { + if (response == null) + { + throw new IllegalArgumentException("'response' cannot be null"); + } + + if (caPubs != null) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < caPubs.length; i++) + { + v.add(caPubs[i]); + } + this.caPubs = new DERSequence(v); + } + + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < response.length; i++) + { + v.add(response[i]); + } + this.response = new DERSequence(v); + } + } + + public CMPCertificate[] getCaPubs() + { + if (caPubs == null) + { + return null; + } + + CMPCertificate[] results = new CMPCertificate[caPubs.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = CMPCertificate.getInstance(caPubs.getObjectAt(i)); + } + + return results; + } + + public CertResponse[] getResponse() + { + CertResponse[] results = new CertResponse[response.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = CertResponse.getInstance(response.getObjectAt(i)); + } + + return results; + } + + /** + *
+ * CertRepMessage ::= SEQUENCE { + * caPubs [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate + * OPTIONAL, + * response SEQUENCE OF CertResponse + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (caPubs != null) + { + v.add(new DERTaggedObject(true, 1, caPubs)); + } + + v.add(response); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertResponse.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertResponse.java new file mode 100644 index 000000000..9efcff681 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertResponse.java @@ -0,0 +1,139 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class CertResponse + extends ASN1Object +{ + private ASN1Integer certReqId; + private PKIStatusInfo status; + private CertifiedKeyPair certifiedKeyPair; + private ASN1OctetString rspInfo; + + private CertResponse(ASN1Sequence seq) + { + certReqId = ASN1Integer.getInstance(seq.getObjectAt(0)); + status = PKIStatusInfo.getInstance(seq.getObjectAt(1)); + + if (seq.size() >= 3) + { + if (seq.size() == 3) + { + ASN1Encodable o = seq.getObjectAt(2); + if (o instanceof ASN1OctetString) + { + rspInfo = ASN1OctetString.getInstance(o); + } + else + { + certifiedKeyPair = CertifiedKeyPair.getInstance(o); + } + } + else + { + certifiedKeyPair = CertifiedKeyPair.getInstance(seq.getObjectAt(2)); + rspInfo = ASN1OctetString.getInstance(seq.getObjectAt(3)); + } + } + } + + public static CertResponse getInstance(Object o) + { + if (o instanceof CertResponse) + { + return (CertResponse)o; + } + + if (o != null) + { + return new CertResponse(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertResponse( + ASN1Integer certReqId, + PKIStatusInfo status) + { + this(certReqId, status, null, null); + } + + public CertResponse( + ASN1Integer certReqId, + PKIStatusInfo status, + CertifiedKeyPair certifiedKeyPair, + ASN1OctetString rspInfo) + { + if (certReqId == null) + { + throw new IllegalArgumentException("'certReqId' cannot be null"); + } + if (status == null) + { + throw new IllegalArgumentException("'status' cannot be null"); + } + this.certReqId = certReqId; + this.status = status; + this.certifiedKeyPair = certifiedKeyPair; + this.rspInfo = rspInfo; + } + + public ASN1Integer getCertReqId() + { + return certReqId; + } + + public PKIStatusInfo getStatus() + { + return status; + } + + public CertifiedKeyPair getCertifiedKeyPair() + { + return certifiedKeyPair; + } + + /** + *
+ * CertResponse ::= SEQUENCE { + * certReqId INTEGER, + * -- to match this response with corresponding request (a value + * -- of -1 is to be used if certReqId is not specified in the + * -- corresponding request) + * status PKIStatusInfo, + * certifiedKeyPair CertifiedKeyPair OPTIONAL, + * rspInfo OCTET STRING OPTIONAL + * -- analogous to the id-regInfo-utf8Pairs string defined + * -- for regInfo in CertReqMsg [CRMF] + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certReqId); + v.add(status); + + if (certifiedKeyPair != null) + { + v.add(certifiedKeyPair); + } + + if (rspInfo != null) + { + v.add(rspInfo); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertStatus.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertStatus.java new file mode 100644 index 000000000..86664eed9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertStatus.java @@ -0,0 +1,102 @@ +package org.spongycastle.asn1.cmp; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class CertStatus + extends ASN1Object +{ + private ASN1OctetString certHash; + private ASN1Integer certReqId; + private PKIStatusInfo statusInfo; + + private CertStatus(ASN1Sequence seq) + { + certHash = ASN1OctetString.getInstance(seq.getObjectAt(0)); + certReqId = ASN1Integer.getInstance(seq.getObjectAt(1)); + + if (seq.size() > 2) + { + statusInfo = PKIStatusInfo.getInstance(seq.getObjectAt(2)); + } + } + + public CertStatus(byte[] certHash, BigInteger certReqId) + { + this.certHash = new DEROctetString(certHash); + this.certReqId = new ASN1Integer(certReqId); + } + + public CertStatus(byte[] certHash, BigInteger certReqId, PKIStatusInfo statusInfo) + { + this.certHash = new DEROctetString(certHash); + this.certReqId = new ASN1Integer(certReqId); + this.statusInfo = statusInfo; + } + + public static CertStatus getInstance(Object o) + { + if (o instanceof CertStatus) + { + return (CertStatus)o; + } + + if (o != null) + { + return new CertStatus(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ASN1OctetString getCertHash() + { + return certHash; + } + + public ASN1Integer getCertReqId() + { + return certReqId; + } + + public PKIStatusInfo getStatusInfo() + { + return statusInfo; + } + + /** + *
+ * CertStatus ::= SEQUENCE { + * certHash OCTET STRING, + * -- the hash of the certificate, using the same hash algorithm + * -- as is used to create and verify the certificate signature + * certReqId INTEGER, + * -- to match this confirmation with the corresponding req/rep + * statusInfo PKIStatusInfo OPTIONAL + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certHash); + v.add(certReqId); + + if (statusInfo != null) + { + v.add(statusInfo); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertifiedKeyPair.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertifiedKeyPair.java new file mode 100644 index 000000000..500c9d034 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/CertifiedKeyPair.java @@ -0,0 +1,127 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.crmf.EncryptedValue; +import org.spongycastle.asn1.crmf.PKIPublicationInfo; + +public class CertifiedKeyPair + extends ASN1Object +{ + private CertOrEncCert certOrEncCert; + private EncryptedValue privateKey; + private PKIPublicationInfo publicationInfo; + + private CertifiedKeyPair(ASN1Sequence seq) + { + certOrEncCert = CertOrEncCert.getInstance(seq.getObjectAt(0)); + + if (seq.size() >= 2) + { + if (seq.size() == 2) + { + ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(seq.getObjectAt(1)); + if (tagged.getTagNo() == 0) + { + privateKey = EncryptedValue.getInstance(tagged.getObject()); + } + else + { + publicationInfo = PKIPublicationInfo.getInstance(tagged.getObject()); + } + } + else + { + privateKey = EncryptedValue.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(1))); + publicationInfo = PKIPublicationInfo.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(2))); + } + } + } + + public static CertifiedKeyPair getInstance(Object o) + { + if (o instanceof CertifiedKeyPair) + { + return (CertifiedKeyPair)o; + } + + if (o != null) + { + return new CertifiedKeyPair(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertifiedKeyPair( + CertOrEncCert certOrEncCert) + { + this(certOrEncCert, null, null); + } + + public CertifiedKeyPair( + CertOrEncCert certOrEncCert, + EncryptedValue privateKey, + PKIPublicationInfo publicationInfo + ) + { + if (certOrEncCert == null) + { + throw new IllegalArgumentException("'certOrEncCert' cannot be null"); + } + + this.certOrEncCert = certOrEncCert; + this.privateKey = privateKey; + this.publicationInfo = publicationInfo; + } + + public CertOrEncCert getCertOrEncCert() + { + return certOrEncCert; + } + + public EncryptedValue getPrivateKey() + { + return privateKey; + } + + public PKIPublicationInfo getPublicationInfo() + { + return publicationInfo; + } + + /** + *
+ * CertifiedKeyPair ::= SEQUENCE { + * certOrEncCert CertOrEncCert, + * privateKey [0] EncryptedValue OPTIONAL, + * -- see [CRMF] for comment on encoding + * publicationInfo [1] PKIPublicationInfo OPTIONAL + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certOrEncCert); + + if (privateKey != null) + { + v.add(new DERTaggedObject(true, 0, privateKey)); + } + + if (publicationInfo != null) + { + v.add(new DERTaggedObject(true, 1, publicationInfo)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/Challenge.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/Challenge.java new file mode 100644 index 000000000..ef02b28bc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/Challenge.java @@ -0,0 +1,120 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class Challenge + extends ASN1Object +{ + private AlgorithmIdentifier owf; + private ASN1OctetString witness; + private ASN1OctetString challenge; + + private Challenge(ASN1Sequence seq) + { + int index = 0; + + if (seq.size() == 3) + { + owf = AlgorithmIdentifier.getInstance(seq.getObjectAt(index++)); + } + + witness = ASN1OctetString.getInstance(seq.getObjectAt(index++)); + challenge = ASN1OctetString.getInstance(seq.getObjectAt(index)); + } + + public static Challenge getInstance(Object o) + { + if (o instanceof Challenge) + { + return (Challenge)o; + } + + if (o != null) + { + return new Challenge(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public Challenge(byte[] witness, byte[] challenge) + { + this(null, witness, challenge); + } + + public Challenge(AlgorithmIdentifier owf, byte[] witness, byte[] challenge) + { + this.owf = owf; + this.witness = new DEROctetString(witness); + this.challenge = new DEROctetString(challenge); + } + + public AlgorithmIdentifier getOwf() + { + return owf; + } + + public byte[] getWitness() + { + return witness.getOctets(); + } + + public byte[] getChallenge() + { + return challenge.getOctets(); + } + + /** + *
+ * Challenge ::= SEQUENCE { + * owf AlgorithmIdentifier OPTIONAL, + * + * -- MUST be present in the first Challenge; MAY be omitted in + * -- any subsequent Challenge in POPODecKeyChallContent (if + * -- omitted, then the owf used in the immediately preceding + * -- Challenge is to be used). + * + * witness OCTET STRING, + * -- the result of applying the one-way function (owf) to a + * -- randomly-generated INTEGER, A. [Note that a different + * -- INTEGER MUST be used for each Challenge.] + * challenge OCTET STRING + * -- the encryption (under the public key for which the cert. + * -- request is being made) of Rand, where Rand is specified as + * -- Rand ::= SEQUENCE { + * -- int INTEGER, + * -- - the randomly-generated INTEGER A (above) + * -- sender GeneralName + * -- - the sender's name (as included in PKIHeader) + * -- } + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + addOptional(v, owf); + v.add(witness); + v.add(challenge); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, ASN1Encodable obj) + { + if (obj != null) + { + v.add(obj); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/ErrorMsgContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/ErrorMsgContent.java new file mode 100644 index 000000000..e7ef57163 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/ErrorMsgContent.java @@ -0,0 +1,121 @@ +package org.spongycastle.asn1.cmp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class ErrorMsgContent + extends ASN1Object +{ + private PKIStatusInfo pkiStatusInfo; + private ASN1Integer errorCode; + private PKIFreeText errorDetails; + + private ErrorMsgContent(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + pkiStatusInfo = PKIStatusInfo.getInstance(en.nextElement()); + + while (en.hasMoreElements()) + { + Object o = en.nextElement(); + + if (o instanceof ASN1Integer) + { + errorCode = ASN1Integer.getInstance(o); + } + else + { + errorDetails = PKIFreeText.getInstance(o); + } + } + } + + public static ErrorMsgContent getInstance(Object o) + { + if (o instanceof ErrorMsgContent) + { + return (ErrorMsgContent)o; + } + + if (o != null) + { + return new ErrorMsgContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ErrorMsgContent(PKIStatusInfo pkiStatusInfo) + { + this(pkiStatusInfo, null, null); + } + + public ErrorMsgContent( + PKIStatusInfo pkiStatusInfo, + ASN1Integer errorCode, + PKIFreeText errorDetails) + { + if (pkiStatusInfo == null) + { + throw new IllegalArgumentException("'pkiStatusInfo' cannot be null"); + } + + this.pkiStatusInfo = pkiStatusInfo; + this.errorCode = errorCode; + this.errorDetails = errorDetails; + } + + public PKIStatusInfo getPKIStatusInfo() + { + return pkiStatusInfo; + } + + public ASN1Integer getErrorCode() + { + return errorCode; + } + + public PKIFreeText getErrorDetails() + { + return errorDetails; + } + + /** + *
+ * ErrorMsgContent ::= SEQUENCE { + * pKIStatusInfo PKIStatusInfo, + * errorCode INTEGER OPTIONAL, + * -- implementation-specific error codes + * errorDetails PKIFreeText OPTIONAL + * -- implementation-specific error details + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(pkiStatusInfo); + addOptional(v, errorCode); + addOptional(v, errorDetails); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, ASN1Encodable obj) + { + if (obj != null) + { + v.add(obj); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/GenMsgContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/GenMsgContent.java new file mode 100644 index 000000000..5d65e0d14 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/GenMsgContent.java @@ -0,0 +1,71 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class GenMsgContent + extends ASN1Object +{ + private ASN1Sequence content; + + private GenMsgContent(ASN1Sequence seq) + { + content = seq; + } + + public static GenMsgContent getInstance(Object o) + { + if (o instanceof GenMsgContent) + { + return (GenMsgContent)o; + } + + if (o != null) + { + return new GenMsgContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public GenMsgContent(InfoTypeAndValue itv) + { + content = new DERSequence(itv); + } + + public GenMsgContent(InfoTypeAndValue[] itv) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < itv.length; i++) + { + v.add(itv[i]); + } + content = new DERSequence(v); + } + + public InfoTypeAndValue[] toInfoTypeAndValueArray() + { + InfoTypeAndValue[] result = new InfoTypeAndValue[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = InfoTypeAndValue.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * GenMsgContent ::= SEQUENCE OF InfoTypeAndValue + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/GenRepContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/GenRepContent.java new file mode 100644 index 000000000..a6a2f4dbb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/GenRepContent.java @@ -0,0 +1,71 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class GenRepContent + extends ASN1Object +{ + private ASN1Sequence content; + + private GenRepContent(ASN1Sequence seq) + { + content = seq; + } + + public static GenRepContent getInstance(Object o) + { + if (o instanceof GenRepContent) + { + return (GenRepContent)o; + } + + if (o != null) + { + return new GenRepContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public GenRepContent(InfoTypeAndValue itv) + { + content = new DERSequence(itv); + } + + public GenRepContent(InfoTypeAndValue[] itv) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < itv.length; i++) + { + v.add(itv[i]); + } + content = new DERSequence(v); + } + + public InfoTypeAndValue[] toInfoTypeAndValueArray() + { + InfoTypeAndValue[] result = new InfoTypeAndValue[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = InfoTypeAndValue.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * GenRepContent ::= SEQUENCE OF InfoTypeAndValue + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/InfoTypeAndValue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/InfoTypeAndValue.java new file mode 100644 index 000000000..3f99d1e17 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/InfoTypeAndValue.java @@ -0,0 +1,132 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * Example InfoTypeAndValue contents include, but are not limited + * to, the following (un-comment in this ASN.1 module and use as + * appropriate for a given environment): + *
+ * id-it-caProtEncCert OBJECT IDENTIFIER ::= {id-it 1} + * CAProtEncCertValue ::= CMPCertificate + * id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2} + * SignKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier + * id-it-encKeyPairTypes OBJECT IDENTIFIER ::= {id-it 3} + * EncKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier + * id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4} + * PreferredSymmAlgValue ::= AlgorithmIdentifier + * id-it-caKeyUpdateInfo OBJECT IDENTIFIER ::= {id-it 5} + * CAKeyUpdateInfoValue ::= CAKeyUpdAnnContent + * id-it-currentCRL OBJECT IDENTIFIER ::= {id-it 6} + * CurrentCRLValue ::= CertificateList + * id-it-unsupportedOIDs OBJECT IDENTIFIER ::= {id-it 7} + * UnsupportedOIDsValue ::= SEQUENCE OF OBJECT IDENTIFIER + * id-it-keyPairParamReq OBJECT IDENTIFIER ::= {id-it 10} + * KeyPairParamReqValue ::= OBJECT IDENTIFIER + * id-it-keyPairParamRep OBJECT IDENTIFIER ::= {id-it 11} + * KeyPairParamRepValue ::= AlgorithmIdentifer + * id-it-revPassphrase OBJECT IDENTIFIER ::= {id-it 12} + * RevPassphraseValue ::= EncryptedValue + * id-it-implicitConfirm OBJECT IDENTIFIER ::= {id-it 13} + * ImplicitConfirmValue ::= NULL + * id-it-confirmWaitTime OBJECT IDENTIFIER ::= {id-it 14} + * ConfirmWaitTimeValue ::= GeneralizedTime + * id-it-origPKIMessage OBJECT IDENTIFIER ::= {id-it 15} + * OrigPKIMessageValue ::= PKIMessages + * id-it-suppLangTags OBJECT IDENTIFIER ::= {id-it 16} + * SuppLangTagsValue ::= SEQUENCE OF UTF8String + * + * where + * + * id-pkix OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) + * dod(6) internet(1) security(5) mechanisms(5) pkix(7)} + * and + * id-it OBJECT IDENTIFIER ::= {id-pkix 4} + *+ */ +public class InfoTypeAndValue + extends ASN1Object +{ + private ASN1ObjectIdentifier infoType; + private ASN1Encodable infoValue; + + private InfoTypeAndValue(ASN1Sequence seq) + { + infoType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + infoValue = (ASN1Encodable)seq.getObjectAt(1); + } + } + + public static InfoTypeAndValue getInstance(Object o) + { + if (o instanceof InfoTypeAndValue) + { + return (InfoTypeAndValue)o; + } + + if (o != null) + { + return new InfoTypeAndValue(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public InfoTypeAndValue( + ASN1ObjectIdentifier infoType) + { + this.infoType = infoType; + this.infoValue = null; + } + + public InfoTypeAndValue( + ASN1ObjectIdentifier infoType, + ASN1Encodable optionalValue) + { + this.infoType = infoType; + this.infoValue = optionalValue; + } + + public ASN1ObjectIdentifier getInfoType() + { + return infoType; + } + + public ASN1Encodable getInfoValue() + { + return infoValue; + } + + /** + *
+ * InfoTypeAndValue ::= SEQUENCE { + * infoType OBJECT IDENTIFIER, + * infoValue ANY DEFINED BY infoType OPTIONAL + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(infoType); + + if (infoValue != null) + { + v.add(infoValue); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/KeyRecRepContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/KeyRecRepContent.java new file mode 100644 index 000000000..903352e55 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/KeyRecRepContent.java @@ -0,0 +1,142 @@ +package org.spongycastle.asn1.cmp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class KeyRecRepContent + extends ASN1Object +{ + private PKIStatusInfo status; + private CMPCertificate newSigCert; + private ASN1Sequence caCerts; + private ASN1Sequence keyPairHist; + + private KeyRecRepContent(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + status = PKIStatusInfo.getInstance(en.nextElement()); + + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(en.nextElement()); + + switch (tObj.getTagNo()) + { + case 0: + newSigCert = CMPCertificate.getInstance(tObj.getObject()); + break; + case 1: + caCerts = ASN1Sequence.getInstance(tObj.getObject()); + break; + case 2: + keyPairHist = ASN1Sequence.getInstance(tObj.getObject()); + break; + default: + throw new IllegalArgumentException("unknown tag number: " + tObj.getTagNo()); + } + } + } + + public static KeyRecRepContent getInstance(Object o) + { + if (o instanceof KeyRecRepContent) + { + return (KeyRecRepContent)o; + } + + if (o != null) + { + return new KeyRecRepContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + + public PKIStatusInfo getStatus() + { + return status; + } + + public CMPCertificate getNewSigCert() + { + return newSigCert; + } + + public CMPCertificate[] getCaCerts() + { + if (caCerts == null) + { + return null; + } + + CMPCertificate[] results = new CMPCertificate[caCerts.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = CMPCertificate.getInstance(caCerts.getObjectAt(i)); + } + + return results; + } + + public CertifiedKeyPair[] getKeyPairHist() + { + if (keyPairHist == null) + { + return null; + } + + CertifiedKeyPair[] results = new CertifiedKeyPair[keyPairHist.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = CertifiedKeyPair.getInstance(keyPairHist.getObjectAt(i)); + } + + return results; + } + + /** + *
+ * KeyRecRepContent ::= SEQUENCE { + * status PKIStatusInfo, + * newSigCert [0] CMPCertificate OPTIONAL, + * caCerts [1] SEQUENCE SIZE (1..MAX) OF + * CMPCertificate OPTIONAL, + * keyPairHist [2] SEQUENCE SIZE (1..MAX) OF + * CertifiedKeyPair OPTIONAL + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(status); + + addOptional(v, 0, newSigCert); + addOptional(v, 1, caCerts); + addOptional(v, 2, keyPairHist); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(true, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/OOBCertHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/OOBCertHash.java new file mode 100644 index 000000000..dc81e7ec1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/OOBCertHash.java @@ -0,0 +1,117 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.crmf.CertId; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class OOBCertHash + extends ASN1Object +{ + private AlgorithmIdentifier hashAlg; + private CertId certId; + private DERBitString hashVal; + + private OOBCertHash(ASN1Sequence seq) + { + int index = seq.size() - 1; + + hashVal = DERBitString.getInstance(seq.getObjectAt(index--)); + + for (int i = index; i >= 0; i--) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)seq.getObjectAt(i); + + if (tObj.getTagNo() == 0) + { + hashAlg = AlgorithmIdentifier.getInstance(tObj, true); + } + else + { + certId = CertId.getInstance(tObj, true); + } + } + + } + + public static OOBCertHash getInstance(Object o) + { + if (o instanceof OOBCertHash) + { + return (OOBCertHash)o; + } + + if (o != null) + { + return new OOBCertHash(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public OOBCertHash(AlgorithmIdentifier hashAlg, CertId certId, byte[] hashVal) + { + this(hashAlg, certId, new DERBitString(hashVal)); + } + + public OOBCertHash(AlgorithmIdentifier hashAlg, CertId certId, DERBitString hashVal) + { + this.hashAlg = hashAlg; + this.certId = certId; + this.hashVal = hashVal; + } + + public AlgorithmIdentifier getHashAlg() + { + return hashAlg; + } + + public CertId getCertId() + { + return certId; + } + + public DERBitString getHashVal() + { + return hashVal; + } + + /** + *
+ * OOBCertHash ::= SEQUENCE { + * hashAlg [0] AlgorithmIdentifier OPTIONAL, + * certId [1] CertId OPTIONAL, + * hashVal BIT STRING + * -- hashVal is calculated over the DER encoding of the + * -- self-signed certificate with the identifier certID. + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + addOptional(v, 0, hashAlg); + addOptional(v, 1, certId); + + v.add(hashVal); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(true, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PBMParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PBMParameter.java new file mode 100644 index 000000000..83c443cf7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PBMParameter.java @@ -0,0 +1,117 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class PBMParameter + extends ASN1Object +{ + private ASN1OctetString salt; + private AlgorithmIdentifier owf; + private ASN1Integer iterationCount; + private AlgorithmIdentifier mac; + + private PBMParameter(ASN1Sequence seq) + { + salt = ASN1OctetString.getInstance(seq.getObjectAt(0)); + owf = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + iterationCount = ASN1Integer.getInstance(seq.getObjectAt(2)); + mac = AlgorithmIdentifier.getInstance(seq.getObjectAt(3)); + } + + public static PBMParameter getInstance(Object o) + { + if (o instanceof PBMParameter) + { + return (PBMParameter)o; + } + + if (o != null) + { + return new PBMParameter(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public PBMParameter( + byte[] salt, + AlgorithmIdentifier owf, + int iterationCount, + AlgorithmIdentifier mac) + { + this(new DEROctetString(salt), owf, + new ASN1Integer(iterationCount), mac); + } + + public PBMParameter( + ASN1OctetString salt, + AlgorithmIdentifier owf, + ASN1Integer iterationCount, + AlgorithmIdentifier mac) + { + this.salt = salt; + this.owf = owf; + this.iterationCount = iterationCount; + this.mac = mac; + } + + public ASN1OctetString getSalt() + { + return salt; + } + + public AlgorithmIdentifier getOwf() + { + return owf; + } + + public ASN1Integer getIterationCount() + { + return iterationCount; + } + + public AlgorithmIdentifier getMac() + { + return mac; + } + + /** + *
+ * PBMParameter ::= SEQUENCE { + * salt OCTET STRING, + * -- note: implementations MAY wish to limit acceptable sizes + * -- of this string to values appropriate for their environment + * -- in order to reduce the risk of denial-of-service attacks + * owf AlgorithmIdentifier, + * -- AlgId for a One-Way Function (SHA-1 recommended) + * iterationCount INTEGER, + * -- number of times the OWF is applied + * -- note: implementations MAY wish to limit acceptable sizes + * -- of this integer to values appropriate for their environment + * -- in order to reduce the risk of denial-of-service attacks + * mac AlgorithmIdentifier + * -- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11], + * } -- or HMAC [RFC2104, RFC2202]) + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(salt); + v.add(owf); + v.add(iterationCount); + v.add(mac); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIBody.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIBody.java new file mode 100644 index 000000000..95b6a11c5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIBody.java @@ -0,0 +1,194 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.crmf.CertReqMessages; +import org.spongycastle.asn1.pkcs.CertificationRequest; + +public class PKIBody + extends ASN1Object + implements ASN1Choice +{ + public static final int TYPE_INIT_REQ = 0; + public static final int TYPE_INIT_REP = 1; + public static final int TYPE_CERT_REQ = 2; + public static final int TYPE_CERT_REP = 3; + public static final int TYPE_P10_CERT_REQ = 4; + public static final int TYPE_POPO_CHALL = 5; + public static final int TYPE_POPO_REP = 6; + public static final int TYPE_KEY_UPDATE_REQ = 7; + public static final int TYPE_KEY_UPDATE_REP = 8; + public static final int TYPE_KEY_RECOVERY_REQ = 9; + public static final int TYPE_KEY_RECOVERY_REP = 10; + public static final int TYPE_REVOCATION_REQ = 11; + public static final int TYPE_REVOCATION_REP = 12; + public static final int TYPE_CROSS_CERT_REQ = 13; + public static final int TYPE_CROSS_CERT_REP = 14; + public static final int TYPE_CA_KEY_UPDATE_ANN = 15; + public static final int TYPE_CERT_ANN = 16; + public static final int TYPE_REVOCATION_ANN = 17; + public static final int TYPE_CRL_ANN = 18; + public static final int TYPE_CONFIRM = 19; + public static final int TYPE_NESTED = 20; + public static final int TYPE_GEN_MSG = 21; + public static final int TYPE_GEN_REP = 22; + public static final int TYPE_ERROR = 23; + public static final int TYPE_CERT_CONFIRM = 24; + public static final int TYPE_POLL_REQ = 25; + public static final int TYPE_POLL_REP = 26; + + private int tagNo; + private ASN1Encodable body; + + public static PKIBody getInstance(Object o) + { + if (o == null || o instanceof PKIBody) + { + return (PKIBody)o; + } + + if (o instanceof ASN1TaggedObject) + { + return new PKIBody((ASN1TaggedObject)o); + } + + throw new IllegalArgumentException("Invalid object: " + o.getClass().getName()); + } + + private PKIBody(ASN1TaggedObject tagged) + { + tagNo = tagged.getTagNo(); + body = getBodyForType(tagNo, tagged.getObject()); + } + + /** + * Creates a new PKIBody. + * @param type one of the TYPE_* constants + * @param content message content + */ + public PKIBody( + int type, + ASN1Encodable content) + { + tagNo = type; + body = getBodyForType(type, content); + } + + private static ASN1Encodable getBodyForType( + int type, + ASN1Encodable o) + { + switch (type) + { + case TYPE_INIT_REQ: + return CertReqMessages.getInstance(o); + case TYPE_INIT_REP: + return CertRepMessage.getInstance(o); + case TYPE_CERT_REQ: + return CertReqMessages.getInstance(o); + case TYPE_CERT_REP: + return CertRepMessage.getInstance(o); + case TYPE_P10_CERT_REQ: + return CertificationRequest.getInstance(o); + case TYPE_POPO_CHALL: + return POPODecKeyChallContent.getInstance(o); + case TYPE_POPO_REP: + return POPODecKeyRespContent.getInstance(o); + case TYPE_KEY_UPDATE_REQ: + return CertReqMessages.getInstance(o); + case TYPE_KEY_UPDATE_REP: + return CertRepMessage.getInstance(o); + case TYPE_KEY_RECOVERY_REQ: + return CertReqMessages.getInstance(o); + case TYPE_KEY_RECOVERY_REP: + return KeyRecRepContent.getInstance(o); + case TYPE_REVOCATION_REQ: + return RevReqContent.getInstance(o); + case TYPE_REVOCATION_REP: + return RevRepContent.getInstance(o); + case TYPE_CROSS_CERT_REQ: + return CertReqMessages.getInstance(o); + case TYPE_CROSS_CERT_REP: + return CertRepMessage.getInstance(o); + case TYPE_CA_KEY_UPDATE_ANN: + return CAKeyUpdAnnContent.getInstance(o); + case TYPE_CERT_ANN: + return CMPCertificate.getInstance(o); + case TYPE_REVOCATION_ANN: + return RevAnnContent.getInstance(o); + case TYPE_CRL_ANN: + return CRLAnnContent.getInstance(o); + case TYPE_CONFIRM: + return PKIConfirmContent.getInstance(o); + case TYPE_NESTED: + return PKIMessages.getInstance(o); + case TYPE_GEN_MSG: + return GenMsgContent.getInstance(o); + case TYPE_GEN_REP: + return GenRepContent.getInstance(o); + case TYPE_ERROR: + return ErrorMsgContent.getInstance(o); + case TYPE_CERT_CONFIRM: + return CertConfirmContent.getInstance(o); + case TYPE_POLL_REQ: + return PollReqContent.getInstance(o); + case TYPE_POLL_REP: + return PollRepContent.getInstance(o); + default: + throw new IllegalArgumentException("unknown tag number: " + type); + } + } + + public int getType() + { + return tagNo; + } + + public ASN1Encodable getContent() + { + return body; + } + + /** + *
+ * PKIBody ::= CHOICE { -- message-specific body elements + * ir [0] CertReqMessages, --Initialization Request + * ip [1] CertRepMessage, --Initialization Response + * cr [2] CertReqMessages, --Certification Request + * cp [3] CertRepMessage, --Certification Response + * p10cr [4] CertificationRequest, --imported from [PKCS10] + * popdecc [5] POPODecKeyChallContent, --pop Challenge + * popdecr [6] POPODecKeyRespContent, --pop Response + * kur [7] CertReqMessages, --Key Update Request + * kup [8] CertRepMessage, --Key Update Response + * krr [9] CertReqMessages, --Key Recovery Request + * krp [10] KeyRecRepContent, --Key Recovery Response + * rr [11] RevReqContent, --Revocation Request + * rp [12] RevRepContent, --Revocation Response + * ccr [13] CertReqMessages, --Cross-Cert. Request + * ccp [14] CertRepMessage, --Cross-Cert. Response + * ckuann [15] CAKeyUpdAnnContent, --CA Key Update Ann. + * cann [16] CertAnnContent, --Certificate Ann. + * rann [17] RevAnnContent, --Revocation Ann. + * crlann [18] CRLAnnContent, --CRL Announcement + * pkiconf [19] PKIConfirmContent, --Confirmation + * nested [20] NestedMessageContent, --Nested Message + * genm [21] GenMsgContent, --General Message + * genp [22] GenRepContent, --General Response + * error [23] ErrorMsgContent, --Error Message + * certConf [24] CertConfirmContent, --Certificate confirm + * pollReq [25] PollReqContent, --Polling request + * pollRep [26] PollRepContent --Polling response + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return new DERTaggedObject(true, tagNo, body); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIConfirmContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIConfirmContent.java new file mode 100644 index 000000000..4019779f7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIConfirmContent.java @@ -0,0 +1,48 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Null; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERNull; + +public class PKIConfirmContent + extends ASN1Object +{ + private ASN1Null val; + + private PKIConfirmContent(ASN1Null val) + { + this.val = val; + } + + public static PKIConfirmContent getInstance(Object o) + { + if (o == null || o instanceof PKIConfirmContent) + { + return (PKIConfirmContent)o; + } + + if (o instanceof ASN1Null) + { + return new PKIConfirmContent((ASN1Null)o); + } + + throw new IllegalArgumentException("Invalid object: " + o.getClass().getName()); + } + + public PKIConfirmContent() + { + val = DERNull.INSTANCE; + } + + /** + *
+ * PKIConfirmContent ::= NULL + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return val; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIFailureInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIFailureInfo.java new file mode 100644 index 000000000..03dd5574e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIFailureInfo.java @@ -0,0 +1,126 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.DERBitString; + +/** + *
+ * PKIFailureInfo ::= BIT STRING { + * badAlg (0), + * -- unrecognized or unsupported Algorithm Identifier + * badMessageCheck (1), -- integrity check failed (e.g., signature did not verify) + * badRequest (2), + * -- transaction not permitted or supported + * badTime (3), -- messageTime was not sufficiently close to the system time, as defined by local policy + * badCertId (4), -- no certificate could be found matching the provided criteria + * badDataFormat (5), + * -- the data submitted has the wrong format + * wrongAuthority (6), -- the authority indicated in the request is different from the one creating the response token + * incorrectData (7), -- the requester's data is incorrect (for notary services) + * missingTimeStamp (8), -- when the timestamp is missing but should be there (by policy) + * badPOP (9) -- the proof-of-possession failed + * certRevoked (10), + * certConfirmed (11), + * wrongIntegrity (12), + * badRecipientNonce (13), + * timeNotAvailable (14), + * -- the TSA's time source is not available + * unacceptedPolicy (15), + * -- the requested TSA policy is not supported by the TSA + * unacceptedExtension (16), + * -- the requested extension is not supported by the TSA + * addInfoNotAvailable (17) + * -- the additional information requested could not be understood + * -- or is not available + * badSenderNonce (18), + * badCertTemplate (19), + * signerNotTrusted (20), + * transactionIdInUse (21), + * unsupportedVersion (22), + * notAuthorized (23), + * systemUnavail (24), + * systemFailure (25), + * -- the request cannot be handled due to system failure + * duplicateCertReq (26) + *+ */ +public class PKIFailureInfo + extends DERBitString +{ + public static final int badAlg = (1 << 7); // unrecognized or unsupported Algorithm Identifier + public static final int badMessageCheck = (1 << 6); // integrity check failed (e.g., signature did not verify) + public static final int badRequest = (1 << 5); + public static final int badTime = (1 << 4); // -- messageTime was not sufficiently close to the system time, as defined by local policy + public static final int badCertId = (1 << 3); // no certificate could be found matching the provided criteria + public static final int badDataFormat = (1 << 2); + public static final int wrongAuthority = (1 << 1); // the authority indicated in the request is different from the one creating the response token + public static final int incorrectData = 1; // the requester's data is incorrect (for notary services) + public static final int missingTimeStamp = (1 << 15); // when the timestamp is missing but should be there (by policy) + public static final int badPOP = (1 << 14); // the proof-of-possession failed + public static final int certRevoked = (1 << 13); + public static final int certConfirmed = (1 << 12); + public static final int wrongIntegrity = (1 << 11); + public static final int badRecipientNonce = (1 << 10); + public static final int timeNotAvailable = (1 << 9); // the TSA's time source is not available + public static final int unacceptedPolicy = (1 << 8); // the requested TSA policy is not supported by the TSA + public static final int unacceptedExtension = (1 << 23); //the requested extension is not supported by the TSA + public static final int addInfoNotAvailable = (1 << 22); //the additional information requested could not be understood or is not available + public static final int badSenderNonce = (1 << 21); + public static final int badCertTemplate = (1 << 20); + public static final int signerNotTrusted = (1 << 19); + public static final int transactionIdInUse = (1 << 18); + public static final int unsupportedVersion = (1 << 17); + public static final int notAuthorized = (1 << 16); + public static final int systemUnavail = (1 << 31); + public static final int systemFailure = (1 << 30); //the request cannot be handled due to system failure + public static final int duplicateCertReq = (1 << 29); + + /** @deprecated use lower case version */ + public static final int BAD_ALG = badAlg; // unrecognized or unsupported Algorithm Identifier + /** @deprecated use lower case version */ + public static final int BAD_MESSAGE_CHECK = badMessageCheck; + /** @deprecated use lower case version */ + public static final int BAD_REQUEST = badRequest; // transaction not permitted or supported + /** @deprecated use lower case version */ + public static final int BAD_TIME = badTime; + /** @deprecated use lower case version */ + public static final int BAD_CERT_ID = badCertId; + /** @deprecated use lower case version */ + public static final int BAD_DATA_FORMAT = badDataFormat; // the data submitted has the wrong format + /** @deprecated use lower case version */ + public static final int WRONG_AUTHORITY = wrongAuthority; + /** @deprecated use lower case version */ + public static final int INCORRECT_DATA = incorrectData; + /** @deprecated use lower case version */ + public static final int MISSING_TIME_STAMP = missingTimeStamp; + /** @deprecated use lower case version */ + public static final int BAD_POP = badPOP; + /** @deprecated use lower case version */ + public static final int TIME_NOT_AVAILABLE = timeNotAvailable; + /** @deprecated use lower case version */ + public static final int UNACCEPTED_POLICY = unacceptedPolicy; + /** @deprecated use lower case version */ + public static final int UNACCEPTED_EXTENSION = unacceptedExtension; + /** @deprecated use lower case version */ + public static final int ADD_INFO_NOT_AVAILABLE = addInfoNotAvailable; + /** @deprecated use lower case version */ + public static final int SYSTEM_FAILURE = systemFailure; + /** + * Basic constructor. + */ + public PKIFailureInfo( + int info) + { + super(getBytes(info), getPadBits(info)); + } + + public PKIFailureInfo( + DERBitString info) + { + super(info.getBytes(), info.getPadBits()); + } + + public String toString() + { + return "PKIFailureInfo: 0x" + Integer.toHexString(this.intValue()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIFreeText.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIFreeText.java new file mode 100644 index 000000000..8a10714fb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIFreeText.java @@ -0,0 +1,115 @@ +package org.spongycastle.asn1.cmp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERUTF8String; + +public class PKIFreeText + extends ASN1Object +{ + ASN1Sequence strings; + + public static PKIFreeText getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static PKIFreeText getInstance( + Object obj) + { + if (obj instanceof PKIFreeText) + { + return (PKIFreeText)obj; + } + else if (obj != null) + { + return new PKIFreeText(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private PKIFreeText( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + while (e.hasMoreElements()) + { + if (!(e.nextElement() instanceof DERUTF8String)) + { + throw new IllegalArgumentException("attempt to insert non UTF8 STRING into PKIFreeText"); + } + } + + strings = seq; + } + + public PKIFreeText( + DERUTF8String p) + { + strings = new DERSequence(p); + } + + public PKIFreeText( + String p) + { + this(new DERUTF8String(p)); + } + + public PKIFreeText( + DERUTF8String[] strs) + { + strings = new DERSequence(strs); + } + + public PKIFreeText( + String[] strs) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < strs.length; i++) + { + v.add(new DERUTF8String(strs[i])); + } + strings = new DERSequence(v); + } + + /** + * Return the number of string elements present. + * + * @return number of elements present. + */ + public int size() + { + return strings.size(); + } + + /** + * Return the UTF8STRING at index i. + * + * @param i index of the string of interest + * @return the string at index i. + */ + public DERUTF8String getStringAt( + int i) + { + return (DERUTF8String)strings.getObjectAt(i); + } + + /** + *
+ * PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String + *+ */ + public ASN1Primitive toASN1Primitive() + { + return strings; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIHeader.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIHeader.java new file mode 100644 index 000000000..69687e4f1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIHeader.java @@ -0,0 +1,260 @@ +package org.spongycastle.asn1.cmp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.GeneralName; + +public class PKIHeader + extends ASN1Object +{ + /** + * Value for a "null" recipient or sender. + */ + public static final GeneralName NULL_NAME = new GeneralName(X500Name.getInstance(new DERSequence())); + + public static final int CMP_1999 = 1; + public static final int CMP_2000 = 2; + + private ASN1Integer pvno; + private GeneralName sender; + private GeneralName recipient; + private DERGeneralizedTime messageTime; + private AlgorithmIdentifier protectionAlg; + private ASN1OctetString senderKID; // KeyIdentifier + private ASN1OctetString recipKID; // KeyIdentifier + private ASN1OctetString transactionID; + private ASN1OctetString senderNonce; + private ASN1OctetString recipNonce; + private PKIFreeText freeText; + private ASN1Sequence generalInfo; + + private PKIHeader(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + pvno = ASN1Integer.getInstance(en.nextElement()); + sender = GeneralName.getInstance(en.nextElement()); + recipient = GeneralName.getInstance(en.nextElement()); + + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement(); + + switch (tObj.getTagNo()) + { + case 0: + messageTime = DERGeneralizedTime.getInstance(tObj, true); + break; + case 1: + protectionAlg = AlgorithmIdentifier.getInstance(tObj, true); + break; + case 2: + senderKID = ASN1OctetString.getInstance(tObj, true); + break; + case 3: + recipKID = ASN1OctetString.getInstance(tObj, true); + break; + case 4: + transactionID = ASN1OctetString.getInstance(tObj, true); + break; + case 5: + senderNonce = ASN1OctetString.getInstance(tObj, true); + break; + case 6: + recipNonce = ASN1OctetString.getInstance(tObj, true); + break; + case 7: + freeText = PKIFreeText.getInstance(tObj, true); + break; + case 8: + generalInfo = ASN1Sequence.getInstance(tObj, true); + break; + default: + throw new IllegalArgumentException("unknown tag number: " + tObj.getTagNo()); + } + } + } + + public static PKIHeader getInstance(Object o) + { + if (o instanceof PKIHeader) + { + return (PKIHeader)o; + } + + if (o != null) + { + return new PKIHeader(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public PKIHeader( + int pvno, + GeneralName sender, + GeneralName recipient) + { + this(new ASN1Integer(pvno), sender, recipient); + } + + private PKIHeader( + ASN1Integer pvno, + GeneralName sender, + GeneralName recipient) + { + this.pvno = pvno; + this.sender = sender; + this.recipient = recipient; + } + + public ASN1Integer getPvno() + { + return pvno; + } + + public GeneralName getSender() + { + return sender; + } + + public GeneralName getRecipient() + { + return recipient; + } + + public DERGeneralizedTime getMessageTime() + { + return messageTime; + } + + public AlgorithmIdentifier getProtectionAlg() + { + return protectionAlg; + } + + public ASN1OctetString getSenderKID() + { + return senderKID; + } + + public ASN1OctetString getRecipKID() + { + return recipKID; + } + + public ASN1OctetString getTransactionID() + { + return transactionID; + } + + public ASN1OctetString getSenderNonce() + { + return senderNonce; + } + + public ASN1OctetString getRecipNonce() + { + return recipNonce; + } + + public PKIFreeText getFreeText() + { + return freeText; + } + + public InfoTypeAndValue[] getGeneralInfo() + { + if (generalInfo == null) + { + return null; + } + InfoTypeAndValue[] results = new InfoTypeAndValue[generalInfo.size()]; + for (int i = 0; i < results.length; i++) + { + results[i] + = InfoTypeAndValue.getInstance(generalInfo.getObjectAt(i)); + } + return results; + } + + /** + *
+ * PKIHeader ::= SEQUENCE { + * pvno INTEGER { cmp1999(1), cmp2000(2) }, + * sender GeneralName, + * -- identifies the sender + * recipient GeneralName, + * -- identifies the intended recipient + * messageTime [0] GeneralizedTime OPTIONAL, + * -- time of production of this message (used when sender + * -- believes that the transport will be "suitable"; i.e., + * -- that the time will still be meaningful upon receipt) + * protectionAlg [1] AlgorithmIdentifier OPTIONAL, + * -- algorithm used for calculation of protection bits + * senderKID [2] KeyIdentifier OPTIONAL, + * recipKID [3] KeyIdentifier OPTIONAL, + * -- to identify specific keys used for protection + * transactionID [4] OCTET STRING OPTIONAL, + * -- identifies the transaction; i.e., this will be the same in + * -- corresponding request, response, certConf, and PKIConf + * -- messages + * senderNonce [5] OCTET STRING OPTIONAL, + * recipNonce [6] OCTET STRING OPTIONAL, + * -- nonces used to provide replay protection, senderNonce + * -- is inserted by the creator of this message; recipNonce + * -- is a nonce previously inserted in a related message by + * -- the intended recipient of this message + * freeText [7] PKIFreeText OPTIONAL, + * -- this may be used to indicate context-specific instructions + * -- (this field is intended for human consumption) + * generalInfo [8] SEQUENCE SIZE (1..MAX) OF + * InfoTypeAndValue OPTIONAL + * -- this may be used to convey context-specific information + * -- (this field not primarily intended for human consumption) + * } + *+ * + * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(pvno); + v.add(sender); + v.add(recipient); + addOptional(v, 0, messageTime); + addOptional(v, 1, protectionAlg); + addOptional(v, 2, senderKID); + addOptional(v, 3, recipKID); + addOptional(v, 4, transactionID); + addOptional(v, 5, senderNonce); + addOptional(v, 6, recipNonce); + addOptional(v, 7, freeText); + addOptional(v, 8, generalInfo); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(true, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIHeaderBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIHeaderBuilder.java new file mode 100644 index 000000000..6532d91f8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIHeaderBuilder.java @@ -0,0 +1,254 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.GeneralName; + +public class PKIHeaderBuilder +{ + private ASN1Integer pvno; + private GeneralName sender; + private GeneralName recipient; + private ASN1GeneralizedTime messageTime; + private AlgorithmIdentifier protectionAlg; + private ASN1OctetString senderKID; // KeyIdentifier + private ASN1OctetString recipKID; // KeyIdentifier + private ASN1OctetString transactionID; + private ASN1OctetString senderNonce; + private ASN1OctetString recipNonce; + private PKIFreeText freeText; + private ASN1Sequence generalInfo; + + public PKIHeaderBuilder( + int pvno, + GeneralName sender, + GeneralName recipient) + { + this(new ASN1Integer(pvno), sender, recipient); + } + + private PKIHeaderBuilder( + ASN1Integer pvno, + GeneralName sender, + GeneralName recipient) + { + this.pvno = pvno; + this.sender = sender; + this.recipient = recipient; + } + + /** + * @deprecated use ASN1GeneralizedTime + */ + public PKIHeaderBuilder setMessageTime(DERGeneralizedTime time) + { + messageTime = ASN1GeneralizedTime.getInstance(time); + + return this; + } + + public PKIHeaderBuilder setMessageTime(ASN1GeneralizedTime time) + { + messageTime = time; + + return this; + } + + public PKIHeaderBuilder setProtectionAlg(AlgorithmIdentifier aid) + { + protectionAlg = aid; + + return this; + } + + public PKIHeaderBuilder setSenderKID(byte[] kid) + { + return setSenderKID(kid == null ? null : new DEROctetString(kid)); + } + + public PKIHeaderBuilder setSenderKID(ASN1OctetString kid) + { + senderKID = kid; + + return this; + } + + public PKIHeaderBuilder setRecipKID(byte[] kid) + { + return setRecipKID(kid == null ? null : new DEROctetString(kid)); + } + + public PKIHeaderBuilder setRecipKID(DEROctetString kid) + { + recipKID = kid; + + return this; + } + + public PKIHeaderBuilder setTransactionID(byte[] tid) + { + return setTransactionID(tid == null ? null : new DEROctetString(tid)); + } + + public PKIHeaderBuilder setTransactionID(ASN1OctetString tid) + { + transactionID = tid; + + return this; + } + + public PKIHeaderBuilder setSenderNonce(byte[] nonce) + { + return setSenderNonce(nonce == null ? null : new DEROctetString(nonce)); + } + + public PKIHeaderBuilder setSenderNonce(ASN1OctetString nonce) + { + senderNonce = nonce; + + return this; + } + + public PKIHeaderBuilder setRecipNonce(byte[] nonce) + { + return setRecipNonce(nonce == null ? null : new DEROctetString(nonce)); + } + + public PKIHeaderBuilder setRecipNonce(ASN1OctetString nonce) + { + recipNonce = nonce; + + return this; + } + + public PKIHeaderBuilder setFreeText(PKIFreeText text) + { + freeText = text; + + return this; + } + + public PKIHeaderBuilder setGeneralInfo(InfoTypeAndValue genInfo) + { + return setGeneralInfo(makeGeneralInfoSeq(genInfo)); + } + + public PKIHeaderBuilder setGeneralInfo(InfoTypeAndValue[] genInfos) + { + return setGeneralInfo(makeGeneralInfoSeq(genInfos)); + } + + public PKIHeaderBuilder setGeneralInfo(ASN1Sequence seqOfInfoTypeAndValue) + { + generalInfo = seqOfInfoTypeAndValue; + + return this; + } + + private static ASN1Sequence makeGeneralInfoSeq( + InfoTypeAndValue generalInfo) + { + return new DERSequence(generalInfo); + } + + private static ASN1Sequence makeGeneralInfoSeq( + InfoTypeAndValue[] generalInfos) + { + ASN1Sequence genInfoSeq = null; + if (generalInfos != null) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < generalInfos.length; i++) + { + v.add(generalInfos[i]); + } + genInfoSeq = new DERSequence(v); + } + return genInfoSeq; + } + + /** + *
+ * PKIHeader ::= SEQUENCE { + * pvno INTEGER { cmp1999(1), cmp2000(2) }, + * sender GeneralName, + * -- identifies the sender + * recipient GeneralName, + * -- identifies the intended recipient + * messageTime [0] GeneralizedTime OPTIONAL, + * -- time of production of this message (used when sender + * -- believes that the transport will be "suitable"; i.e., + * -- that the time will still be meaningful upon receipt) + * protectionAlg [1] AlgorithmIdentifier OPTIONAL, + * -- algorithm used for calculation of protection bits + * senderKID [2] KeyIdentifier OPTIONAL, + * recipKID [3] KeyIdentifier OPTIONAL, + * -- to identify specific keys used for protection + * transactionID [4] OCTET STRING OPTIONAL, + * -- identifies the transaction; i.e., this will be the same in + * -- corresponding request, response, certConf, and PKIConf + * -- messages + * senderNonce [5] OCTET STRING OPTIONAL, + * recipNonce [6] OCTET STRING OPTIONAL, + * -- nonces used to provide replay protection, senderNonce + * -- is inserted by the creator of this message; recipNonce + * -- is a nonce previously inserted in a related message by + * -- the intended recipient of this message + * freeText [7] PKIFreeText OPTIONAL, + * -- this may be used to indicate context-specific instructions + * -- (this field is intended for human consumption) + * generalInfo [8] SEQUENCE SIZE (1..MAX) OF + * InfoTypeAndValue OPTIONAL + * -- this may be used to convey context-specific information + * -- (this field not primarily intended for human consumption) + * } + *+ * @return a basic ASN.1 object representation. + */ + public PKIHeader build() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(pvno); + v.add(sender); + v.add(recipient); + addOptional(v, 0, messageTime); + addOptional(v, 1, protectionAlg); + addOptional(v, 2, senderKID); + addOptional(v, 3, recipKID); + addOptional(v, 4, transactionID); + addOptional(v, 5, senderNonce); + addOptional(v, 6, recipNonce); + addOptional(v, 7, freeText); + addOptional(v, 8, generalInfo); + + messageTime = null; + protectionAlg = null; + senderKID = null; + recipKID = null; + transactionID = null; + senderNonce = null; + recipNonce = null; + freeText = null; + generalInfo = null; + + return PKIHeader.getInstance(new DERSequence(v)); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(true, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIMessage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIMessage.java new file mode 100644 index 000000000..fbb4dfe7f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIMessage.java @@ -0,0 +1,166 @@ +package org.spongycastle.asn1.cmp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class PKIMessage + extends ASN1Object +{ + private PKIHeader header; + private PKIBody body; + private DERBitString protection; + private ASN1Sequence extraCerts; + + private PKIMessage(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + header = PKIHeader.getInstance(en.nextElement()); + body = PKIBody.getInstance(en.nextElement()); + + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement(); + + if (tObj.getTagNo() == 0) + { + protection = DERBitString.getInstance(tObj, true); + } + else + { + extraCerts = ASN1Sequence.getInstance(tObj, true); + } + } + } + + public static PKIMessage getInstance(Object o) + { + if (o instanceof PKIMessage) + { + return (PKIMessage)o; + } + else if (o != null) + { + return new PKIMessage(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * Creates a new PKIMessage. + * + * @param header message header + * @param body message body + * @param protection message protection (may be null) + * @param extraCerts extra certificates (may be null) + */ + public PKIMessage( + PKIHeader header, + PKIBody body, + DERBitString protection, + CMPCertificate[] extraCerts) + { + this.header = header; + this.body = body; + this.protection = protection; + if (extraCerts != null) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < extraCerts.length; i++) + { + v.add(extraCerts[i]); + } + this.extraCerts = new DERSequence(v); + } + } + + public PKIMessage( + PKIHeader header, + PKIBody body, + DERBitString protection) + { + this(header, body, protection, null); + } + + public PKIMessage( + PKIHeader header, + PKIBody body) + { + this(header, body, null, null); + } + + public PKIHeader getHeader() + { + return header; + } + + public PKIBody getBody() + { + return body; + } + + public DERBitString getProtection() + { + return protection; + } + + public CMPCertificate[] getExtraCerts() + { + if (extraCerts == null) + { + return null; + } + + CMPCertificate[] results = new CMPCertificate[extraCerts.size()]; + + for (int i = 0; i < results.length; i++) + { + results[i] = CMPCertificate.getInstance(extraCerts.getObjectAt(i)); + } + return results; + } + + /** + *
+ * PKIMessage ::= SEQUENCE { + * header PKIHeader, + * body PKIBody, + * protection [0] PKIProtection OPTIONAL, + * extraCerts [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate + * OPTIONAL + * } + *+ * + * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(header); + v.add(body); + + addOptional(v, 0, protection); + addOptional(v, 1, extraCerts); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(true, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIMessages.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIMessages.java new file mode 100644 index 000000000..33b47323c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIMessages.java @@ -0,0 +1,71 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class PKIMessages + extends ASN1Object +{ + private ASN1Sequence content; + + private PKIMessages(ASN1Sequence seq) + { + content = seq; + } + + public static PKIMessages getInstance(Object o) + { + if (o instanceof PKIMessages) + { + return (PKIMessages)o; + } + + if (o != null) + { + return new PKIMessages(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public PKIMessages(PKIMessage msg) + { + content = new DERSequence(msg); + } + + public PKIMessages(PKIMessage[] msgs) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < msgs.length; i++) + { + v.add(msgs[i]); + } + content = new DERSequence(v); + } + + public PKIMessage[] toPKIMessageArray() + { + PKIMessage[] result = new PKIMessage[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = PKIMessage.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * PKIMessages ::= SEQUENCE SIZE (1..MAX) OF PKIMessage + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIStatus.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIStatus.java new file mode 100644 index 000000000..af4235f1a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIStatus.java @@ -0,0 +1,64 @@ +package org.spongycastle.asn1.cmp; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; + +public class PKIStatus + extends ASN1Object +{ + public static final int GRANTED = 0; + public static final int GRANTED_WITH_MODS = 1; + public static final int REJECTION = 2; + public static final int WAITING = 3; + public static final int REVOCATION_WARNING = 4; + public static final int REVOCATION_NOTIFICATION = 5; + public static final int KEY_UPDATE_WARNING = 6; + + public static final PKIStatus granted = new PKIStatus(GRANTED); + public static final PKIStatus grantedWithMods = new PKIStatus(GRANTED_WITH_MODS); + public static final PKIStatus rejection = new PKIStatus(REJECTION); + public static final PKIStatus waiting = new PKIStatus(WAITING); + public static final PKIStatus revocationWarning = new PKIStatus(REVOCATION_WARNING); + public static final PKIStatus revocationNotification = new PKIStatus(REVOCATION_NOTIFICATION); + public static final PKIStatus keyUpdateWaiting = new PKIStatus(KEY_UPDATE_WARNING); + + private ASN1Integer value; + + private PKIStatus(int value) + { + this(new ASN1Integer(value)); + } + + private PKIStatus(ASN1Integer value) + { + this.value = value; + } + + public static PKIStatus getInstance(Object o) + { + if (o instanceof PKIStatus) + { + return (PKIStatus)o; + } + + if (o != null) + { + return new PKIStatus(ASN1Integer.getInstance(o)); + } + + return null; + } + + public BigInteger getValue() + { + return value.getValue(); + } + + public ASN1Primitive toASN1Primitive() + { + return value; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIStatusInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIStatusInfo.java new file mode 100644 index 000000000..5deae95f4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PKIStatusInfo.java @@ -0,0 +1,165 @@ +package org.spongycastle.asn1.cmp; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; + +public class PKIStatusInfo + extends ASN1Object +{ + ASN1Integer status; + PKIFreeText statusString; + DERBitString failInfo; + + public static PKIStatusInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static PKIStatusInfo getInstance( + Object obj) + { + if (obj instanceof PKIStatusInfo) + { + return (PKIStatusInfo)obj; + } + else if (obj != null) + { + return new PKIStatusInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private PKIStatusInfo( + ASN1Sequence seq) + { + this.status = ASN1Integer.getInstance(seq.getObjectAt(0)); + + this.statusString = null; + this.failInfo = null; + + if (seq.size() > 2) + { + this.statusString = PKIFreeText.getInstance(seq.getObjectAt(1)); + this.failInfo = DERBitString.getInstance(seq.getObjectAt(2)); + } + else if (seq.size() > 1) + { + Object obj = seq.getObjectAt(1); + if (obj instanceof DERBitString) + { + this.failInfo = DERBitString.getInstance(obj); + } + else + { + this.statusString = PKIFreeText.getInstance(obj); + } + } + } + + /** + * @param status + */ + public PKIStatusInfo(PKIStatus status) + { + this.status = ASN1Integer.getInstance(status.toASN1Primitive()); + } + + /** + * + * @param status + * @param statusString + */ + public PKIStatusInfo( + PKIStatus status, + PKIFreeText statusString) + { + this.status = ASN1Integer.getInstance(status.toASN1Primitive()); + this.statusString = statusString; + } + + public PKIStatusInfo( + PKIStatus status, + PKIFreeText statusString, + PKIFailureInfo failInfo) + { + this.status = ASN1Integer.getInstance(status.toASN1Primitive()); + this.statusString = statusString; + this.failInfo = failInfo; + } + + public BigInteger getStatus() + { + return status.getValue(); + } + + public PKIFreeText getStatusString() + { + return statusString; + } + + public DERBitString getFailInfo() + { + return failInfo; + } + + /** + *
+ * PKIStatusInfo ::= SEQUENCE { + * status PKIStatus, (INTEGER) + * statusString PKIFreeText OPTIONAL, + * failInfo PKIFailureInfo OPTIONAL (BIT STRING) + * } + * + * PKIStatus: + * granted (0), -- you got exactly what you asked for + * grantedWithMods (1), -- you got something like what you asked for + * rejection (2), -- you don't get it, more information elsewhere in the message + * waiting (3), -- the request body part has not yet been processed, expect to hear more later + * revocationWarning (4), -- this message contains a warning that a revocation is imminent + * revocationNotification (5), -- notification that a revocation has occurred + * keyUpdateWarning (6) -- update already done for the oldCertId specified in CertReqMsg + * + * PKIFailureInfo: + * badAlg (0), -- unrecognized or unsupported Algorithm Identifier + * badMessageCheck (1), -- integrity check failed (e.g., signature did not verify) + * badRequest (2), -- transaction not permitted or supported + * badTime (3), -- messageTime was not sufficiently close to the system time, as defined by local policy + * badCertId (4), -- no certificate could be found matching the provided criteria + * badDataFormat (5), -- the data submitted has the wrong format + * wrongAuthority (6), -- the authority indicated in the request is different from the one creating the response token + * incorrectData (7), -- the requester's data is incorrect (for notary services) + * missingTimeStamp (8), -- when the timestamp is missing but should be there (by policy) + * badPOP (9) -- the proof-of-possession failed + * + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(status); + + if (statusString != null) + { + v.add(statusString); + } + + if (failInfo!= null) + { + v.add(failInfo); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/POPODecKeyChallContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/POPODecKeyChallContent.java new file mode 100644 index 000000000..2c6361431 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/POPODecKeyChallContent.java @@ -0,0 +1,54 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; + +public class POPODecKeyChallContent + extends ASN1Object +{ + private ASN1Sequence content; + + private POPODecKeyChallContent(ASN1Sequence seq) + { + content = seq; + } + + public static POPODecKeyChallContent getInstance(Object o) + { + if (o instanceof POPODecKeyChallContent) + { + return (POPODecKeyChallContent)o; + } + + if (o != null) + { + return new POPODecKeyChallContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public Challenge[] toChallengeArray() + { + Challenge[] result = new Challenge[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = Challenge.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * POPODecKeyChallContent ::= SEQUENCE OF Challenge + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/POPODecKeyRespContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/POPODecKeyRespContent.java new file mode 100644 index 000000000..48466b893 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/POPODecKeyRespContent.java @@ -0,0 +1,55 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; + +public class POPODecKeyRespContent + extends ASN1Object +{ + private ASN1Sequence content; + + private POPODecKeyRespContent(ASN1Sequence seq) + { + content = seq; + } + + public static POPODecKeyRespContent getInstance(Object o) + { + if (o instanceof POPODecKeyRespContent) + { + return (POPODecKeyRespContent)o; + } + + if (o != null) + { + return new POPODecKeyRespContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ASN1Integer[] toASN1IntegerArray() + { + ASN1Integer[] result = new ASN1Integer[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = ASN1Integer.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * POPODecKeyRespContent ::= SEQUENCE OF INTEGER + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PollRepContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PollRepContent.java new file mode 100644 index 000000000..d2116b9a8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PollRepContent.java @@ -0,0 +1,119 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class PollRepContent + extends ASN1Object +{ + private ASN1Integer[] certReqId; + private ASN1Integer[] checkAfter; + private PKIFreeText[] reason; + + private PollRepContent(ASN1Sequence seq) + { + certReqId = new ASN1Integer[seq.size()]; + checkAfter = new ASN1Integer[seq.size()]; + reason = new PKIFreeText[seq.size()]; + + for (int i = 0; i != seq.size(); i++) + { + ASN1Sequence s = ASN1Sequence.getInstance(seq.getObjectAt(i)); + + certReqId[i] = ASN1Integer.getInstance(s.getObjectAt(0)); + checkAfter[i] = ASN1Integer.getInstance(s.getObjectAt(1)); + + if (s.size() > 2) + { + reason[i] = PKIFreeText.getInstance(s.getObjectAt(2)); + } + } + } + + public static PollRepContent getInstance(Object o) + { + if (o instanceof PollRepContent) + { + return (PollRepContent)o; + } + + if (o != null) + { + return new PollRepContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public PollRepContent(ASN1Integer certReqId, ASN1Integer checkAfter) + { + this(certReqId, checkAfter, null); + } + + public PollRepContent(ASN1Integer certReqId, ASN1Integer checkAfter, PKIFreeText reason) + { + this.certReqId = new ASN1Integer[1]; + this.checkAfter = new ASN1Integer[1]; + this.reason = new PKIFreeText[1]; + + this.certReqId[0] = certReqId; + this.checkAfter[0] = checkAfter; + this.reason[0] = reason; + } + + public int size() + { + return certReqId.length; + } + + public ASN1Integer getCertReqId(int index) + { + return certReqId[index]; + } + + public ASN1Integer getCheckAfter(int index) + { + return checkAfter[index]; + } + + public PKIFreeText getReason(int index) + { + return reason[index]; + } + + /** + *
+ * PollRepContent ::= SEQUENCE OF SEQUENCE { + * certReqId INTEGER, + * checkAfter INTEGER, -- time in seconds + * reason PKIFreeText OPTIONAL + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector outer = new ASN1EncodableVector(); + + for (int i = 0; i != certReqId.length; i++) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certReqId[i]); + v.add(checkAfter[i]); + + if (reason[i] != null) + { + v.add(reason[i]); + } + + outer.add(new DERSequence(v)); + } + + return new DERSequence(outer); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PollReqContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PollReqContent.java new file mode 100644 index 000000000..58bfbd08d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/PollReqContent.java @@ -0,0 +1,80 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class PollReqContent + extends ASN1Object +{ + private ASN1Sequence content; + + private PollReqContent(ASN1Sequence seq) + { + content = seq; + } + + public static PollReqContent getInstance(Object o) + { + if (o instanceof PollReqContent) + { + return (PollReqContent)o; + } + + if (o != null) + { + return new PollReqContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * Create a pollReqContent for a single certReqId. + * + * @param certReqId the certificate request ID. + */ + public PollReqContent(ASN1Integer certReqId) + { + this(new DERSequence(new DERSequence(certReqId))); + } + + public ASN1Integer[][] getCertReqIds() + { + ASN1Integer[][] result = new ASN1Integer[content.size()][]; + + for (int i = 0; i != result.length; i++) + { + result[i] = sequenceToASN1IntegerArray((ASN1Sequence)content.getObjectAt(i)); + } + + return result; + } + + private static ASN1Integer[] sequenceToASN1IntegerArray(ASN1Sequence seq) + { + ASN1Integer[] result = new ASN1Integer[seq.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = ASN1Integer.getInstance(seq.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * PollReqContent ::= SEQUENCE OF SEQUENCE { + * certReqId INTEGER + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/ProtectedPart.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/ProtectedPart.java new file mode 100644 index 000000000..7594e8ecd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/ProtectedPart.java @@ -0,0 +1,70 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class ProtectedPart + extends ASN1Object +{ + private PKIHeader header; + private PKIBody body; + + private ProtectedPart(ASN1Sequence seq) + { + header = PKIHeader.getInstance(seq.getObjectAt(0)); + body = PKIBody.getInstance(seq.getObjectAt(1)); + } + + public static ProtectedPart getInstance(Object o) + { + if (o instanceof ProtectedPart) + { + return (ProtectedPart)o; + } + + if (o != null) + { + return new ProtectedPart(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ProtectedPart(PKIHeader header, PKIBody body) + { + this.header = header; + this.body = body; + } + + public PKIHeader getHeader() + { + return header; + } + + public PKIBody getBody() + { + return body; + } + + /** + *
+ * ProtectedPart ::= SEQUENCE { + * header PKIHeader, + * body PKIBody + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(header); + v.add(body); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevAnnContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevAnnContent.java new file mode 100644 index 000000000..39d4de9ed --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevAnnContent.java @@ -0,0 +1,103 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.crmf.CertId; +import org.spongycastle.asn1.x509.Extensions; + +public class RevAnnContent + extends ASN1Object +{ + private PKIStatus status; + private CertId certId; + private ASN1GeneralizedTime willBeRevokedAt; + private ASN1GeneralizedTime badSinceDate; + private Extensions crlDetails; + + private RevAnnContent(ASN1Sequence seq) + { + status = PKIStatus.getInstance(seq.getObjectAt(0)); + certId = CertId.getInstance(seq.getObjectAt(1)); + willBeRevokedAt = ASN1GeneralizedTime.getInstance(seq.getObjectAt(2)); + badSinceDate = ASN1GeneralizedTime.getInstance(seq.getObjectAt(3)); + + if (seq.size() > 4) + { + crlDetails = Extensions.getInstance(seq.getObjectAt(4)); + } + } + + public static RevAnnContent getInstance(Object o) + { + if (o instanceof RevAnnContent) + { + return (RevAnnContent)o; + } + + if (o != null) + { + return new RevAnnContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public PKIStatus getStatus() + { + return status; + } + + public CertId getCertId() + { + return certId; + } + + public ASN1GeneralizedTime getWillBeRevokedAt() + { + return willBeRevokedAt; + } + + public ASN1GeneralizedTime getBadSinceDate() + { + return badSinceDate; + } + + public Extensions getCrlDetails() + { + return crlDetails; + } + + /** + *
+ * RevAnnContent ::= SEQUENCE { + * status PKIStatus, + * certId CertId, + * willBeRevokedAt GeneralizedTime, + * badSinceDate GeneralizedTime, + * crlDetails Extensions OPTIONAL + * -- extra CRL details (e.g., crl number, reason, location, etc.) + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(status); + v.add(certId); + v.add(willBeRevokedAt); + v.add(badSinceDate); + + if (crlDetails != null) + { + v.add(crlDetails); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevDetails.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevDetails.java new file mode 100644 index 000000000..6009a3bef --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevDetails.java @@ -0,0 +1,100 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.crmf.CertTemplate; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.X509Extensions; + +public class RevDetails + extends ASN1Object +{ + private CertTemplate certDetails; + private Extensions crlEntryDetails; + + private RevDetails(ASN1Sequence seq) + { + certDetails = CertTemplate.getInstance(seq.getObjectAt(0)); + if (seq.size() > 1) + { + crlEntryDetails = Extensions.getInstance(seq.getObjectAt(1)); + } + } + + public static RevDetails getInstance(Object o) + { + if (o instanceof RevDetails) + { + return (RevDetails)o; + } + + if (o != null) + { + return new RevDetails(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public RevDetails(CertTemplate certDetails) + { + this.certDetails = certDetails; + } + + /** + * @deprecated use method taking Extensions + * @param certDetails + * @param crlEntryDetails + */ + public RevDetails(CertTemplate certDetails, X509Extensions crlEntryDetails) + { + this.certDetails = certDetails; + this.crlEntryDetails = Extensions.getInstance(crlEntryDetails.toASN1Primitive()); + } + + public RevDetails(CertTemplate certDetails, Extensions crlEntryDetails) + { + this.certDetails = certDetails; + this.crlEntryDetails = crlEntryDetails; + } + + public CertTemplate getCertDetails() + { + return certDetails; + } + + public Extensions getCrlEntryDetails() + { + return crlEntryDetails; + } + + /** + *
+ * RevDetails ::= SEQUENCE { + * certDetails CertTemplate, + * -- allows requester to specify as much as they can about + * -- the cert. for which revocation is requested + * -- (e.g., for cases in which serialNumber is not available) + * crlEntryDetails Extensions OPTIONAL + * -- requested crlEntryExtensions + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certDetails); + + if (crlEntryDetails != null) + { + v.add(crlEntryDetails); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevRepContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevRepContent.java new file mode 100644 index 000000000..b67741937 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevRepContent.java @@ -0,0 +1,137 @@ +package org.spongycastle.asn1.cmp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.crmf.CertId; +import org.spongycastle.asn1.x509.CertificateList; + +public class RevRepContent + extends ASN1Object +{ + private ASN1Sequence status; + private ASN1Sequence revCerts; + private ASN1Sequence crls; + + private RevRepContent(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + status = ASN1Sequence.getInstance(en.nextElement()); + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(en.nextElement()); + + if (tObj.getTagNo() == 0) + { + revCerts = ASN1Sequence.getInstance(tObj, true); + } + else + { + crls = ASN1Sequence.getInstance(tObj, true); + } + } + } + + public static RevRepContent getInstance(Object o) + { + if (o instanceof RevRepContent) + { + return (RevRepContent)o; + } + + if (o != null) + { + return new RevRepContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public PKIStatusInfo[] getStatus() + { + PKIStatusInfo[] results = new PKIStatusInfo[status.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = PKIStatusInfo.getInstance(status.getObjectAt(i)); + } + + return results; + } + + public CertId[] getRevCerts() + { + if (revCerts == null) + { + return null; + } + + CertId[] results = new CertId[revCerts.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = CertId.getInstance(revCerts.getObjectAt(i)); + } + + return results; + } + + public CertificateList[] getCrls() + { + if (crls == null) + { + return null; + } + + CertificateList[] results = new CertificateList[crls.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = CertificateList.getInstance(crls.getObjectAt(i)); + } + + return results; + } + + /** + *
+ * RevRepContent ::= SEQUENCE { + * status SEQUENCE SIZE (1..MAX) OF PKIStatusInfo, + * -- in same order as was sent in RevReqContent + * revCerts [0] SEQUENCE SIZE (1..MAX) OF CertId OPTIONAL, + * -- IDs for which revocation was requested + * -- (same order as status) + * crls [1] SEQUENCE SIZE (1..MAX) OF CertificateList OPTIONAL + * -- the resulting CRLs (there may be more than one) + * } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(status); + + addOptional(v, 0, revCerts); + addOptional(v, 1, crls); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(true, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevRepContentBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevRepContentBuilder.java new file mode 100644 index 000000000..d154f17c2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevRepContentBuilder.java @@ -0,0 +1,59 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.crmf.CertId; +import org.spongycastle.asn1.x509.CertificateList; + +public class RevRepContentBuilder +{ + private ASN1EncodableVector status = new ASN1EncodableVector(); + private ASN1EncodableVector revCerts = new ASN1EncodableVector(); + private ASN1EncodableVector crls = new ASN1EncodableVector(); + + public RevRepContentBuilder add(PKIStatusInfo status) + { + this.status.add(status); + + return this; + } + + public RevRepContentBuilder add(PKIStatusInfo status, CertId certId) + { + if (this.status.size() != this.revCerts.size()) + { + throw new IllegalStateException("status and revCerts sequence must be in common order"); + } + this.status.add(status); + this.revCerts.add(certId); + + return this; + } + + public RevRepContentBuilder addCrl(CertificateList crl) + { + this.crls.add(crl); + + return this; + } + + public RevRepContent build() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new DERSequence(status)); + + if (revCerts.size() != 0) + { + v.add(new DERTaggedObject(true, 0, new DERSequence(revCerts))); + } + + if (crls.size() != 0) + { + v.add(new DERTaggedObject(true, 1, new DERSequence(crls))); + } + + return RevRepContent.getInstance(new DERSequence(v)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevReqContent.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevReqContent.java new file mode 100644 index 000000000..e232417b7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cmp/RevReqContent.java @@ -0,0 +1,73 @@ +package org.spongycastle.asn1.cmp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class RevReqContent + extends ASN1Object +{ + private ASN1Sequence content; + + private RevReqContent(ASN1Sequence seq) + { + content = seq; + } + + public static RevReqContent getInstance(Object o) + { + if (o instanceof RevReqContent) + { + return (RevReqContent)o; + } + + if (o != null) + { + return new RevReqContent(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public RevReqContent(RevDetails revDetails) + { + this.content = new DERSequence(revDetails); + } + + public RevReqContent(RevDetails[] revDetailsArray) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != revDetailsArray.length; i++) + { + v.add(revDetailsArray[i]); + } + + this.content = new DERSequence(v); + } + + public RevDetails[] toRevDetailsArray() + { + RevDetails[] result = new RevDetails[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = RevDetails.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * RevReqContent ::= SEQUENCE OF RevDetails + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/Attribute.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/Attribute.java new file mode 100644 index 000000000..292303a03 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/Attribute.java @@ -0,0 +1,122 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DERSequence; + +/** + * RFC 5652: + * Attribute is a pair of OID (as type identifier) + set of values. + *
+ *
+ * Attribute ::= SEQUENCE { + * attrType OBJECT IDENTIFIER, + * attrValues SET OF AttributeValue + * } + * + * AttributeValue ::= ANY + *+ *
+ * General rule on values is that same AttributeValue must not be included + * multiple times into the set. That is, if the value is a SET OF INTEGERs, + * then having same value repeated is wrong: (1, 1), but different values is OK: (1, 2). + * Normally the AttributeValue syntaxes are more complicated than that. + *
+ * General rule of Attribute usage is that the {@link Attributes} containers + * must not have multiple Attribute:s with same attrType (OID) there. + */ +public class Attribute + extends ASN1Object +{ + private ASN1ObjectIdentifier attrType; + private ASN1Set attrValues; + + /** + * Return an Attribute object from the given object. + *
+ * Accepted inputs: + *
+ * SignedAttributes ::= SET SIZE (1..MAX) OF Attribute + * UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute + * UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute + * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute + * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute + * + * Attributes ::= + * SET SIZE(1..MAX) OF Attribute + *+ */ +public class Attributes + extends ASN1Object +{ + private ASN1Set attributes; + + private Attributes(ASN1Set set) + { + attributes = set; + } + + public Attributes(ASN1EncodableVector v) + { + attributes = new DLSet(v); + } + + /** + * Return an Attribute set object from the given object. + *
+ * Accepted inputs: + *
+ * ASN.1: + *
+ * id-ct-authEnvelopedData OBJECT IDENTIFIER ::= { iso(1) + * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) + * smime(16) ct(1) 23 } + * + * AuthEnvelopedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * authEncryptedContentInfo EncryptedContentInfo, + * authAttrs [1] IMPLICIT AuthAttributes OPTIONAL, + * mac MessageAuthenticationCode, + * unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL } + *+ */ +public class AuthEnvelopedData + extends ASN1Object +{ + private ASN1Integer version; + private OriginatorInfo originatorInfo; + private ASN1Set recipientInfos; + private EncryptedContentInfo authEncryptedContentInfo; + private ASN1Set authAttrs; + private ASN1OctetString mac; + private ASN1Set unauthAttrs; + + public AuthEnvelopedData( + OriginatorInfo originatorInfo, + ASN1Set recipientInfos, + EncryptedContentInfo authEncryptedContentInfo, + ASN1Set authAttrs, + ASN1OctetString mac, + ASN1Set unauthAttrs) + { + // "It MUST be set to 0." + this.version = new ASN1Integer(0); + + this.originatorInfo = originatorInfo; + + // TODO + // "There MUST be at least one element in the collection." + this.recipientInfos = recipientInfos; + + this.authEncryptedContentInfo = authEncryptedContentInfo; + + // TODO + // "The authAttrs MUST be present if the content type carried in + // EncryptedContentInfo is not id-data." + this.authAttrs = authAttrs; + + this.mac = mac; + + this.unauthAttrs = unauthAttrs; + } + + /** + * Constructs AuthEnvelopedData by parsing supplied ASN1Sequence + *
+ * @param seq An ASN1Sequence with AuthEnvelopedData + * @deprecated use getInstance(). + */ + public AuthEnvelopedData( + ASN1Sequence seq) + { + int index = 0; + + // TODO + // "It MUST be set to 0." + ASN1Primitive tmp = seq.getObjectAt(index++).toASN1Primitive(); + version = (ASN1Integer)tmp; + + tmp = seq.getObjectAt(index++).toASN1Primitive(); + if (tmp instanceof ASN1TaggedObject) + { + originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false); + tmp = seq.getObjectAt(index++).toASN1Primitive(); + } + + // TODO + // "There MUST be at least one element in the collection." + recipientInfos = ASN1Set.getInstance(tmp); + + tmp = seq.getObjectAt(index++).toASN1Primitive(); + authEncryptedContentInfo = EncryptedContentInfo.getInstance(tmp); + + tmp = seq.getObjectAt(index++).toASN1Primitive(); + if (tmp instanceof ASN1TaggedObject) + { + authAttrs = ASN1Set.getInstance((ASN1TaggedObject)tmp, false); + tmp = seq.getObjectAt(index++).toASN1Primitive(); + } + else + { + // TODO + // "The authAttrs MUST be present if the content type carried in + // EncryptedContentInfo is not id-data." + } + + mac = ASN1OctetString.getInstance(tmp); + + if (seq.size() > index) + { + tmp = seq.getObjectAt(index++).toASN1Primitive(); + unauthAttrs = ASN1Set.getInstance((ASN1TaggedObject)tmp, false); + } + } + + /** + * Return an AuthEnvelopedData object from a tagged object. + *
+ * Accepted inputs: + *
+ * Accepted inputs: + *
+ * AuthEnvelopedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * authEncryptedContentInfo EncryptedContentInfo, + * authAttrs [1] IMPLICIT AuthAttributes OPTIONAL, + * mac MessageAuthenticationCode, + * unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL } + *+ */ +public class AuthEnvelopedDataParser +{ + private ASN1SequenceParser seq; + private ASN1Integer version; + private ASN1Encodable nextObject; + private boolean originatorInfoCalled; + + public AuthEnvelopedDataParser(ASN1SequenceParser seq) throws IOException + { + this.seq = seq; + + // TODO + // "It MUST be set to 0." + this.version = ASN1Integer.getInstance(seq.readObject()); + } + + public ASN1Integer getVersion() + { + return version; + } + + public OriginatorInfo getOriginatorInfo() + throws IOException + { + originatorInfoCalled = true; + + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)nextObject).getTagNo() == 0) + { + ASN1SequenceParser originatorInfo = (ASN1SequenceParser) ((ASN1TaggedObjectParser)nextObject).getObjectParser(BERTags.SEQUENCE, false); + nextObject = null; + return OriginatorInfo.getInstance(originatorInfo.toASN1Primitive()); + } + + return null; + } + + public ASN1SetParser getRecipientInfos() + throws IOException + { + if (!originatorInfoCalled) + { + getOriginatorInfo(); + } + + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + ASN1SetParser recipientInfos = (ASN1SetParser)nextObject; + nextObject = null; + return recipientInfos; + } + + public EncryptedContentInfoParser getAuthEncryptedContentInfo() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject != null) + { + ASN1SequenceParser o = (ASN1SequenceParser) nextObject; + nextObject = null; + return new EncryptedContentInfoParser(o); + } + + return null; + } + + public ASN1SetParser getAuthAttrs() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject instanceof ASN1TaggedObjectParser) + { + ASN1Encodable o = nextObject; + nextObject = null; + return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false); + } + + // TODO + // "The authAttrs MUST be present if the content type carried in + // EncryptedContentInfo is not id-data." + + return null; + } + + public ASN1OctetString getMac() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + ASN1Encodable o = nextObject; + nextObject = null; + + return ASN1OctetString.getInstance(o.toASN1Primitive()); + } + + public ASN1SetParser getUnauthAttrs() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject != null) + { + ASN1Encodable o = nextObject; + nextObject = null; + return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false); + } + + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/AuthenticatedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/AuthenticatedData.java new file mode 100644 index 000000000..474e5216f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/AuthenticatedData.java @@ -0,0 +1,312 @@ +package org.spongycastle.asn1.cms; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * RFC 5652 section 9.1: + * The AuthenticatedData carries AuthAttributes and other data + * which define what really is being signed. + *
+ *
+ * AuthenticatedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * macAlgorithm MessageAuthenticationCodeAlgorithm, + * digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL, + * encapContentInfo EncapsulatedContentInfo, + * authAttrs [2] IMPLICIT AuthAttributes OPTIONAL, + * mac MessageAuthenticationCode, + * unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL } + * + * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute + * + * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute + * + * MessageAuthenticationCode ::= OCTET STRING + *+ */ +public class AuthenticatedData + extends ASN1Object +{ + private ASN1Integer version; + private OriginatorInfo originatorInfo; + private ASN1Set recipientInfos; + private AlgorithmIdentifier macAlgorithm; + private AlgorithmIdentifier digestAlgorithm; + private ContentInfo encapsulatedContentInfo; + private ASN1Set authAttrs; + private ASN1OctetString mac; + private ASN1Set unauthAttrs; + + public AuthenticatedData( + OriginatorInfo originatorInfo, + ASN1Set recipientInfos, + AlgorithmIdentifier macAlgorithm, + AlgorithmIdentifier digestAlgorithm, + ContentInfo encapsulatedContent, + ASN1Set authAttrs, + ASN1OctetString mac, + ASN1Set unauthAttrs) + { + if (digestAlgorithm != null || authAttrs != null) + { + if (digestAlgorithm == null || authAttrs == null) + { + throw new IllegalArgumentException("digestAlgorithm and authAttrs must be set together"); + } + } + + version = new ASN1Integer(calculateVersion(originatorInfo)); + + this.originatorInfo = originatorInfo; + this.macAlgorithm = macAlgorithm; + this.digestAlgorithm = digestAlgorithm; + this.recipientInfos = recipientInfos; + this.encapsulatedContentInfo = encapsulatedContent; + this.authAttrs = authAttrs; + this.mac = mac; + this.unauthAttrs = unauthAttrs; + } + + /** + * @deprecated use getInstance() + */ + public AuthenticatedData( + ASN1Sequence seq) + { + int index = 0; + + version = (ASN1Integer)seq.getObjectAt(index++); + + Object tmp = seq.getObjectAt(index++); + + if (tmp instanceof ASN1TaggedObject) + { + originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false); + tmp = seq.getObjectAt(index++); + } + + recipientInfos = ASN1Set.getInstance(tmp); + macAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(index++)); + + tmp = seq.getObjectAt(index++); + + if (tmp instanceof ASN1TaggedObject) + { + digestAlgorithm = AlgorithmIdentifier.getInstance((ASN1TaggedObject)tmp, false); + tmp = seq.getObjectAt(index++); + } + + encapsulatedContentInfo = ContentInfo.getInstance(tmp); + + tmp = seq.getObjectAt(index++); + + if (tmp instanceof ASN1TaggedObject) + { + authAttrs = ASN1Set.getInstance((ASN1TaggedObject)tmp, false); + tmp = seq.getObjectAt(index++); + } + + mac = ASN1OctetString.getInstance(tmp); + + if (seq.size() > index) + { + unauthAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false); + } + } + + /** + * Return an AuthenticatedData object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static AuthenticatedData getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an AuthenticatedData object from the given object. + *
+ * Accepted inputs: + *
+ * AuthenticatedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * macAlgorithm MessageAuthenticationCodeAlgorithm, + * digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL, + * encapContentInfo EncapsulatedContentInfo, + * authAttrs [2] IMPLICIT AuthAttributes OPTIONAL, + * mac MessageAuthenticationCode, + * unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL } + * + * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute + * + * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute + * + * MessageAuthenticationCode ::= OCTET STRING + *+ */ +public class AuthenticatedDataParser +{ + private ASN1SequenceParser seq; + private ASN1Integer version; + private ASN1Encodable nextObject; + private boolean originatorInfoCalled; + + public AuthenticatedDataParser( + ASN1SequenceParser seq) + throws IOException + { + this.seq = seq; + this.version = ASN1Integer.getInstance(seq.readObject()); + } + + public ASN1Integer getVersion() + { + return version; + } + + public OriginatorInfo getOriginatorInfo() + throws IOException + { + originatorInfoCalled = true; + + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)nextObject).getTagNo() == 0) + { + ASN1SequenceParser originatorInfo = (ASN1SequenceParser) ((ASN1TaggedObjectParser)nextObject).getObjectParser(BERTags.SEQUENCE, false); + nextObject = null; + return OriginatorInfo.getInstance(originatorInfo.toASN1Primitive()); + } + + return null; + } + + public ASN1SetParser getRecipientInfos() + throws IOException + { + if (!originatorInfoCalled) + { + getOriginatorInfo(); + } + + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + ASN1SetParser recipientInfos = (ASN1SetParser)nextObject; + nextObject = null; + return recipientInfos; + } + + public AlgorithmIdentifier getMacAlgorithm() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject != null) + { + ASN1SequenceParser o = (ASN1SequenceParser)nextObject; + nextObject = null; + return AlgorithmIdentifier.getInstance(o.toASN1Primitive()); + } + + return null; + } + + public AlgorithmIdentifier getDigestAlgorithm() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject instanceof ASN1TaggedObjectParser) + { + AlgorithmIdentifier obj = AlgorithmIdentifier.getInstance((ASN1TaggedObject)nextObject.toASN1Primitive(), false); + nextObject = null; + return obj; + } + + return null; + } + + /** + * @deprecated use getEncapsulatedContentInfo() + */ + public ContentInfoParser getEnapsulatedContentInfo() + throws IOException + { + return getEncapsulatedContentInfo(); + } + + public ContentInfoParser getEncapsulatedContentInfo() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject != null) + { + ASN1SequenceParser o = (ASN1SequenceParser)nextObject; + nextObject = null; + return new ContentInfoParser(o); + } + + return null; + } + + public ASN1SetParser getAuthAttrs() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject instanceof ASN1TaggedObjectParser) + { + ASN1Encodable o = nextObject; + nextObject = null; + return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false); + } + + return null; + } + + public ASN1OctetString getMac() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + ASN1Encodable o = nextObject; + nextObject = null; + + return ASN1OctetString.getInstance(o.toASN1Primitive()); + } + + public ASN1SetParser getUnauthAttrs() + throws IOException + { + if (nextObject == null) + { + nextObject = seq.readObject(); + } + + if (nextObject != null) + { + ASN1Encodable o = nextObject; + nextObject = null; + return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false); + } + + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CCMParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CCMParameters.java new file mode 100644 index 000000000..1c5054494 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CCMParameters.java @@ -0,0 +1,102 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.util.Arrays; + +/** + * RFC 5084: CCMParameters object. + *
+ *
+ CCMParameters ::= SEQUENCE { + aes-nonce OCTET STRING, -- recommended size is 12 octets + aes-ICVlen AES-CCM-ICVlen DEFAULT 12 } + *+ */ +public class CCMParameters + extends ASN1Object +{ + private byte[] nonce; + private int icvLen; + + /** + * Return an CCMParameters object from the given object. + *
+ * Accepted inputs: + *
+ * contentType ::= 1.2.840.113549.1.9.3 + * messageDigest ::= 1.2.840.113549.1.9.4 + * signingTime ::= 1.2.840.113549.1.9.5 + * counterSignature ::= 1.2.840.113549.1.9.6 + * + * contentHint ::= 1.2.840.113549.1.9.16.2.4 + *+ */ + +public interface CMSAttributes +{ + /** PKCS#9: 1.2.840.113549.1.9.3 */ + public static final ASN1ObjectIdentifier contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType; + /** PKCS#9: 1.2.840.113549.1.9.4 */ + public static final ASN1ObjectIdentifier messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest; + /** PKCS#9: 1.2.840.113549.1.9.5 */ + public static final ASN1ObjectIdentifier signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime; + /** PKCS#9: 1.2.840.113549.1.9.6 */ + public static final ASN1ObjectIdentifier counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature; + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See RFC 2634 */ + public static final ASN1ObjectIdentifier contentHint = PKCSObjectIdentifiers.id_aa_contentHint; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CMSObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CMSObjectIdentifiers.java new file mode 100644 index 000000000..9e38740fb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CMSObjectIdentifiers.java @@ -0,0 +1,43 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +public interface CMSObjectIdentifiers +{ + /** PKCS#7: 1.2.840.113549.1.7.1 */ + static final ASN1ObjectIdentifier data = PKCSObjectIdentifiers.data; + /** PKCS#7: 1.2.840.113549.1.7.2 */ + static final ASN1ObjectIdentifier signedData = PKCSObjectIdentifiers.signedData; + /** PKCS#7: 1.2.840.113549.1.7.3 */ + static final ASN1ObjectIdentifier envelopedData = PKCSObjectIdentifiers.envelopedData; + /** PKCS#7: 1.2.840.113549.1.7.4 */ + static final ASN1ObjectIdentifier signedAndEnvelopedData = PKCSObjectIdentifiers.signedAndEnvelopedData; + /** PKCS#7: 1.2.840.113549.1.7.5 */ + static final ASN1ObjectIdentifier digestedData = PKCSObjectIdentifiers.digestedData; + /** PKCS#7: 1.2.840.113549.1.7.6 */ + static final ASN1ObjectIdentifier encryptedData = PKCSObjectIdentifiers.encryptedData; + /** PKCS#9: 1.2.840.113549.1.9.16.1.2 -- smime ct authData */ + static final ASN1ObjectIdentifier authenticatedData = PKCSObjectIdentifiers.id_ct_authData; + /** PKCS#9: 1.2.840.113549.1.9.16.1.9 -- smime ct compressedData */ + static final ASN1ObjectIdentifier compressedData = PKCSObjectIdentifiers.id_ct_compressedData; + /** PKCS#9: 1.2.840.113549.1.9.16.1.23 -- smime ct authEnvelopedData */ + static final ASN1ObjectIdentifier authEnvelopedData = PKCSObjectIdentifiers.id_ct_authEnvelopedData; + /** PKCS#9: 1.2.840.113549.1.9.16.1.31 -- smime ct timestampedData*/ + static final ASN1ObjectIdentifier timestampedData = PKCSObjectIdentifiers.id_ct_timestampedData; + + /** + * The other Revocation Info arc + *
+ *
+ * id-ri OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) + * dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) } + *+ */ + static final ASN1ObjectIdentifier id_ri = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.16"); + + /** 1.3.6.1.5.5.7.16.2 */ + static final ASN1ObjectIdentifier id_ri_ocsp_response = id_ri.branch("2"); + /** 1.3.6.1.5.5.7.16.4 */ + static final ASN1ObjectIdentifier id_ri_scvp = id_ri.branch("4"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CompressedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CompressedData.java new file mode 100644 index 000000000..88d3c458c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/CompressedData.java @@ -0,0 +1,117 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * RFC 3274: CMS Compressed Data. + * + *
+ * CompressedData ::= SEQUENCE { + * version CMSVersion, + * compressionAlgorithm CompressionAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo + * } + *+ */ +public class CompressedData + extends ASN1Object +{ + private ASN1Integer version; + private AlgorithmIdentifier compressionAlgorithm; + private ContentInfo encapContentInfo; + + public CompressedData( + AlgorithmIdentifier compressionAlgorithm, + ContentInfo encapContentInfo) + { + this.version = new ASN1Integer(0); + this.compressionAlgorithm = compressionAlgorithm; + this.encapContentInfo = encapContentInfo; + } + + private CompressedData( + ASN1Sequence seq) + { + this.version = (ASN1Integer)seq.getObjectAt(0); + this.compressionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + this.encapContentInfo = ContentInfo.getInstance(seq.getObjectAt(2)); + } + + /** + * Return a CompressedData object from a tagged object. + * + * @param ato the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static CompressedData getInstance( + ASN1TaggedObject ato, + boolean isExplicit) + { + return getInstance(ASN1Sequence.getInstance(ato, isExplicit)); + } + + /** + * Return a CompressedData object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * CompressedData ::= SEQUENCE { + * version CMSVersion, + * compressionAlgorithm CompressionAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo + * } + *+ */ +public class CompressedDataParser +{ + private ASN1Integer _version; + private AlgorithmIdentifier _compressionAlgorithm; + private ContentInfoParser _encapContentInfo; + + public CompressedDataParser( + ASN1SequenceParser seq) + throws IOException + { + this._version = (ASN1Integer)seq.readObject(); + this._compressionAlgorithm = AlgorithmIdentifier.getInstance(seq.readObject().toASN1Primitive()); + this._encapContentInfo = new ContentInfoParser((ASN1SequenceParser)seq.readObject()); + } + + public ASN1Integer getVersion() + { + return _version; + } + + public AlgorithmIdentifier getCompressionAlgorithmIdentifier() + { + return _compressionAlgorithm; + } + + public ContentInfoParser getEncapContentInfo() + { + return _encapContentInfo; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/ContentInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/ContentInfo.java new file mode 100644 index 000000000..7616a7e58 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/ContentInfo.java @@ -0,0 +1,130 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.BERTaggedObject; + +/** + * RFC 5652 ContentInfo, and + * RFC 5652 EncapsulatedContentInfo objects. + * + *
+ * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + * } + * + * EncapsulatedContentInfo ::= SEQUENCE { + * eContentType ContentType, + * eContent [0] EXPLICIT OCTET STRING OPTIONAL + * } + *+ */ +public class ContentInfo + extends ASN1Object + implements CMSObjectIdentifiers +{ + private ASN1ObjectIdentifier contentType; + private ASN1Encodable content; + + /** + * Return an ContentInfo object from the given object. + *
+ * Accepted inputs: + *
+ * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + *+ */ +public class ContentInfoParser +{ + private ASN1ObjectIdentifier contentType; + private ASN1TaggedObjectParser content; + + public ContentInfoParser( + ASN1SequenceParser seq) + throws IOException + { + contentType = (ASN1ObjectIdentifier)seq.readObject(); + content = (ASN1TaggedObjectParser)seq.readObject(); + } + + public ASN1ObjectIdentifier getContentType() + { + return contentType; + } + + public ASN1Encodable getContent( + int tag) + throws IOException + { + if (content != null) + { + return content.getObjectParser(tag, true); + } + + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/DigestedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/DigestedData.java new file mode 100644 index 000000000..e20a4178c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/DigestedData.java @@ -0,0 +1,128 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * RFC 5652 DigestedData object. + *
+ * DigestedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithm DigestAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo, + * digest Digest } + *+ */ +public class DigestedData + extends ASN1Object +{ + private ASN1Integer version; + private AlgorithmIdentifier digestAlgorithm; + private ContentInfo encapContentInfo; + private ASN1OctetString digest; + + public DigestedData( + AlgorithmIdentifier digestAlgorithm, + ContentInfo encapContentInfo, + byte[] digest) + { + this.version = new ASN1Integer(0); + this.digestAlgorithm = digestAlgorithm; + this.encapContentInfo = encapContentInfo; + this.digest = new DEROctetString(digest); + } + + private DigestedData( + ASN1Sequence seq) + { + this.version = (ASN1Integer)seq.getObjectAt(0); + this.digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + this.encapContentInfo = ContentInfo.getInstance(seq.getObjectAt(2)); + this.digest = ASN1OctetString.getInstance(seq.getObjectAt(3)); + } + + /** + * Return a DigestedData object from a tagged object. + * + * @param ato the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static DigestedData getInstance( + ASN1TaggedObject ato, + boolean isExplicit) + { + return getInstance(ASN1Sequence.getInstance(ato, isExplicit)); + } + + /** + * Return a DigestedData object from the given object. + *
+ * Accepted inputs: + *
+ * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + *+ */ +public class EncryptedContentInfo + extends ASN1Object +{ + private ASN1ObjectIdentifier contentType; + private AlgorithmIdentifier contentEncryptionAlgorithm; + private ASN1OctetString encryptedContent; + + public EncryptedContentInfo( + ASN1ObjectIdentifier contentType, + AlgorithmIdentifier contentEncryptionAlgorithm, + ASN1OctetString encryptedContent) + { + this.contentType = contentType; + this.contentEncryptionAlgorithm = contentEncryptionAlgorithm; + this.encryptedContent = encryptedContent; + } + + private EncryptedContentInfo( + ASN1Sequence seq) + { + if (seq.size() < 2) + { + throw new IllegalArgumentException("Truncated Sequence Found"); + } + + contentType = (ASN1ObjectIdentifier)seq.getObjectAt(0); + contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance( + seq.getObjectAt(1)); + if (seq.size() > 2) + { + encryptedContent = ASN1OctetString.getInstance( + (ASN1TaggedObject)seq.getObjectAt(2), false); + } + } + + /** + * Return an EncryptedContentInfo object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + *+ */ +public class EncryptedContentInfoParser +{ + private ASN1ObjectIdentifier _contentType; + private AlgorithmIdentifier _contentEncryptionAlgorithm; + private ASN1TaggedObjectParser _encryptedContent; + + public EncryptedContentInfoParser( + ASN1SequenceParser seq) + throws IOException + { + _contentType = (ASN1ObjectIdentifier)seq.readObject(); + _contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.readObject().toASN1Primitive()); + _encryptedContent = (ASN1TaggedObjectParser)seq.readObject(); + } + + public ASN1ObjectIdentifier getContentType() + { + return _contentType; + } + + public AlgorithmIdentifier getContentEncryptionAlgorithm() + { + return _contentEncryptionAlgorithm; + } + + public ASN1Encodable getEncryptedContent( + int tag) + throws IOException + { + return _encryptedContent.getObjectParser(tag, false); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/EncryptedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/EncryptedData.java new file mode 100644 index 000000000..69b86781d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/EncryptedData.java @@ -0,0 +1,111 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.BERTaggedObject; + +/** + * RFC 5652 EncryptedData object. + *
+ *
+ * EncryptedData ::= SEQUENCE { + * version CMSVersion, + * encryptedContentInfo EncryptedContentInfo, + * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } + *+ */ +public class EncryptedData + extends ASN1Object +{ + private ASN1Integer version; + private EncryptedContentInfo encryptedContentInfo; + private ASN1Set unprotectedAttrs; + + /** + * Return an EncryptedData object from the given object. + *
+ * Accepted inputs: + *
+ * EnvelopedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * encryptedContentInfo EncryptedContentInfo, + * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL + * } + *+ */ +public class EnvelopedData + extends ASN1Object +{ + private ASN1Integer version; + private OriginatorInfo originatorInfo; + private ASN1Set recipientInfos; + private EncryptedContentInfo encryptedContentInfo; + private ASN1Set unprotectedAttrs; + + public EnvelopedData( + OriginatorInfo originatorInfo, + ASN1Set recipientInfos, + EncryptedContentInfo encryptedContentInfo, + ASN1Set unprotectedAttrs) + { + version = new ASN1Integer(calculateVersion(originatorInfo, recipientInfos, unprotectedAttrs)); + + this.originatorInfo = originatorInfo; + this.recipientInfos = recipientInfos; + this.encryptedContentInfo = encryptedContentInfo; + this.unprotectedAttrs = unprotectedAttrs; + } + + public EnvelopedData( + OriginatorInfo originatorInfo, + ASN1Set recipientInfos, + EncryptedContentInfo encryptedContentInfo, + Attributes unprotectedAttrs) + { + version = new ASN1Integer(calculateVersion(originatorInfo, recipientInfos, ASN1Set.getInstance(unprotectedAttrs))); + + this.originatorInfo = originatorInfo; + this.recipientInfos = recipientInfos; + this.encryptedContentInfo = encryptedContentInfo; + this.unprotectedAttrs = ASN1Set.getInstance(unprotectedAttrs); + } + + /** + * @deprecated use getInstance() + */ + public EnvelopedData( + ASN1Sequence seq) + { + int index = 0; + + version = (ASN1Integer)seq.getObjectAt(index++); + + Object tmp = seq.getObjectAt(index++); + + if (tmp instanceof ASN1TaggedObject) + { + originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false); + tmp = seq.getObjectAt(index++); + } + + recipientInfos = ASN1Set.getInstance(tmp); + + encryptedContentInfo = EncryptedContentInfo.getInstance(seq.getObjectAt(index++)); + + if(seq.size() > index) + { + unprotectedAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false); + } + } + + /** + * Return an EnvelopedData object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static EnvelopedData getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an EnvelopedData object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * EnvelopedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * encryptedContentInfo EncryptedContentInfo, + * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL + * } + *+ */ +public class EnvelopedDataParser +{ + private ASN1SequenceParser _seq; + private ASN1Integer _version; + private ASN1Encodable _nextObject; + private boolean _originatorInfoCalled; + + public EnvelopedDataParser( + ASN1SequenceParser seq) + throws IOException + { + this._seq = seq; + this._version = ASN1Integer.getInstance(seq.readObject()); + } + + public ASN1Integer getVersion() + { + return _version; + } + + public OriginatorInfo getOriginatorInfo() + throws IOException + { + _originatorInfoCalled = true; + + if (_nextObject == null) + { + _nextObject = _seq.readObject(); + } + + if (_nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)_nextObject).getTagNo() == 0) + { + ASN1SequenceParser originatorInfo = (ASN1SequenceParser) ((ASN1TaggedObjectParser)_nextObject).getObjectParser(BERTags.SEQUENCE, false); + _nextObject = null; + return OriginatorInfo.getInstance(originatorInfo.toASN1Primitive()); + } + + return null; + } + + public ASN1SetParser getRecipientInfos() + throws IOException + { + if (!_originatorInfoCalled) + { + getOriginatorInfo(); + } + + if (_nextObject == null) + { + _nextObject = _seq.readObject(); + } + + ASN1SetParser recipientInfos = (ASN1SetParser)_nextObject; + _nextObject = null; + return recipientInfos; + } + + public EncryptedContentInfoParser getEncryptedContentInfo() + throws IOException + { + if (_nextObject == null) + { + _nextObject = _seq.readObject(); + } + + + if (_nextObject != null) + { + ASN1SequenceParser o = (ASN1SequenceParser) _nextObject; + _nextObject = null; + return new EncryptedContentInfoParser(o); + } + + return null; + } + + public ASN1SetParser getUnprotectedAttrs() + throws IOException + { + if (_nextObject == null) + { + _nextObject = _seq.readObject(); + } + + + if (_nextObject != null) + { + ASN1Encodable o = _nextObject; + _nextObject = null; + return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false); + } + + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/Evidence.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/Evidence.java new file mode 100644 index 000000000..a8b173f90 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/Evidence.java @@ -0,0 +1,80 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * RFC 5544: + * Binding Documents with Time-Stamps; Evidence object. + *
+ *
+ * Evidence ::= CHOICE { + * tstEvidence [0] TimeStampTokenEvidence, -- see RFC 3161 + * ersEvidence [1] EvidenceRecord, -- see RFC 4998 + * otherEvidence [2] OtherEvidence + * } + *+ */ +public class Evidence + extends ASN1Object + implements ASN1Choice +{ + private TimeStampTokenEvidence tstEvidence; + + public Evidence(TimeStampTokenEvidence tstEvidence) + { + this.tstEvidence = tstEvidence; + } + + private Evidence(ASN1TaggedObject tagged) + { + if (tagged.getTagNo() == 0) + { + this.tstEvidence = TimeStampTokenEvidence.getInstance(tagged, false); + } + } + + /** + * Return an Evidence object from the given object. + *
+ * Accepted inputs: + *
+ *
+ GCMParameters ::= SEQUENCE { + aes-nonce OCTET STRING, -- recommended size is 12 octets + aes-ICVlen AES-GCM-ICVlen DEFAULT 12 } + *+ */ +public class GCMParameters + extends ASN1Object +{ + private byte[] nonce; + private int icvLen; + + /** + * Return an GCMParameters object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * IssuerAndSerialNumber ::= SEQUENCE { + * issuer Name, + * serialNumber CertificateSerialNumber + * } + * + * CertificateSerialNumber ::= INTEGER -- See RFC 5280 + *+ */ +public class IssuerAndSerialNumber + extends ASN1Object +{ + private X500Name name; + private ASN1Integer serialNumber; + + /** + * Return an IssuerAndSerialNumber object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * KEKIdentifier ::= SEQUENCE { + * keyIdentifier OCTET STRING, + * date GeneralizedTime OPTIONAL, + * other OtherKeyAttribute OPTIONAL + * } + *+ */ +public class KEKIdentifier + extends ASN1Object +{ + private ASN1OctetString keyIdentifier; + private ASN1GeneralizedTime date; + private OtherKeyAttribute other; + + public KEKIdentifier( + byte[] keyIdentifier, + ASN1GeneralizedTime date, + OtherKeyAttribute other) + { + this.keyIdentifier = new DEROctetString(keyIdentifier); + this.date = date; + this.other = other; + } + + private KEKIdentifier( + ASN1Sequence seq) + { + keyIdentifier = (ASN1OctetString)seq.getObjectAt(0); + + switch (seq.size()) + { + case 1: + break; + case 2: + if (seq.getObjectAt(1) instanceof ASN1GeneralizedTime) + { + date = (ASN1GeneralizedTime)seq.getObjectAt(1); + } + else + { + other = OtherKeyAttribute.getInstance(seq.getObjectAt(1)); + } + break; + case 3: + date = (ASN1GeneralizedTime)seq.getObjectAt(1); + other = OtherKeyAttribute.getInstance(seq.getObjectAt(2)); + break; + default: + throw new IllegalArgumentException("Invalid KEKIdentifier"); + } + } + + /** + * Return a KEKIdentifier object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KEKIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a KEKIdentifier object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * KEKRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- always set to 4 + * kekid KEKIdentifier, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey + * } + *+ */ +public class KEKRecipientInfo + extends ASN1Object +{ + private ASN1Integer version; + private KEKIdentifier kekid; + private AlgorithmIdentifier keyEncryptionAlgorithm; + private ASN1OctetString encryptedKey; + + public KEKRecipientInfo( + KEKIdentifier kekid, + AlgorithmIdentifier keyEncryptionAlgorithm, + ASN1OctetString encryptedKey) + { + this.version = new ASN1Integer(4); + this.kekid = kekid; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + public KEKRecipientInfo( + ASN1Sequence seq) + { + version = (ASN1Integer)seq.getObjectAt(0); + kekid = KEKIdentifier.getInstance(seq.getObjectAt(1)); + keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2)); + encryptedKey = (ASN1OctetString)seq.getObjectAt(3); + } + + /** + * Return a KEKRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KEKRecipientInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a KEKRecipientInfo object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * KeyAgreeRecipientIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * rKeyId [0] IMPLICIT RecipientKeyIdentifier } + *+ */ +public class KeyAgreeRecipientIdentifier + extends ASN1Object + implements ASN1Choice +{ + private IssuerAndSerialNumber issuerSerial; + private RecipientKeyIdentifier rKeyID; + + /** + * Return an KeyAgreeRecipientIdentifier object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KeyAgreeRecipientIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an KeyAgreeRecipientIdentifier object from the given object. + *
+ * Accepted inputs: + *
+ * Note: no byte[] input! + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static KeyAgreeRecipientIdentifier getInstance( + Object obj) + { + if (obj == null || obj instanceof KeyAgreeRecipientIdentifier) + { + return (KeyAgreeRecipientIdentifier)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new KeyAgreeRecipientIdentifier(IssuerAndSerialNumber.getInstance(obj)); + } + + if (obj instanceof ASN1TaggedObject && ((ASN1TaggedObject)obj).getTagNo() == 0) + { + return new KeyAgreeRecipientIdentifier(RecipientKeyIdentifier.getInstance( + (ASN1TaggedObject)obj, false)); + } + + throw new IllegalArgumentException("Invalid KeyAgreeRecipientIdentifier: " + obj.getClass().getName()); + } + + public KeyAgreeRecipientIdentifier( + IssuerAndSerialNumber issuerSerial) + { + this.issuerSerial = issuerSerial; + this.rKeyID = null; + } + + public KeyAgreeRecipientIdentifier( + RecipientKeyIdentifier rKeyID) + { + this.issuerSerial = null; + this.rKeyID = rKeyID; + } + + public IssuerAndSerialNumber getIssuerAndSerialNumber() + { + return issuerSerial; + } + + public RecipientKeyIdentifier getRKeyID() + { + return rKeyID; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + */ + public ASN1Primitive toASN1Primitive() + { + if (issuerSerial != null) + { + return issuerSerial.toASN1Primitive(); + } + + return new DERTaggedObject(false, 0, rKeyID); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/KeyAgreeRecipientInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/KeyAgreeRecipientInfo.java new file mode 100644 index 000000000..28a1edc24 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/KeyAgreeRecipientInfo.java @@ -0,0 +1,166 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * RFC 5652: + * Content encryption key delivery mechanisms. + *
+ *
+ * KeyAgreeRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- always set to 3 + * originator [0] EXPLICIT OriginatorIdentifierOrKey, + * ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * recipientEncryptedKeys RecipientEncryptedKeys + * } + * + * UserKeyingMaterial ::= OCTET STRING + *+ */ +public class KeyAgreeRecipientInfo + extends ASN1Object +{ + private ASN1Integer version; + private OriginatorIdentifierOrKey originator; + private ASN1OctetString ukm; + private AlgorithmIdentifier keyEncryptionAlgorithm; + private ASN1Sequence recipientEncryptedKeys; + + public KeyAgreeRecipientInfo( + OriginatorIdentifierOrKey originator, + ASN1OctetString ukm, + AlgorithmIdentifier keyEncryptionAlgorithm, + ASN1Sequence recipientEncryptedKeys) + { + this.version = new ASN1Integer(3); + this.originator = originator; + this.ukm = ukm; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.recipientEncryptedKeys = recipientEncryptedKeys; + } + + /** + * @deprecated use getInstance() + */ + public KeyAgreeRecipientInfo( + ASN1Sequence seq) + { + int index = 0; + + version = (ASN1Integer)seq.getObjectAt(index++); + originator = OriginatorIdentifierOrKey.getInstance( + (ASN1TaggedObject)seq.getObjectAt(index++), true); + + if (seq.getObjectAt(index) instanceof ASN1TaggedObject) + { + ukm = ASN1OctetString.getInstance( + (ASN1TaggedObject)seq.getObjectAt(index++), true); + } + + keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance( + seq.getObjectAt(index++)); + + recipientEncryptedKeys = (ASN1Sequence)seq.getObjectAt(index++); + } + + /** + * Return a KeyAgreeRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KeyAgreeRecipientInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a KeyAgreeRecipientInfo object from the given object. + *
+ * Accepted inputs: + *
+ * KeyTransRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- always set to 0 or 2 + * rid RecipientIdentifier, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey + * } + *+ */ +public class KeyTransRecipientInfo + extends ASN1Object +{ + private ASN1Integer version; + private RecipientIdentifier rid; + private AlgorithmIdentifier keyEncryptionAlgorithm; + private ASN1OctetString encryptedKey; + + public KeyTransRecipientInfo( + RecipientIdentifier rid, + AlgorithmIdentifier keyEncryptionAlgorithm, + ASN1OctetString encryptedKey) + { + if (rid.toASN1Primitive() instanceof ASN1TaggedObject) + { + this.version = new ASN1Integer(2); + } + else + { + this.version = new ASN1Integer(0); + } + + this.rid = rid; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + /** + * @deprecated use getInstance() + */ + public KeyTransRecipientInfo( + ASN1Sequence seq) + { + this.version = (ASN1Integer)seq.getObjectAt(0); + this.rid = RecipientIdentifier.getInstance(seq.getObjectAt(1)); + this.keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2)); + this.encryptedKey = (ASN1OctetString)seq.getObjectAt(3); + } + + /** + * Return a KeyTransRecipientInfo object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * MetaData ::= SEQUENCE { + * hashProtected BOOLEAN, + * fileName UTF8String OPTIONAL, + * mediaType IA5String OPTIONAL, + * otherMetaData Attributes OPTIONAL + * } + *+ */ +public class MetaData + extends ASN1Object +{ + private ASN1Boolean hashProtected; + private DERUTF8String fileName; + private DERIA5String mediaType; + private Attributes otherMetaData; + + public MetaData( + ASN1Boolean hashProtected, + DERUTF8String fileName, + DERIA5String mediaType, + Attributes otherMetaData) + { + this.hashProtected = hashProtected; + this.fileName = fileName; + this.mediaType = mediaType; + this.otherMetaData = otherMetaData; + } + + private MetaData(ASN1Sequence seq) + { + this.hashProtected = ASN1Boolean.getInstance(seq.getObjectAt(0)); + + int index = 1; + + if (index < seq.size() && seq.getObjectAt(index) instanceof DERUTF8String) + { + this.fileName = DERUTF8String.getInstance(seq.getObjectAt(index++)); + } + if (index < seq.size() && seq.getObjectAt(index) instanceof DERIA5String) + { + this.mediaType = DERIA5String.getInstance(seq.getObjectAt(index++)); + } + if (index < seq.size()) + { + this.otherMetaData = Attributes.getInstance(seq.getObjectAt(index++)); + } + } + + /** + * Return a MetaData object from the given object. + *
+ * Accepted inputs: + *
+ * OriginatorIdentifierOrKey ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * subjectKeyIdentifier [0] SubjectKeyIdentifier, + * originatorKey [1] OriginatorPublicKey + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + *+ */ +public class OriginatorIdentifierOrKey + extends ASN1Object + implements ASN1Choice +{ + private ASN1Encodable id; + + public OriginatorIdentifierOrKey( + IssuerAndSerialNumber id) + { + this.id = id; + } + + /** + * @deprecated use version taking a SubjectKeyIdentifier + */ + public OriginatorIdentifierOrKey( + ASN1OctetString id) + { + this(new SubjectKeyIdentifier(id.getOctets())); + } + + public OriginatorIdentifierOrKey( + SubjectKeyIdentifier id) + { + this.id = new DERTaggedObject(false, 0, id); + } + + public OriginatorIdentifierOrKey( + OriginatorPublicKey id) + { + this.id = new DERTaggedObject(false, 1, id); + } + + /** + * @deprecated use more specific version + */ + public OriginatorIdentifierOrKey( + ASN1Primitive id) + { + this.id = id; + } + + /** + * Return an OriginatorIdentifierOrKey object from a tagged object. + * + * @param o the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OriginatorIdentifierOrKey getInstance( + ASN1TaggedObject o, + boolean explicit) + { + if (!explicit) + { + throw new IllegalArgumentException( + "Can't implicitly tag OriginatorIdentifierOrKey"); + } + + return getInstance(o.getObject()); + } + + /** + * Return an OriginatorIdentifierOrKey object from the given object. + *
+ * Accepted inputs: + *
+ * RFC 3369: + * + * OriginatorInfo ::= SEQUENCE { + * certs [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL + * } + * CertificateRevocationLists ::= SET OF CertificateList (from X.509) + * + * RFC 3582 / 5652: + * + * OriginatorInfo ::= SEQUENCE { + * certs [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL + * } + * RevocationInfoChoices ::= SET OF RevocationInfoChoice + * RevocationInfoChoice ::= CHOICE { + * crl CertificateList, + * other [1] IMPLICIT OtherRevocationInfoFormat } + * + * OtherRevocationInfoFormat ::= SEQUENCE { + * otherRevInfoFormat OBJECT IDENTIFIER, + * otherRevInfo ANY DEFINED BY otherRevInfoFormat } + *+ *
+ * TODO: RevocationInfoChoices / RevocationInfoChoice. + * Constructor using CertificateSet, CertificationInfoChoices + */ +public class OriginatorInfo + extends ASN1Object +{ + private ASN1Set certs; + private ASN1Set crls; + + public OriginatorInfo( + ASN1Set certs, + ASN1Set crls) + { + this.certs = certs; + this.crls = crls; + } + + private OriginatorInfo( + ASN1Sequence seq) + { + switch (seq.size()) + { + case 0: // empty + break; + case 1: + ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0); + switch (o.getTagNo()) + { + case 0 : + certs = ASN1Set.getInstance(o, false); + break; + case 1 : + crls = ASN1Set.getInstance(o, false); + break; + default: + throw new IllegalArgumentException("Bad tag in OriginatorInfo: " + o.getTagNo()); + } + break; + case 2: + certs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(0), false); + crls = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false); + break; + default: + throw new IllegalArgumentException("OriginatorInfo too big"); + } + } + + /** + * Return an OriginatorInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OriginatorInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an OriginatorInfo object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * OriginatorPublicKey ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * publicKey BIT STRING + * } + *+ */ +public class OriginatorPublicKey + extends ASN1Object +{ + private AlgorithmIdentifier algorithm; + private DERBitString publicKey; + + public OriginatorPublicKey( + AlgorithmIdentifier algorithm, + byte[] publicKey) + { + this.algorithm = algorithm; + this.publicKey = new DERBitString(publicKey); + } + + /** + * @deprecated use getInstance() + */ + public OriginatorPublicKey( + ASN1Sequence seq) + { + algorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + publicKey = (DERBitString)seq.getObjectAt(1); + } + + /** + * Return an OriginatorPublicKey object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OriginatorPublicKey getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an OriginatorPublicKey object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * OtherKeyAttribute ::= SEQUENCE { + * keyAttrId OBJECT IDENTIFIER, + * keyAttr ANY DEFINED BY keyAttrId OPTIONAL + * } + *+ */ +public class OtherKeyAttribute + extends ASN1Object +{ + private ASN1ObjectIdentifier keyAttrId; + private ASN1Encodable keyAttr; + + /** + * Return an OtherKeyAttribute object from the given object. + *
+ * Accepted inputs: + *
+ * OtherRecipientInfo ::= SEQUENCE { + * oriType OBJECT IDENTIFIER, + * oriValue ANY DEFINED BY oriType } + *+ */ +public class OtherRecipientInfo + extends ASN1Object +{ + private ASN1ObjectIdentifier oriType; + private ASN1Encodable oriValue; + + public OtherRecipientInfo( + ASN1ObjectIdentifier oriType, + ASN1Encodable oriValue) + { + this.oriType = oriType; + this.oriValue = oriValue; + } + + /** + * @deprecated use getInstance(). + */ + public OtherRecipientInfo( + ASN1Sequence seq) + { + oriType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + oriValue = seq.getObjectAt(1); + } + + /** + * Return a OtherRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OtherRecipientInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a OtherRecipientInfo object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * OtherRevocationInfoFormat ::= SEQUENCE { + * otherRevInfoFormat OBJECT IDENTIFIER, + * otherRevInfo ANY DEFINED BY otherRevInfoFormat } + *+ */ +public class OtherRevocationInfoFormat + extends ASN1Object +{ + private ASN1ObjectIdentifier otherRevInfoFormat; + private ASN1Encodable otherRevInfo; + + public OtherRevocationInfoFormat( + ASN1ObjectIdentifier otherRevInfoFormat, + ASN1Encodable otherRevInfo) + { + this.otherRevInfoFormat = otherRevInfoFormat; + this.otherRevInfo = otherRevInfo; + } + + private OtherRevocationInfoFormat( + ASN1Sequence seq) + { + otherRevInfoFormat = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + otherRevInfo = seq.getObjectAt(1); + } + + /** + * Return a OtherRevocationInfoFormat object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OtherRevocationInfoFormat getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a OtherRevocationInfoFormat object from the given object. + *
+ * Accepted inputs: + *
+ * PasswordRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- Always set to 0 + * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier + * OPTIONAL, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey } + *+ */ +public class PasswordRecipientInfo + extends ASN1Object +{ + private ASN1Integer version; + private AlgorithmIdentifier keyDerivationAlgorithm; + private AlgorithmIdentifier keyEncryptionAlgorithm; + private ASN1OctetString encryptedKey; + + public PasswordRecipientInfo( + AlgorithmIdentifier keyEncryptionAlgorithm, + ASN1OctetString encryptedKey) + { + this.version = new ASN1Integer(0); + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + public PasswordRecipientInfo( + AlgorithmIdentifier keyDerivationAlgorithm, + AlgorithmIdentifier keyEncryptionAlgorithm, + ASN1OctetString encryptedKey) + { + this.version = new ASN1Integer(0); + this.keyDerivationAlgorithm = keyDerivationAlgorithm; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + /** + * @deprecated use getInstance() method. + */ + public PasswordRecipientInfo( + ASN1Sequence seq) + { + version = (ASN1Integer)seq.getObjectAt(0); + if (seq.getObjectAt(1) instanceof ASN1TaggedObject) + { + keyDerivationAlgorithm = AlgorithmIdentifier.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false); + keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2)); + encryptedKey = (ASN1OctetString)seq.getObjectAt(3); + } + else + { + keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + encryptedKey = (ASN1OctetString)seq.getObjectAt(2); + } + } + + /** + * Return a PasswordRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static PasswordRecipientInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a PasswordRecipientInfo object from the given object. + *
+ * Accepted inputs: + *
+ * RecipientEncryptedKey ::= SEQUENCE { + * rid KeyAgreeRecipientIdentifier, + * encryptedKey EncryptedKey + * } + *+ */ +public class RecipientEncryptedKey + extends ASN1Object +{ + private KeyAgreeRecipientIdentifier identifier; + private ASN1OctetString encryptedKey; + + private RecipientEncryptedKey( + ASN1Sequence seq) + { + identifier = KeyAgreeRecipientIdentifier.getInstance(seq.getObjectAt(0)); + encryptedKey = (ASN1OctetString)seq.getObjectAt(1); + } + + /** + * Return an RecipientEncryptedKey object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static RecipientEncryptedKey getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a RecipientEncryptedKey object from the given object. + *
+ * Accepted inputs: + *
+ * RecipientIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * subjectKeyIdentifier [0] SubjectKeyIdentifier + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + *+ */ +public class RecipientIdentifier + extends ASN1Object + implements ASN1Choice +{ + private ASN1Encodable id; + + public RecipientIdentifier( + IssuerAndSerialNumber id) + { + this.id = id; + } + + public RecipientIdentifier( + ASN1OctetString id) + { + this.id = new DERTaggedObject(false, 0, id); + } + + public RecipientIdentifier( + ASN1Primitive id) + { + this.id = id; + } + + /** + * Return a RecipientIdentifier object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * RecipientInfo ::= CHOICE { + * ktri KeyTransRecipientInfo, + * kari [1] KeyAgreeRecipientInfo, + * kekri [2] KEKRecipientInfo, + * pwri [3] PasswordRecipientInfo, + * ori [4] OtherRecipientInfo } + *+ */ +public class RecipientInfo + extends ASN1Object + implements ASN1Choice +{ + ASN1Encodable info; + + public RecipientInfo( + KeyTransRecipientInfo info) + { + this.info = info; + } + + public RecipientInfo( + KeyAgreeRecipientInfo info) + { + this.info = new DERTaggedObject(false, 1, info); + } + + public RecipientInfo( + KEKRecipientInfo info) + { + this.info = new DERTaggedObject(false, 2, info); + } + + public RecipientInfo( + PasswordRecipientInfo info) + { + this.info = new DERTaggedObject(false, 3, info); + } + + public RecipientInfo( + OtherRecipientInfo info) + { + this.info = new DERTaggedObject(false, 4, info); + } + + public RecipientInfo( + ASN1Primitive info) + { + this.info = info; + } + + /** + * Return a RecipientInfo object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * RecipientKeyIdentifier ::= SEQUENCE { + * subjectKeyIdentifier SubjectKeyIdentifier, + * date GeneralizedTime OPTIONAL, + * other OtherKeyAttribute OPTIONAL + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + *+ */ +public class RecipientKeyIdentifier + extends ASN1Object +{ + private ASN1OctetString subjectKeyIdentifier; + private DERGeneralizedTime date; + private OtherKeyAttribute other; + + public RecipientKeyIdentifier( + ASN1OctetString subjectKeyIdentifier, + DERGeneralizedTime date, + OtherKeyAttribute other) + { + this.subjectKeyIdentifier = subjectKeyIdentifier; + this.date = date; + this.other = other; + } + + public RecipientKeyIdentifier( + byte[] subjectKeyIdentifier, + DERGeneralizedTime date, + OtherKeyAttribute other) + { + this.subjectKeyIdentifier = new DEROctetString(subjectKeyIdentifier); + this.date = date; + this.other = other; + } + + public RecipientKeyIdentifier( + byte[] subjectKeyIdentifier) + { + this(subjectKeyIdentifier, null, null); + } + + /** + * @deprecated use getInstance() + */ + public RecipientKeyIdentifier( + ASN1Sequence seq) + { + subjectKeyIdentifier = ASN1OctetString.getInstance( + seq.getObjectAt(0)); + + switch(seq.size()) + { + case 1: + break; + case 2: + if (seq.getObjectAt(1) instanceof DERGeneralizedTime) + { + date = (DERGeneralizedTime)seq.getObjectAt(1); + } + else + { + other = OtherKeyAttribute.getInstance(seq.getObjectAt(2)); + } + break; + case 3: + date = (DERGeneralizedTime)seq.getObjectAt(1); + other = OtherKeyAttribute.getInstance(seq.getObjectAt(2)); + break; + default: + throw new IllegalArgumentException("Invalid RecipientKeyIdentifier"); + } + } + + /** + * Return a RecipientKeyIdentifier object from a tagged object. + * + * @param ato the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static RecipientKeyIdentifier getInstance(ASN1TaggedObject ato, boolean isExplicit) + { + return getInstance(ASN1Sequence.getInstance(ato, isExplicit)); + } + + /** + * Return a RecipientKeyIdentifier object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * SCVPReqRes ::= SEQUENCE { + * request [0] EXPLICIT ContentInfo OPTIONAL, + * response ContentInfo } + *+ */ +public class SCVPReqRes + extends ASN1Object +{ + private final ContentInfo request; + private final ContentInfo response; + + /** + * Return a SCVPReqRes object from the given object. + *
+ * Accepted inputs: + *
+ * A signed data object containing multitude of {@link SignerInfo}s. + *
+ * SignedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithms DigestAlgorithmIdentifiers, + * encapContentInfo EncapsulatedContentInfo, + * certificates [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos + * } + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * + * SignerInfos ::= SET OF SignerInfo + *+ *
+ * The version calculation uses following ruleset from RFC 3852 section 5.1: + *
+ * IF ((certificates is present) AND + * (any certificates with a type of other are present)) OR + * ((crls is present) AND + * (any crls with a type of other are present)) + * THEN version MUST be 5 + * ELSE + * IF (certificates is present) AND + * (any version 2 attribute certificates are present) + * THEN version MUST be 4 + * ELSE + * IF ((certificates is present) AND + * (any version 1 attribute certificates are present)) OR + * (any SignerInfo structures are version 3) OR + * (encapContentInfo eContentType is other than id-data) + * THEN version MUST be 3 + * ELSE version MUST be 1 + *+ *
+ * @todo Check possible update for this to RFC 5652 level + */ +public class SignedData + extends ASN1Object +{ + private static final ASN1Integer VERSION_1 = new ASN1Integer(1); + private static final ASN1Integer VERSION_3 = new ASN1Integer(3); + private static final ASN1Integer VERSION_4 = new ASN1Integer(4); + private static final ASN1Integer VERSION_5 = new ASN1Integer(5); + + private ASN1Integer version; + private ASN1Set digestAlgorithms; + private ContentInfo contentInfo; + private ASN1Set certificates; + private ASN1Set crls; + private ASN1Set signerInfos; + private boolean certsBer; + private boolean crlsBer; + + /** + * Return a SignedData object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * SignedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithms DigestAlgorithmIdentifiers, + * encapContentInfo EncapsulatedContentInfo, + * certificates [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos + * } + *+ */ +public class SignedDataParser +{ + private ASN1SequenceParser _seq; + private ASN1Integer _version; + private Object _nextObject; + private boolean _certsCalled; + private boolean _crlsCalled; + + public static SignedDataParser getInstance( + Object o) + throws IOException + { + if (o instanceof ASN1Sequence) + { + return new SignedDataParser(((ASN1Sequence)o).parser()); + } + if (o instanceof ASN1SequenceParser) + { + return new SignedDataParser((ASN1SequenceParser)o); + } + + throw new IOException("unknown object encountered: " + o.getClass().getName()); + } + + private SignedDataParser( + ASN1SequenceParser seq) + throws IOException + { + this._seq = seq; + this._version = (ASN1Integer)seq.readObject(); + } + + public ASN1Integer getVersion() + { + return _version; + } + + public ASN1SetParser getDigestAlgorithms() + throws IOException + { + Object o = _seq.readObject(); + + if (o instanceof ASN1Set) + { + return ((ASN1Set)o).parser(); + } + + return (ASN1SetParser)o; + } + + public ContentInfoParser getEncapContentInfo() + throws IOException + { + return new ContentInfoParser((ASN1SequenceParser)_seq.readObject()); + } + + public ASN1SetParser getCertificates() + throws IOException + { + _certsCalled = true; + _nextObject = _seq.readObject(); + + if (_nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)_nextObject).getTagNo() == 0) + { + ASN1SetParser certs = (ASN1SetParser)((ASN1TaggedObjectParser)_nextObject).getObjectParser(BERTags.SET, false); + _nextObject = null; + + return certs; + } + + return null; + } + + public ASN1SetParser getCrls() + throws IOException + { + if (!_certsCalled) + { + throw new IOException("getCerts() has not been called."); + } + + _crlsCalled = true; + + if (_nextObject == null) + { + _nextObject = _seq.readObject(); + } + + if (_nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)_nextObject).getTagNo() == 1) + { + ASN1SetParser crls = (ASN1SetParser)((ASN1TaggedObjectParser)_nextObject).getObjectParser(BERTags.SET, false); + _nextObject = null; + + return crls; + } + + return null; + } + + public ASN1SetParser getSignerInfos() + throws IOException + { + if (!_certsCalled || !_crlsCalled) + { + throw new IOException("getCerts() and/or getCrls() has not been called."); + } + + if (_nextObject == null) + { + _nextObject = _seq.readObject(); + } + + return (ASN1SetParser)_nextObject; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignerIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignerIdentifier.java new file mode 100644 index 000000000..a2ab088b1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignerIdentifier.java @@ -0,0 +1,114 @@ +package org.spongycastle.asn1.cms; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * RFC 5652: + * Identify who signed the containing {@link SignerInfo} object. + *
+ * The certificates referred to by this are at containing {@link SignedData} structure. + *
+ *
+ * SignerIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * subjectKeyIdentifier [0] SubjectKeyIdentifier + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + *+ */ +public class SignerIdentifier + extends ASN1Object + implements ASN1Choice +{ + private ASN1Encodable id; + + public SignerIdentifier( + IssuerAndSerialNumber id) + { + this.id = id; + } + + public SignerIdentifier( + ASN1OctetString id) + { + this.id = new DERTaggedObject(false, 0, id); + } + + public SignerIdentifier( + ASN1Primitive id) + { + this.id = id; + } + + /** + * Return a SignerIdentifier object from the given object. + *
+ * Accepted inputs: + *
+ * PKCS#7: + * + * SignerInfo ::= SEQUENCE { + * version Version, + * sid SignerIdentifier, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + * } + * + * EncryptedDigest ::= OCTET STRING + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * ----------------------------------------- + * + * RFC 5256: + * + * SignerInfo ::= SEQUENCE { + * version CMSVersion, + * sid SignerIdentifier, + * digestAlgorithm DigestAlgorithmIdentifier, + * signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, + * signatureAlgorithm SignatureAlgorithmIdentifier, + * signature SignatureValue, + * unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL + * } + * + * -- {@link SignerIdentifier} referenced certificates are at containing + * -- {@link SignedData} certificates element. + * + * SignerIdentifier ::= CHOICE { + * issuerAndSerialNumber {@link IssuerAndSerialNumber}, + * subjectKeyIdentifier [0] SubjectKeyIdentifier } + * + * -- See {@link Attributes} for generalized SET OF {@link Attribute} + * + * SignedAttributes ::= SET SIZE (1..MAX) OF Attribute + * UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute + * + * {@link Attribute} ::= SEQUENCE { + * attrType OBJECT IDENTIFIER, + * attrValues SET OF AttributeValue } + * + * AttributeValue ::= ANY + * + * SignatureValue ::= OCTET STRING + *+ */ +public class SignerInfo + extends ASN1Object +{ + private ASN1Integer version; + private SignerIdentifier sid; + private AlgorithmIdentifier digAlgorithm; + private ASN1Set authenticatedAttributes; + private AlgorithmIdentifier digEncryptionAlgorithm; + private ASN1OctetString encryptedDigest; + private ASN1Set unauthenticatedAttributes; + + /** + * Return a SignerInfo object from the given input + *
+ * Accepted inputs: + *
+ *
+ * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + *+ *
+ * This has a constructor using java.util.Date for input which generates + * a {@link org.spongycastle.asn1.DERUTCTime DERUTCTime} object if the + * supplied datetime is in range 1950-01-01-00:00:00 UTC until 2049-12-31-23:59:60 UTC. + * If the datetime value is outside that range, the generated object will be + * {@link org.spongycastle.asn1.DERGeneralizedTime DERGeneralizedTime}. + */ +public class Time + extends ASN1Object + implements ASN1Choice +{ + ASN1Primitive time; + + public static Time getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * @deprecated use getInstance() + */ + public Time( + ASN1Primitive time) + { + if (!(time instanceof DERUTCTime) + && !(time instanceof DERGeneralizedTime)) + { + throw new IllegalArgumentException("unknown object passed to Time"); + } + + this.time = time; + } + + /** + * Create a time object from a given date - if the year is in between 1950 + * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime + * is used. + */ + public Time( + Date date) + { + SimpleTimeZone tz = new SimpleTimeZone(0, "Z"); + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss"); + + dateF.setTimeZone(tz); + + String d = dateF.format(date) + "Z"; + int year = Integer.parseInt(d.substring(0, 4)); + + if (year < 1950 || year > 2049) + { + time = new DERGeneralizedTime(d); + } + else + { + time = new DERUTCTime(d.substring(2)); + } + } + + /** + * Return a Time object from the given object. + *
+ * Accepted inputs: + *
+ * TimeStampAndCRL ::= SEQUENCE { + * timeStamp TimeStampToken, -- according to RFC 3161 + * crl CertificateList OPTIONAL -- according to RFC 5280 + * } + *+ */ +public class TimeStampAndCRL + extends ASN1Object +{ + private ContentInfo timeStamp; + private CertificateList crl; + + public TimeStampAndCRL(ContentInfo timeStamp) + { + this.timeStamp = timeStamp; + } + + private TimeStampAndCRL(ASN1Sequence seq) + { + this.timeStamp = ContentInfo.getInstance(seq.getObjectAt(0)); + if (seq.size() == 2) + { + this.crl = CertificateList.getInstance(seq.getObjectAt(1)); + } + } + + /** + * Return a TimeStampAndCRL object from the given object. + *
+ * Accepted inputs: + *
+ * TimeStampTokenEvidence ::= + * SEQUENCE SIZE(1..MAX) OF TimeStampAndCRL + *+ */ +public class TimeStampTokenEvidence + extends ASN1Object +{ + private TimeStampAndCRL[] timeStampAndCRLs; + + public TimeStampTokenEvidence(TimeStampAndCRL[] timeStampAndCRLs) + { + this.timeStampAndCRLs = timeStampAndCRLs; + } + + public TimeStampTokenEvidence(TimeStampAndCRL timeStampAndCRL) + { + this.timeStampAndCRLs = new TimeStampAndCRL[1]; + + timeStampAndCRLs[0] = timeStampAndCRL; + } + + private TimeStampTokenEvidence(ASN1Sequence seq) + { + this.timeStampAndCRLs = new TimeStampAndCRL[seq.size()]; + + int count = 0; + + for (Enumeration en = seq.getObjects(); en.hasMoreElements();) + { + timeStampAndCRLs[count++] = TimeStampAndCRL.getInstance(en.nextElement()); + } + } + + public static TimeStampTokenEvidence getInstance(ASN1TaggedObject tagged, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(tagged, explicit)); + } + + /** + * Return a TimeStampTokenEvidence object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * TimeStampedData ::= SEQUENCE { + * version INTEGER { v1(1) }, + * dataUri IA5String OPTIONAL, + * metaData MetaData OPTIONAL, + * content OCTET STRING OPTIONAL, + * temporalEvidence Evidence + * } + *+ */ +public class TimeStampedData + extends ASN1Object +{ + private ASN1Integer version; + private DERIA5String dataUri; + private MetaData metaData; + private ASN1OctetString content; + private Evidence temporalEvidence; + + public TimeStampedData(DERIA5String dataUri, MetaData metaData, ASN1OctetString content, Evidence temporalEvidence) + { + this.version = new ASN1Integer(1); + this.dataUri = dataUri; + this.metaData = metaData; + this.content = content; + this.temporalEvidence = temporalEvidence; + } + + private TimeStampedData(ASN1Sequence seq) + { + this.version = ASN1Integer.getInstance(seq.getObjectAt(0)); + + int index = 1; + if (seq.getObjectAt(index) instanceof DERIA5String) + { + this.dataUri = DERIA5String.getInstance(seq.getObjectAt(index++)); + } + if (seq.getObjectAt(index) instanceof MetaData || seq.getObjectAt(index) instanceof ASN1Sequence) + { + this.metaData = MetaData.getInstance(seq.getObjectAt(index++)); + } + if (seq.getObjectAt(index) instanceof ASN1OctetString) + { + this.content = ASN1OctetString.getInstance(seq.getObjectAt(index++)); + } + this.temporalEvidence = Evidence.getInstance(seq.getObjectAt(index)); + } + + /** + * Return a TimeStampedData object from the given object. + *
+ * Accepted inputs: + *
+ *
+ * TimeStampedData ::= SEQUENCE { + * version INTEGER { v1(1) }, + * dataUri IA5String OPTIONAL, + * metaData MetaData OPTIONAL, + * content OCTET STRING OPTIONAL, + * temporalEvidence Evidence + * } + *+ */ +public class TimeStampedDataParser +{ + private ASN1Integer version; + private DERIA5String dataUri; + private MetaData metaData; + private ASN1OctetStringParser content; + private Evidence temporalEvidence; + private ASN1SequenceParser parser; + + private TimeStampedDataParser(ASN1SequenceParser parser) + throws IOException + { + this.parser = parser; + this.version = ASN1Integer.getInstance(parser.readObject()); + + ASN1Encodable obj = parser.readObject(); + + if (obj instanceof DERIA5String) + { + this.dataUri = DERIA5String.getInstance(obj); + obj = parser.readObject(); + } + if (obj instanceof MetaData || obj instanceof ASN1SequenceParser) + { + this.metaData = MetaData.getInstance(obj.toASN1Primitive()); + obj = parser.readObject(); + } + if (obj instanceof ASN1OctetStringParser) + { + this.content = (ASN1OctetStringParser)obj; + } + } + + public static TimeStampedDataParser getInstance(Object obj) + throws IOException + { + if (obj instanceof ASN1Sequence) + { + return new TimeStampedDataParser(((ASN1Sequence)obj).parser()); + } + if (obj instanceof ASN1SequenceParser) + { + return new TimeStampedDataParser((ASN1SequenceParser)obj); + } + + return null; + } + + public DERIA5String getDataUri() + { + return dataUri; + } + + public MetaData getMetaData() + { + return metaData; + } + + public ASN1OctetStringParser getContent() + { + return content; + } + + public Evidence getTemporalEvidence() + throws IOException + { + if (temporalEvidence == null) + { + temporalEvidence = Evidence.getInstance(parser.readObject().toASN1Primitive()); + } + + return temporalEvidence; + } + + /** + *
+ * TimeStampedData ::= SEQUENCE { + * version INTEGER { v1(1) }, + * dataUri IA5String OPTIONAL, + * metaData MetaData OPTIONAL, + * content OCTET STRING OPTIONAL, + * temporalEvidence Evidence + * } + *+ * @return + * @deprecated will be removed + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + + if (dataUri != null) + { + v.add(dataUri); + } + + if (metaData != null) + { + v.add(metaData); + } + + if (content != null) + { + v.add(content); + } + + v.add(temporalEvidence); + + return new BERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java new file mode 100644 index 000000000..ae95a3d94 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java @@ -0,0 +1,122 @@ +package org.spongycastle.asn1.cms.ecc; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cms.OriginatorPublicKey; + +/** + * RFC 5753/3278: MQVuserKeyingMaterial object. + *
+ * MQVuserKeyingMaterial ::= SEQUENCE { + * ephemeralPublicKey OriginatorPublicKey, + * addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL } + *+ */ +public class MQVuserKeyingMaterial + extends ASN1Object +{ + private OriginatorPublicKey ephemeralPublicKey; + private ASN1OctetString addedukm; + + public MQVuserKeyingMaterial( + OriginatorPublicKey ephemeralPublicKey, + ASN1OctetString addedukm) + { + // TODO Check ephemeralPublicKey not null + + this.ephemeralPublicKey = ephemeralPublicKey; + this.addedukm = addedukm; + } + + private MQVuserKeyingMaterial( + ASN1Sequence seq) + { + // TODO Check seq has either 1 or 2 elements + + this.ephemeralPublicKey = OriginatorPublicKey.getInstance( + seq.getObjectAt(0)); + + if (seq.size() > 1) + { + this.addedukm = ASN1OctetString.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true); + } + } + + /** + * Return an MQVuserKeyingMaterial object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static MQVuserKeyingMaterial getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an MQVuserKeyingMaterial object from the given object. + *
+ * Accepted inputs: + *
+ * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * value ANY DEFINED BY type } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(type); + v.add(value); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CRMFObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CRMFObjectIdentifiers.java new file mode 100644 index 000000000..3911ad4ad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CRMFObjectIdentifiers.java @@ -0,0 +1,29 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +public interface CRMFObjectIdentifiers +{ + /** 1.3.6.1.5.5.7 */ + static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7"); + + // arc for Internet X.509 PKI protocols and their components + + /** 1.3.6.1.5.5.7.5 */ + static final ASN1ObjectIdentifier id_pkip = id_pkix.branch("5"); + + /** 1.3.6.1.5.5.7.1 */ + static final ASN1ObjectIdentifier id_regCtrl = id_pkip.branch("1"); + /** 1.3.6.1.5.5.7.1.1 */ + static final ASN1ObjectIdentifier id_regCtrl_regToken = id_regCtrl.branch("1"); + /** 1.3.6.1.5.5.7.1.2 */ + static final ASN1ObjectIdentifier id_regCtrl_authenticator = id_regCtrl.branch("2"); + /** 1.3.6.1.5.5.7.1.3 */ + static final ASN1ObjectIdentifier id_regCtrl_pkiPublicationInfo = id_regCtrl.branch("3"); + /** 1.3.6.1.5.5.7.1.4 */ + static final ASN1ObjectIdentifier id_regCtrl_pkiArchiveOptions = id_regCtrl.branch("4"); + + /** 1.2.840.113549.1.9.16.1,21 */ + static final ASN1ObjectIdentifier id_ct_encKeyWithID = PKCSObjectIdentifiers.id_ct.branch("21"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertId.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertId.java new file mode 100644 index 000000000..7a29236eb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertId.java @@ -0,0 +1,84 @@ +package org.spongycastle.asn1.crmf; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.GeneralName; + +public class CertId + extends ASN1Object +{ + private GeneralName issuer; + private ASN1Integer serialNumber; + + private CertId(ASN1Sequence seq) + { + issuer = GeneralName.getInstance(seq.getObjectAt(0)); + serialNumber = ASN1Integer.getInstance(seq.getObjectAt(1)); + } + + public static CertId getInstance(Object o) + { + if (o instanceof CertId) + { + return (CertId)o; + } + + if (o != null) + { + return new CertId(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public static CertId getInstance(ASN1TaggedObject obj, boolean isExplicit) + { + return getInstance(ASN1Sequence.getInstance(obj, isExplicit)); + } + + public CertId(GeneralName issuer, BigInteger serialNumber) + { + this(issuer, new ASN1Integer(serialNumber)); + } + + public CertId(GeneralName issuer, ASN1Integer serialNumber) + { + this.issuer = issuer; + this.serialNumber = serialNumber; + } + + public GeneralName getIssuer() + { + return issuer; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + /** + *
+ * CertId ::= SEQUENCE { + * issuer GeneralName, + * serialNumber INTEGER } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(issuer); + v.add(serialNumber); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertReqMessages.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertReqMessages.java new file mode 100644 index 000000000..1be5fd86b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertReqMessages.java @@ -0,0 +1,74 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class CertReqMessages + extends ASN1Object +{ + private ASN1Sequence content; + + private CertReqMessages(ASN1Sequence seq) + { + content = seq; + } + + public static CertReqMessages getInstance(Object o) + { + if (o instanceof CertReqMessages) + { + return (CertReqMessages)o; + } + + if (o != null) + { + return new CertReqMessages(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertReqMessages( + CertReqMsg msg) + { + content = new DERSequence(msg); + } + + public CertReqMessages( + CertReqMsg[] msgs) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < msgs.length; i++) + { + v.add(msgs[i]); + } + content = new DERSequence(v); + } + + public CertReqMsg[] toCertReqMsgArray() + { + CertReqMsg[] result = new CertReqMsg[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = CertReqMsg.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * CertReqMessages ::= SEQUENCE SIZE (1..MAX) OF CertReqMsg + *+ * + * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertReqMsg.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertReqMsg.java new file mode 100644 index 000000000..9e563c8b1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertReqMsg.java @@ -0,0 +1,145 @@ +package org.spongycastle.asn1.crmf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class CertReqMsg + extends ASN1Object +{ + private CertRequest certReq; + private ProofOfPossession pop; + private ASN1Sequence regInfo; + + private CertReqMsg(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + certReq = CertRequest.getInstance(en.nextElement()); + while (en.hasMoreElements()) + { + Object o = en.nextElement(); + + if (o instanceof ASN1TaggedObject || o instanceof ProofOfPossession) + { + pop = ProofOfPossession.getInstance(o); + } + else + { + regInfo = ASN1Sequence.getInstance(o); + } + } + } + + public static CertReqMsg getInstance(Object o) + { + if (o instanceof CertReqMsg) + { + return (CertReqMsg)o; + } + else if (o != null) + { + return new CertReqMsg(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * Creates a new CertReqMsg. + * @param certReq CertRequest + * @param pop may be null + * @param regInfo may be null + */ + public CertReqMsg( + CertRequest certReq, + ProofOfPossession pop, + AttributeTypeAndValue[] regInfo) + { + if (certReq == null) + { + throw new IllegalArgumentException("'certReq' cannot be null"); + } + + this.certReq = certReq; + this.pop = pop; + + if (regInfo != null) + { + this.regInfo = new DERSequence(regInfo); + } + } + + public CertRequest getCertReq() + { + return certReq; + } + + + /** + * @deprecated use getPopo + */ + public ProofOfPossession getPop() + { + return pop; + } + + + public ProofOfPossession getPopo() + { + return pop; + } + + public AttributeTypeAndValue[] getRegInfo() + { + if (regInfo == null) + { + return null; + } + + AttributeTypeAndValue[] results = new AttributeTypeAndValue[regInfo.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = AttributeTypeAndValue.getInstance(regInfo.getObjectAt(i)); + } + + return results; + } + + /** + *
+ * CertReqMsg ::= SEQUENCE { + * certReq CertRequest, + * popo ProofOfPossession OPTIONAL, + * -- content depends upon key type + * regInfo SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue OPTIONAL } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certReq); + + addOptional(v, pop); + addOptional(v, regInfo); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, ASN1Encodable obj) + { + if (obj != null) + { + v.add(obj); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertRequest.java new file mode 100644 index 000000000..7497aa654 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertRequest.java @@ -0,0 +1,97 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class CertRequest + extends ASN1Object +{ + private ASN1Integer certReqId; + private CertTemplate certTemplate; + private Controls controls; + + private CertRequest(ASN1Sequence seq) + { + certReqId = new ASN1Integer(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue()); + certTemplate = CertTemplate.getInstance(seq.getObjectAt(1)); + if (seq.size() > 2) + { + controls = Controls.getInstance(seq.getObjectAt(2)); + } + } + + public static CertRequest getInstance(Object o) + { + if (o instanceof CertRequest) + { + return (CertRequest)o; + } + else if (o != null) + { + return new CertRequest(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertRequest( + int certReqId, + CertTemplate certTemplate, + Controls controls) + { + this(new ASN1Integer(certReqId), certTemplate, controls); + } + + public CertRequest( + ASN1Integer certReqId, + CertTemplate certTemplate, + Controls controls) + { + this.certReqId = certReqId; + this.certTemplate = certTemplate; + this.controls = controls; + } + + public ASN1Integer getCertReqId() + { + return certReqId; + } + + public CertTemplate getCertTemplate() + { + return certTemplate; + } + + public Controls getControls() + { + return controls; + } + + /** + *
+ * CertRequest ::= SEQUENCE { + * certReqId INTEGER, -- ID for matching request and reply + * certTemplate CertTemplate, -- Selected fields of cert to be issued + * controls Controls OPTIONAL } -- Attributes affecting issuance + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certReqId); + v.add(certTemplate); + + if (controls != null) + { + v.add(controls); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertTemplate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertTemplate.java new file mode 100644 index 000000000..cb32d3f50 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertTemplate.java @@ -0,0 +1,163 @@ +package org.spongycastle.asn1.crmf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; + +public class CertTemplate + extends ASN1Object +{ + private ASN1Sequence seq; + + private ASN1Integer version; + private ASN1Integer serialNumber; + private AlgorithmIdentifier signingAlg; + private X500Name issuer; + private OptionalValidity validity; + private X500Name subject; + private SubjectPublicKeyInfo publicKey; + private DERBitString issuerUID; + private DERBitString subjectUID; + private Extensions extensions; + + private CertTemplate(ASN1Sequence seq) + { + this.seq = seq; + + Enumeration en = seq.getObjects(); + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement(); + + switch (tObj.getTagNo()) + { + case 0: + version = ASN1Integer.getInstance(tObj, false); + break; + case 1: + serialNumber = ASN1Integer.getInstance(tObj, false); + break; + case 2: + signingAlg = AlgorithmIdentifier.getInstance(tObj, false); + break; + case 3: + issuer = X500Name.getInstance(tObj, true); // CHOICE + break; + case 4: + validity = OptionalValidity.getInstance(ASN1Sequence.getInstance(tObj, false)); + break; + case 5: + subject = X500Name.getInstance(tObj, true); // CHOICE + break; + case 6: + publicKey = SubjectPublicKeyInfo.getInstance(tObj, false); + break; + case 7: + issuerUID = DERBitString.getInstance(tObj, false); + break; + case 8: + subjectUID = DERBitString.getInstance(tObj, false); + break; + case 9: + extensions = Extensions.getInstance(tObj, false); + break; + default: + throw new IllegalArgumentException("unknown tag: " + tObj.getTagNo()); + } + } + } + + public static CertTemplate getInstance(Object o) + { + if (o instanceof CertTemplate) + { + return (CertTemplate)o; + } + else if (o != null) + { + return new CertTemplate(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public int getVersion() + { + return version.getValue().intValue(); + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + public AlgorithmIdentifier getSigningAlg() + { + return signingAlg; + } + + public X500Name getIssuer() + { + return issuer; + } + + public OptionalValidity getValidity() + { + return validity; + } + + public X500Name getSubject() + { + return subject; + } + + public SubjectPublicKeyInfo getPublicKey() + { + return publicKey; + } + + public DERBitString getIssuerUID() + { + return issuerUID; + } + + public DERBitString getSubjectUID() + { + return subjectUID; + } + + public Extensions getExtensions() + { + return extensions; + } + + /** + *
+ * CertTemplate ::= SEQUENCE { + * version [0] Version OPTIONAL, + * serialNumber [1] INTEGER OPTIONAL, + * signingAlg [2] AlgorithmIdentifier OPTIONAL, + * issuer [3] Name OPTIONAL, + * validity [4] OptionalValidity OPTIONAL, + * subject [5] Name OPTIONAL, + * publicKey [6] SubjectPublicKeyInfo OPTIONAL, + * issuerUID [7] UniqueIdentifier OPTIONAL, + * subjectUID [8] UniqueIdentifier OPTIONAL, + * extensions [9] Extensions OPTIONAL } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertTemplateBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertTemplateBuilder.java new file mode 100644 index 000000000..4d5957757 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/CertTemplateBuilder.java @@ -0,0 +1,152 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509Extensions; + +public class CertTemplateBuilder +{ + private ASN1Integer version; + private ASN1Integer serialNumber; + private AlgorithmIdentifier signingAlg; + private X500Name issuer; + private OptionalValidity validity; + private X500Name subject; + private SubjectPublicKeyInfo publicKey; + private DERBitString issuerUID; + private DERBitString subjectUID; + private Extensions extensions; + + /** Sets the X.509 version. Note: for X509v3, use 2 here. */ + public CertTemplateBuilder setVersion(int ver) + { + version = new ASN1Integer(ver); + + return this; + } + + public CertTemplateBuilder setSerialNumber(ASN1Integer ser) + { + serialNumber = ser; + + return this; + } + + public CertTemplateBuilder setSigningAlg(AlgorithmIdentifier aid) + { + signingAlg = aid; + + return this; + } + + public CertTemplateBuilder setIssuer(X500Name name) + { + issuer = name; + + return this; + } + + public CertTemplateBuilder setValidity(OptionalValidity v) + { + validity = v; + + return this; + } + + public CertTemplateBuilder setSubject(X500Name name) + { + subject = name; + + return this; + } + + public CertTemplateBuilder setPublicKey(SubjectPublicKeyInfo spki) + { + publicKey = spki; + + return this; + } + + /** Sets the issuer unique ID (deprecated in X.509v3) */ + public CertTemplateBuilder setIssuerUID(DERBitString uid) + { + issuerUID = uid; + + return this; + } + + /** Sets the subject unique ID (deprecated in X.509v3) */ + public CertTemplateBuilder setSubjectUID(DERBitString uid) + { + subjectUID = uid; + + return this; + } + + /** + * @deprecated use method taking Extensions + * @param extens + * @return + */ + public CertTemplateBuilder setExtensions(X509Extensions extens) + { + return setExtensions(Extensions.getInstance(extens)); + } + + public CertTemplateBuilder setExtensions(Extensions extens) + { + extensions = extens; + + return this; + } + + /** + *
+ * CertTemplate ::= SEQUENCE { + * version [0] Version OPTIONAL, + * serialNumber [1] INTEGER OPTIONAL, + * signingAlg [2] AlgorithmIdentifier OPTIONAL, + * issuer [3] Name OPTIONAL, + * validity [4] OptionalValidity OPTIONAL, + * subject [5] Name OPTIONAL, + * publicKey [6] SubjectPublicKeyInfo OPTIONAL, + * issuerUID [7] UniqueIdentifier OPTIONAL, + * subjectUID [8] UniqueIdentifier OPTIONAL, + * extensions [9] Extensions OPTIONAL } + *+ * @return a basic ASN.1 object representation. + */ + public CertTemplate build() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + addOptional(v, 0, false, version); + addOptional(v, 1, false, serialNumber); + addOptional(v, 2, false, signingAlg); + addOptional(v, 3, true, issuer); // CHOICE + addOptional(v, 4, false, validity); + addOptional(v, 5, true, subject); // CHOICE + addOptional(v, 6, false, publicKey); + addOptional(v, 7, false, issuerUID); + addOptional(v, 8, false, subjectUID); + addOptional(v, 9, false, extensions); + + return CertTemplate.getInstance(new DERSequence(v)); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, boolean isExplicit, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(isExplicit, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/Controls.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/Controls.java new file mode 100644 index 000000000..89b1f2dac --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/Controls.java @@ -0,0 +1,72 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class Controls + extends ASN1Object +{ + private ASN1Sequence content; + + private Controls(ASN1Sequence seq) + { + content = seq; + } + + public static Controls getInstance(Object o) + { + if (o instanceof Controls) + { + return (Controls)o; + } + + if (o != null) + { + return new Controls(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public Controls(AttributeTypeAndValue atv) + { + content = new DERSequence(atv); + } + + public Controls(AttributeTypeAndValue[] atvs) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i < atvs.length; i++) + { + v.add(atvs[i]); + } + content = new DERSequence(v); + } + + public AttributeTypeAndValue[] toAttributeTypeAndValueArray() + { + AttributeTypeAndValue[] result = new AttributeTypeAndValue[content.size()]; + + for (int i = 0; i != result.length; i++) + { + result[i] = AttributeTypeAndValue.getInstance(content.getObjectAt(i)); + } + + return result; + } + + /** + *
+ * Controls ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue + *+ * + * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return content; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncKeyWithID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncKeyWithID.java new file mode 100644 index 000000000..a89aa6b51 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncKeyWithID.java @@ -0,0 +1,117 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.GeneralName; + +public class EncKeyWithID + extends ASN1Object +{ + private final PrivateKeyInfo privKeyInfo; + private final ASN1Encodable identifier; + + public static EncKeyWithID getInstance(Object o) + { + if (o instanceof EncKeyWithID) + { + return (EncKeyWithID)o; + } + else if (o != null) + { + return new EncKeyWithID(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private EncKeyWithID(ASN1Sequence seq) + { + this.privKeyInfo = PrivateKeyInfo.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + if (!(seq.getObjectAt(1) instanceof DERUTF8String)) + { + this.identifier = GeneralName.getInstance(seq.getObjectAt(1)); + } + else + { + this.identifier = (ASN1Encodable)seq.getObjectAt(1); + } + } + else + { + this.identifier = null; + } + } + + public EncKeyWithID(PrivateKeyInfo privKeyInfo) + { + this.privKeyInfo = privKeyInfo; + this.identifier = null; + } + + public EncKeyWithID(PrivateKeyInfo privKeyInfo, DERUTF8String str) + { + this.privKeyInfo = privKeyInfo; + this.identifier = str; + } + + public EncKeyWithID(PrivateKeyInfo privKeyInfo, GeneralName generalName) + { + this.privKeyInfo = privKeyInfo; + this.identifier = generalName; + } + + public PrivateKeyInfo getPrivateKey() + { + return privKeyInfo; + } + + public boolean hasIdentifier() + { + return identifier != null; + } + + public boolean isIdentifierUTF8String() + { + return identifier instanceof DERUTF8String; + } + + public ASN1Encodable getIdentifier() + { + return identifier; + } + + /** + *
+ * EncKeyWithID ::= SEQUENCE { + * privateKey PrivateKeyInfo, + * identifier CHOICE { + * string UTF8String, + * generalName GeneralName + * } OPTIONAL + * } + *+ * @return + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(privKeyInfo); + + if (identifier != null) + { + v.add(identifier); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncryptedKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncryptedKey.java new file mode 100644 index 000000000..d62aee760 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncryptedKey.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cms.EnvelopedData; + +public class EncryptedKey + extends ASN1Object + implements ASN1Choice +{ + private EnvelopedData envelopedData; + private EncryptedValue encryptedValue; + + public static EncryptedKey getInstance(Object o) + { + if (o instanceof EncryptedKey) + { + return (EncryptedKey)o; + } + else if (o instanceof ASN1TaggedObject) + { + return new EncryptedKey(EnvelopedData.getInstance((ASN1TaggedObject)o, false)); + } + else if (o instanceof EncryptedValue) + { + return new EncryptedKey((EncryptedValue)o); + } + else + { + return new EncryptedKey(EncryptedValue.getInstance(o)); + } + } + + public EncryptedKey(EnvelopedData envelopedData) + { + this.envelopedData = envelopedData; + } + + public EncryptedKey(EncryptedValue encryptedValue) + { + this.encryptedValue = encryptedValue; + } + + public boolean isEncryptedValue() + { + return encryptedValue != null; + } + + public ASN1Encodable getValue() + { + if (encryptedValue != null) + { + return encryptedValue; + } + + return envelopedData; + } + + /** + *
+ * EncryptedKey ::= CHOICE { + * encryptedValue EncryptedValue, -- deprecated + * envelopedData [0] EnvelopedData } + * -- The encrypted private key MUST be placed in the envelopedData + * -- encryptedContentInfo encryptedContent OCTET STRING. + *+ */ + public ASN1Primitive toASN1Primitive() + { + if (encryptedValue != null) + { + return encryptedValue.toASN1Primitive(); + } + + return new DERTaggedObject(false, 0, envelopedData); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncryptedValue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncryptedValue.java new file mode 100644 index 000000000..6191e3dcc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/EncryptedValue.java @@ -0,0 +1,164 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class EncryptedValue + extends ASN1Object +{ + private AlgorithmIdentifier intendedAlg; + private AlgorithmIdentifier symmAlg; + private DERBitString encSymmKey; + private AlgorithmIdentifier keyAlg; + private ASN1OctetString valueHint; + private DERBitString encValue; + + private EncryptedValue(ASN1Sequence seq) + { + int index = 0; + while (seq.getObjectAt(index) instanceof ASN1TaggedObject) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)seq.getObjectAt(index); + + switch (tObj.getTagNo()) + { + case 0: + intendedAlg = AlgorithmIdentifier.getInstance(tObj, false); + break; + case 1: + symmAlg = AlgorithmIdentifier.getInstance(tObj, false); + break; + case 2: + encSymmKey = DERBitString.getInstance(tObj, false); + break; + case 3: + keyAlg = AlgorithmIdentifier.getInstance(tObj, false); + break; + case 4: + valueHint = ASN1OctetString.getInstance(tObj, false); + break; + } + index++; + } + + encValue = DERBitString.getInstance(seq.getObjectAt(index)); + } + + public static EncryptedValue getInstance(Object o) + { + if (o instanceof EncryptedValue) + { + return (EncryptedValue)o; + } + else if (o != null) + { + return new EncryptedValue(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public EncryptedValue( + AlgorithmIdentifier intendedAlg, + AlgorithmIdentifier symmAlg, + DERBitString encSymmKey, + AlgorithmIdentifier keyAlg, + ASN1OctetString valueHint, + DERBitString encValue) + { + if (encValue == null) + { + throw new IllegalArgumentException("'encValue' cannot be null"); + } + + this.intendedAlg = intendedAlg; + this.symmAlg = symmAlg; + this.encSymmKey = encSymmKey; + this.keyAlg = keyAlg; + this.valueHint = valueHint; + this.encValue = encValue; + } + + public AlgorithmIdentifier getIntendedAlg() + { + return intendedAlg; + } + + public AlgorithmIdentifier getSymmAlg() + { + return symmAlg; + } + + public DERBitString getEncSymmKey() + { + return encSymmKey; + } + + public AlgorithmIdentifier getKeyAlg() + { + return keyAlg; + } + + public ASN1OctetString getValueHint() + { + return valueHint; + } + + public DERBitString getEncValue() + { + return encValue; + } + + /** + *
+ * EncryptedValue ::= SEQUENCE { + * intendedAlg [0] AlgorithmIdentifier OPTIONAL, + * -- the intended algorithm for which the value will be used + * symmAlg [1] AlgorithmIdentifier OPTIONAL, + * -- the symmetric algorithm used to encrypt the value + * encSymmKey [2] BIT STRING OPTIONAL, + * -- the (encrypted) symmetric key used to encrypt the value + * keyAlg [3] AlgorithmIdentifier OPTIONAL, + * -- algorithm used to encrypt the symmetric key + * valueHint [4] OCTET STRING OPTIONAL, + * -- a brief description or identifier of the encValue content + * -- (may be meaningful only to the sending entity, and used only + * -- if EncryptedValue might be re-examined by the sending entity + * -- in the future) + * encValue BIT STRING } + * -- the encrypted value itself + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + addOptional(v, 0, intendedAlg); + addOptional(v, 1, symmAlg); + addOptional(v, 2, encSymmKey); + addOptional(v, 3, keyAlg); + addOptional(v, 4, valueHint); + + v.add(encValue); + + return new DERSequence(v); + } + + private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj) + { + if (obj != null) + { + v.add(new DERTaggedObject(false, tagNo, obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/OptionalValidity.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/OptionalValidity.java new file mode 100644 index 000000000..240d03b36 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/OptionalValidity.java @@ -0,0 +1,98 @@ +package org.spongycastle.asn1.crmf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Time; + +public class OptionalValidity + extends ASN1Object +{ + private Time notBefore; + private Time notAfter; + + private OptionalValidity(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement(); + + if (tObj.getTagNo() == 0) + { + notBefore = Time.getInstance(tObj, true); + } + else + { + notAfter = Time.getInstance(tObj, true); + } + } + } + + public static OptionalValidity getInstance(Object o) + { + if (o instanceof OptionalValidity) + { + return (OptionalValidity)o; + } + + if (o != null) + { + return new OptionalValidity(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public OptionalValidity(Time notBefore, Time notAfter) + { + if (notBefore == null && notAfter == null) + { + throw new IllegalArgumentException("at least one of notBefore/notAfter must not be null."); + } + + this.notBefore = notBefore; + this.notAfter = notAfter; + } + + public Time getNotBefore() + { + return notBefore; + } + + public Time getNotAfter() + { + return notAfter; + } + + /** + *
+ * OptionalValidity ::= SEQUENCE { + * notBefore [0] Time OPTIONAL, + * notAfter [1] Time OPTIONAL } --at least one MUST be present + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (notBefore != null) + { + v.add(new DERTaggedObject(true, 0, notBefore)); + } + + if (notAfter != null) + { + v.add(new DERTaggedObject(true, 1, notAfter)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKIArchiveOptions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKIArchiveOptions.java new file mode 100644 index 000000000..3c374fb0b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKIArchiveOptions.java @@ -0,0 +1,116 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +public class PKIArchiveOptions + extends ASN1Object + implements ASN1Choice +{ + public static final int encryptedPrivKey = 0; + public static final int keyGenParameters = 1; + public static final int archiveRemGenPrivKey = 2; + + private ASN1Encodable value; + + public static PKIArchiveOptions getInstance(Object o) + { + if (o == null || o instanceof PKIArchiveOptions) + { + return (PKIArchiveOptions)o; + } + else if (o instanceof ASN1TaggedObject) + { + return new PKIArchiveOptions((ASN1TaggedObject)o); + } + + throw new IllegalArgumentException("unknown object: " + o); + } + + private PKIArchiveOptions(ASN1TaggedObject tagged) + { + switch (tagged.getTagNo()) + { + case encryptedPrivKey: + value = EncryptedKey.getInstance(tagged.getObject()); + break; + case keyGenParameters: + value = ASN1OctetString.getInstance(tagged, false); + break; + case archiveRemGenPrivKey: + value = ASN1Boolean.getInstance(tagged, false); + break; + default: + throw new IllegalArgumentException("unknown tag number: " + tagged.getTagNo()); + } + } + + public PKIArchiveOptions(EncryptedKey encKey) + { + this.value = encKey; + } + + public PKIArchiveOptions(ASN1OctetString keyGenParameters) + { + this.value = keyGenParameters; + } + + public PKIArchiveOptions(boolean archiveRemGenPrivKey) + { + this.value = ASN1Boolean.getInstance(archiveRemGenPrivKey); + } + + public int getType() + { + if (value instanceof EncryptedKey) + { + return encryptedPrivKey; + } + + if (value instanceof ASN1OctetString) + { + return keyGenParameters; + } + + return archiveRemGenPrivKey; + } + + public ASN1Encodable getValue() + { + return value; + } + + /** + *
+ * PKIArchiveOptions ::= CHOICE { + * encryptedPrivKey [0] EncryptedKey, + * -- the actual value of the private key + * keyGenParameters [1] KeyGenParameters, + * -- parameters which allow the private key to be re-generated + * archiveRemGenPrivKey [2] BOOLEAN } + * -- set to TRUE if sender wishes receiver to archive the private + * -- key of a key pair that the receiver generates in response to + * -- this request; set to FALSE if no archival is desired. + *+ */ + public ASN1Primitive toASN1Primitive() + { + if (value instanceof EncryptedKey) + { + return new DERTaggedObject(true, encryptedPrivKey, value); // choice + } + + if (value instanceof ASN1OctetString) + { + return new DERTaggedObject(false, keyGenParameters, value); + } + + return new DERTaggedObject(false, archiveRemGenPrivKey, value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKIPublicationInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKIPublicationInfo.java new file mode 100644 index 000000000..d19d3f64a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKIPublicationInfo.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class PKIPublicationInfo + extends ASN1Object +{ + private ASN1Integer action; + private ASN1Sequence pubInfos; + + private PKIPublicationInfo(ASN1Sequence seq) + { + action = ASN1Integer.getInstance(seq.getObjectAt(0)); + pubInfos = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + + public static PKIPublicationInfo getInstance(Object o) + { + if (o instanceof PKIPublicationInfo) + { + return (PKIPublicationInfo)o; + } + + if (o != null) + { + return new PKIPublicationInfo(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ASN1Integer getAction() + { + return action; + } + + public SinglePubInfo[] getPubInfos() + { + if (pubInfos == null) + { + return null; + } + + SinglePubInfo[] results = new SinglePubInfo[pubInfos.size()]; + + for (int i = 0; i != results.length; i++) + { + results[i] = SinglePubInfo.getInstance(pubInfos.getObjectAt(i)); + } + + return results; + } + + /** + *
+ * PKIPublicationInfo ::= SEQUENCE { + * action INTEGER { + * dontPublish (0), + * pleasePublish (1) }, + * pubInfos SEQUENCE SIZE (1..MAX) OF SinglePubInfo OPTIONAL } + * -- pubInfos MUST NOT be present if action is "dontPublish" + * -- (if action is "pleasePublish" and pubInfos is omitted, + * -- "dontCare" is assumed) + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(action); + v.add(pubInfos); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKMACValue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKMACValue.java new file mode 100644 index 000000000..c3481df02 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/PKMACValue.java @@ -0,0 +1,104 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.cmp.CMPObjectIdentifiers; +import org.spongycastle.asn1.cmp.PBMParameter; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * Password-based MAC value for use with POPOSigningKeyInput. + */ +public class PKMACValue + extends ASN1Object +{ + private AlgorithmIdentifier algId; + private DERBitString value; + + private PKMACValue(ASN1Sequence seq) + { + algId = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + value = DERBitString.getInstance(seq.getObjectAt(1)); + } + + public static PKMACValue getInstance(Object o) + { + if (o instanceof PKMACValue) + { + return (PKMACValue)o; + } + + if (o != null) + { + return new PKMACValue(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public static PKMACValue getInstance(ASN1TaggedObject obj, boolean isExplicit) + { + return getInstance(ASN1Sequence.getInstance(obj, isExplicit)); + } + + /** + * Creates a new PKMACValue. + * @param params parameters for password-based MAC + * @param value MAC of the DER-encoded SubjectPublicKeyInfo + */ + public PKMACValue( + PBMParameter params, + DERBitString value) + { + this(new AlgorithmIdentifier( + CMPObjectIdentifiers.passwordBasedMac, params), value); + } + + /** + * Creates a new PKMACValue. + * @param aid CMPObjectIdentifiers.passwordBasedMAC, with PBMParameter + * @param value MAC of the DER-encoded SubjectPublicKeyInfo + */ + public PKMACValue( + AlgorithmIdentifier aid, + DERBitString value) + { + this.algId = aid; + this.value = value; + } + + public AlgorithmIdentifier getAlgId() + { + return algId; + } + + public DERBitString getValue() + { + return value; + } + + /** + *
+ * PKMACValue ::= SEQUENCE { + * algId AlgorithmIdentifier, + * -- algorithm value shall be PasswordBasedMac 1.2.840.113533.7.66.13 + * -- parameter value is PBMParameter + * value BIT STRING } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(algId); + v.add(value); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOPrivKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOPrivKey.java new file mode 100644 index 000000000..4b32bceb3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOPrivKey.java @@ -0,0 +1,104 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cms.EnvelopedData; + +public class POPOPrivKey + extends ASN1Object + implements ASN1Choice +{ + public static final int thisMessage = 0; + public static final int subsequentMessage = 1; + public static final int dhMAC = 2; + public static final int agreeMAC = 3; + public static final int encryptedKey = 4; + + private int tagNo; + private ASN1Encodable obj; + + private POPOPrivKey(ASN1TaggedObject obj) + { + this.tagNo = obj.getTagNo(); + + switch (tagNo) + { + case thisMessage: + this.obj = DERBitString.getInstance(obj, false); + break; + case subsequentMessage: + this.obj = SubsequentMessage.valueOf(ASN1Integer.getInstance(obj, false).getValue().intValue()); + break; + case dhMAC: + this.obj = DERBitString.getInstance(obj, false); + break; + case agreeMAC: + this.obj = PKMACValue.getInstance(obj, false); + break; + case encryptedKey: + this.obj = EnvelopedData.getInstance(obj, false); + break; + default: + throw new IllegalArgumentException("unknown tag in POPOPrivKey"); + } + } + + public static POPOPrivKey getInstance(Object obj) + { + if (obj instanceof POPOPrivKey) + { + return (POPOPrivKey)obj; + } + if (obj != null) + { + return new POPOPrivKey(ASN1TaggedObject.getInstance(obj)); + } + + return null; + } + + public static POPOPrivKey getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1TaggedObject.getInstance(obj, explicit)); + } + + public POPOPrivKey(SubsequentMessage msg) + { + this.tagNo = subsequentMessage; + this.obj = msg; + } + + public int getType() + { + return tagNo; + } + + public ASN1Encodable getValue() + { + return obj; + } + + /** + *
+ * POPOPrivKey ::= CHOICE { + * thisMessage [0] BIT STRING, -- Deprecated + * -- possession is proven in this message (which contains the private + * -- key itself (encrypted for the CA)) + * subsequentMessage [1] SubsequentMessage, + * -- possession will be proven in a subsequent message + * dhMAC [2] BIT STRING, -- Deprecated + * agreeMAC [3] PKMACValue, + * encryptedKey [4] EnvelopedData } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return new DERTaggedObject(false, tagNo, obj); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOSigningKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOSigningKey.java new file mode 100644 index 000000000..9f300c0dd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOSigningKey.java @@ -0,0 +1,127 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class POPOSigningKey + extends ASN1Object +{ + private POPOSigningKeyInput poposkInput; + private AlgorithmIdentifier algorithmIdentifier; + private DERBitString signature; + + private POPOSigningKey(ASN1Sequence seq) + { + int index = 0; + + if (seq.getObjectAt(index) instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagObj + = (ASN1TaggedObject)seq.getObjectAt(index++); + if (tagObj.getTagNo() != 0) + { + throw new IllegalArgumentException( + "Unknown POPOSigningKeyInput tag: " + tagObj.getTagNo()); + } + poposkInput = POPOSigningKeyInput.getInstance(tagObj.getObject()); + } + algorithmIdentifier = AlgorithmIdentifier.getInstance(seq.getObjectAt(index++)); + signature = DERBitString.getInstance(seq.getObjectAt(index)); + } + + public static POPOSigningKey getInstance(Object o) + { + if (o instanceof POPOSigningKey) + { + return (POPOSigningKey)o; + } + + if (o != null) + { + return new POPOSigningKey(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public static POPOSigningKey getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Creates a new Proof of Possession object for a signing key. + * + * @param poposkIn the POPOSigningKeyInput structure, or null if the + * CertTemplate includes both subject and publicKey values. + * @param aid the AlgorithmIdentifier used to sign the proof of possession. + * @param signature a signature over the DER-encoded value of poposkIn, + * or the DER-encoded value of certReq if poposkIn is null. + */ + public POPOSigningKey( + POPOSigningKeyInput poposkIn, + AlgorithmIdentifier aid, + DERBitString signature) + { + this.poposkInput = poposkIn; + this.algorithmIdentifier = aid; + this.signature = signature; + } + + public POPOSigningKeyInput getPoposkInput() + { + return poposkInput; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithmIdentifier; + } + + public DERBitString getSignature() + { + return signature; + } + + /** + *
+ * POPOSigningKey ::= SEQUENCE { + * poposkInput [0] POPOSigningKeyInput OPTIONAL, + * algorithmIdentifier AlgorithmIdentifier, + * signature BIT STRING } + * -- The signature (using "algorithmIdentifier") is on the + * -- DER-encoded value of poposkInput. NOTE: If the CertReqMsg + * -- certReq CertTemplate contains the subject and publicKey values, + * -- then poposkInput MUST be omitted and the signature MUST be + * -- computed on the DER-encoded value of CertReqMsg certReq. If + * -- the CertReqMsg certReq CertTemplate does not contain the public + * -- key and subject values, then poposkInput MUST be present and + * -- MUST be signed. This strategy ensures that the public key is + * -- not present in both the poposkInput and CertReqMsg certReq + * -- CertTemplate fields. + *+ * + * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (poposkInput != null) + { + v.add(new DERTaggedObject(false, 0, poposkInput)); + } + + v.add(algorithmIdentifier); + v.add(signature); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOSigningKeyInput.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOSigningKeyInput.java new file mode 100644 index 000000000..5b0c90f3a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/POPOSigningKeyInput.java @@ -0,0 +1,134 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; + +public class POPOSigningKeyInput + extends ASN1Object +{ + private GeneralName sender; + private PKMACValue publicKeyMAC; + private SubjectPublicKeyInfo publicKey; + + private POPOSigningKeyInput(ASN1Sequence seq) + { + ASN1Encodable authInfo = (ASN1Encodable)seq.getObjectAt(0); + + if (authInfo instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagObj = (ASN1TaggedObject)authInfo; + if (tagObj.getTagNo() != 0) + { + throw new IllegalArgumentException( + "Unknown authInfo tag: " + tagObj.getTagNo()); + } + sender = GeneralName.getInstance(tagObj.getObject()); + } + else + { + publicKeyMAC = PKMACValue.getInstance(authInfo); + } + + publicKey = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(1)); + } + + public static POPOSigningKeyInput getInstance(Object o) + { + if (o instanceof POPOSigningKeyInput) + { + return (POPOSigningKeyInput)o; + } + + if (o != null) + { + return new POPOSigningKeyInput(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * Creates a new POPOSigningKeyInput with sender name as authInfo. + */ + public POPOSigningKeyInput( + GeneralName sender, + SubjectPublicKeyInfo spki) + { + this.sender = sender; + this.publicKey = spki; + } + + /** + * Creates a new POPOSigningKeyInput using password-based MAC. + */ + public POPOSigningKeyInput( + PKMACValue pkmac, + SubjectPublicKeyInfo spki) + { + this.publicKeyMAC = pkmac; + this.publicKey = spki; + } + + /** + * Returns the sender field, or null if authInfo is publicKeyMAC + */ + public GeneralName getSender() + { + return sender; + } + + /** + * Returns the publicKeyMAC field, or null if authInfo is sender + */ + public PKMACValue getPublicKeyMAC() + { + return publicKeyMAC; + } + + public SubjectPublicKeyInfo getPublicKey() + { + return publicKey; + } + + /** + *
+ * POPOSigningKeyInput ::= SEQUENCE { + * authInfo CHOICE { + * sender [0] GeneralName, + * -- used only if an authenticated identity has been + * -- established for the sender (e.g., a DN from a + * -- previously-issued and currently-valid certificate + * publicKeyMAC PKMACValue }, + * -- used if no authenticated GeneralName currently exists for + * -- the sender; publicKeyMAC contains a password-based MAC + * -- on the DER-encoded value of publicKey + * publicKey SubjectPublicKeyInfo } -- from CertTemplate + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (sender != null) + { + v.add(new DERTaggedObject(false, 0, sender)); + } + else + { + v.add(publicKeyMAC); + } + + v.add(publicKey); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/ProofOfPossession.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/ProofOfPossession.java new file mode 100644 index 000000000..e1ace1168 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/ProofOfPossession.java @@ -0,0 +1,108 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DERTaggedObject; + +public class ProofOfPossession + extends ASN1Object + implements ASN1Choice +{ + public static final int TYPE_RA_VERIFIED = 0; + public static final int TYPE_SIGNING_KEY = 1; + public static final int TYPE_KEY_ENCIPHERMENT = 2; + public static final int TYPE_KEY_AGREEMENT = 3; + + private int tagNo; + private ASN1Encodable obj; + + private ProofOfPossession(ASN1TaggedObject tagged) + { + tagNo = tagged.getTagNo(); + switch (tagNo) + { + case 0: + obj = DERNull.INSTANCE; + break; + case 1: + obj = POPOSigningKey.getInstance(tagged, false); + break; + case 2: + case 3: + obj = POPOPrivKey.getInstance(tagged, true); + break; + default: + throw new IllegalArgumentException("unknown tag: " + tagNo); + } + } + + public static ProofOfPossession getInstance(Object o) + { + if (o == null || o instanceof ProofOfPossession) + { + return (ProofOfPossession)o; + } + + if (o instanceof ASN1TaggedObject) + { + return new ProofOfPossession((ASN1TaggedObject)o); + } + + throw new IllegalArgumentException("Invalid object: " + o.getClass().getName()); + } + + /** Creates a ProofOfPossession with type raVerified. */ + public ProofOfPossession() + { + tagNo = TYPE_RA_VERIFIED; + obj = DERNull.INSTANCE; + } + + /** Creates a ProofOfPossession for a signing key. */ + public ProofOfPossession(POPOSigningKey poposk) + { + tagNo = TYPE_SIGNING_KEY; + obj = poposk; + } + + /** + * Creates a ProofOfPossession for key encipherment or agreement. + * @param type one of TYPE_KEY_ENCIPHERMENT or TYPE_KEY_AGREEMENT + */ + public ProofOfPossession(int type, POPOPrivKey privkey) + { + tagNo = type; + obj = privkey; + } + + public int getType() + { + return tagNo; + } + + public ASN1Encodable getObject() + { + return obj; + } + + /** + *
+ * ProofOfPossession ::= CHOICE { + * raVerified [0] NULL, + * -- used if the RA has already verified that the requester is in + * -- possession of the private key + * signature [1] POPOSigningKey, + * keyEncipherment [2] POPOPrivKey, + * keyAgreement [3] POPOPrivKey } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + return new DERTaggedObject(false, tagNo, obj); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/SinglePubInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/SinglePubInfo.java new file mode 100644 index 000000000..52f21c991 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/SinglePubInfo.java @@ -0,0 +1,72 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.GeneralName; + +public class SinglePubInfo + extends ASN1Object +{ + private ASN1Integer pubMethod; + private GeneralName pubLocation; + + private SinglePubInfo(ASN1Sequence seq) + { + pubMethod = ASN1Integer.getInstance(seq.getObjectAt(0)); + + if (seq.size() == 2) + { + pubLocation = GeneralName.getInstance(seq.getObjectAt(1)); + } + } + + public static SinglePubInfo getInstance(Object o) + { + if (o instanceof SinglePubInfo) + { + return (SinglePubInfo)o; + } + + if (o != null) + { + return new SinglePubInfo(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public GeneralName getPubLocation() + { + return pubLocation; + } + + /** + *
+ * SinglePubInfo ::= SEQUENCE { + * pubMethod INTEGER { + * dontCare (0), + * x500 (1), + * web (2), + * ldap (3) }, + * pubLocation GeneralName OPTIONAL } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(pubMethod); + + if (pubLocation != null) + { + v.add(pubLocation); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/SubsequentMessage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/SubsequentMessage.java new file mode 100644 index 000000000..c646faf54 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/crmf/SubsequentMessage.java @@ -0,0 +1,29 @@ +package org.spongycastle.asn1.crmf; + +import org.spongycastle.asn1.ASN1Integer; + +public class SubsequentMessage + extends ASN1Integer +{ + public static final SubsequentMessage encrCert = new SubsequentMessage(0); + public static final SubsequentMessage challengeResp = new SubsequentMessage(1); + + private SubsequentMessage(int value) + { + super(value); + } + + public static SubsequentMessage valueOf(int value) + { + if (value == 0) + { + return encrCert; + } + if (value == 1) + { + return challengeResp; + } + + throw new IllegalArgumentException("unknown value: " + value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java new file mode 100644 index 000000000..bdcf9284a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java @@ -0,0 +1,101 @@ +package org.spongycastle.asn1.cryptopro; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + *
+ * GOST Algorithms OBJECT IDENTIFIERS : + * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)} + *+ */ +public interface CryptoProObjectIdentifiers +{ + /** Base OID: 1.2.643.2.2 */ + static final ASN1ObjectIdentifier GOST_id = new ASN1ObjectIdentifier("1.2.643.2.2"); + + /** Gost R3411 OID: 1.2.643.2.2.9 */ + static final ASN1ObjectIdentifier gostR3411 = GOST_id.branch("9"); + /** Gost R3411 HMAC OID: 1.2.643.2.2.10 */ + static final ASN1ObjectIdentifier gostR3411Hmac = GOST_id.branch("10"); + + /** Gost R28147 OID: 1.2.643.2.2.21 */ + static final ASN1ObjectIdentifier gostR28147_gcfb = GOST_id.branch("21"); + + /** Gost R28147-89-CryotoPro-A-ParamSet OID: 1.2.643.2.2.31.1 */ + static final ASN1ObjectIdentifier id_Gost28147_89_CryptoPro_A_ParamSet = GOST_id.branch("31.1"); + + /** Gost R28147-89-CryotoPro-B-ParamSet OID: 1.2.643.2.2.31.2 */ + static final ASN1ObjectIdentifier id_Gost28147_89_CryptoPro_B_ParamSet = GOST_id.branch("31.2"); + + /** Gost R28147-89-CryotoPro-C-ParamSet OID: 1.2.643.2.2.31.3 */ + static final ASN1ObjectIdentifier id_Gost28147_89_CryptoPro_C_ParamSet = GOST_id.branch("31.3"); + + /** Gost R28147-89-CryotoPro-D-ParamSet OID: 1.2.643.2.2.31.4 */ + static final ASN1ObjectIdentifier id_Gost28147_89_CryptoPro_D_ParamSet = GOST_id.branch("31.4"); + + /** Gost R3410-94 OID: 1.2.643.2.2.20 */ + static final ASN1ObjectIdentifier gostR3410_94 = GOST_id.branch("20"); + /** Gost R3410-2001 OID: 1.2.643.2.2.19 */ + static final ASN1ObjectIdentifier gostR3410_2001 = GOST_id.branch("19"); + + /** Gost R3411-94-with-R3410-94 OID: 1.2.643.2.2.4 */ + static final ASN1ObjectIdentifier gostR3411_94_with_gostR3410_94 = GOST_id.branch("4"); + /** Gost R3411-94-with-R3410-2001 OID: 1.2.643.2.2.3 */ + static final ASN1ObjectIdentifier gostR3411_94_with_gostR3410_2001 = GOST_id.branch("3"); + + /** + * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) } + *
+ * Gost R3411-94-CryptoProParamSet OID: 1.2.643.2.2.30.1 + */ + static final ASN1ObjectIdentifier gostR3411_94_CryptoProParamSet = GOST_id.branch("30.1"); + + /** + * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) } + *
+ * Gost R3410-94-CryptoPro-A OID: 1.2.643.2.2.32.2 + */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_A = GOST_id.branch("32.2"); + /** Gost R3410-94-CryptoPro-B OID: 1.2.643.2.2.32.3 */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_B = GOST_id.branch("32.3"); + /** Gost R3410-94-CryptoPro-C OID: 1.2.643.2.2.32.4 */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_C = GOST_id.branch("32.4"); + /** Gost R3410-94-CryptoPro-D OID: 1.2.643.2.2.32.5 */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_D = GOST_id.branch("32.5"); + + /** + * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) } + *
+ * Gost R3410-94-CryptoPro-XchA OID: 1.2.643.2.2.33.1 + */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchA = GOST_id.branch("33.1"); + /** Gost R3410-94-CryptoPro-XchB OID: 1.2.643.2.2.33.2 */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchB = GOST_id.branch("33.2"); + /** Gost R3410-94-CryptoPro-XchC OID: 1.2.643.2.2.33.3 */ + static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchC = GOST_id.branch("33.3"); + + /** + * { iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) } + *
+ * Gost R3410-2001-CryptoPro-A OID: 1.2.643.2.2.35.1 + */ + static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_A = GOST_id.branch("35.1"); + /** Gost R3410-2001-CryptoPro-B OID: 1.2.643.2.2.35.2 */ + static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_B = GOST_id.branch("35.2"); + /** Gost R3410-2001-CryptoPro-C OID: 1.2.643.2.2.35.3 */ + static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_C = GOST_id.branch("35.3"); + + /** + * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) } + *
+ * Gost R3410-2001-CryptoPro-XchA OID: 1.2.643.2.2.36.0 + */ + static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_XchA = GOST_id.branch("36.0"); + /** Gost R3410-2001-CryptoPro-XchA OID: 1.2.643.2.2.36.1 */ + static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_XchB = GOST_id.branch("36.1"); + + /** Gost R3410-ElSqDH3410-default OID: 1.2.643.2.2.36.0 */ + static final ASN1ObjectIdentifier gost_ElSgDH3410_default = GOST_id.branch("36.0"); + /** Gost R3410-ElSqDH3410-1 OID: 1.2.643.2.2.36.1 */ + static final ASN1ObjectIdentifier gost_ElSgDH3410_1 = GOST_id.branch("36.1"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/ECGOST3410NamedCurves.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/ECGOST3410NamedCurves.java new file mode 100644 index 000000000..5260675e1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/ECGOST3410NamedCurves.java @@ -0,0 +1,167 @@ +package org.spongycastle.asn1.cryptopro; + +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +/** + * table of the available named parameters for GOST 3410-2001. + */ +public class ECGOST3410NamedCurves +{ + static final Hashtable objIds = new Hashtable(); + static final Hashtable params = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static + { + BigInteger mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); + BigInteger mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); + + ECCurve.Fp curve = new ECCurve.Fp( + mod_p, // p + new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a + new BigInteger("166")); // b + + ECDomainParameters ecParams = new ECDomainParameters( + curve, + curve.createPoint( + new BigInteger("1"), // x + new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y + mod_q); + + params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, ecParams); + + mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); + mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); + + curve = new ECCurve.Fp( + mod_p, // p + new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), + new BigInteger("166")); + + ecParams = new ECDomainParameters( + curve, + curve.createPoint( + new BigInteger("1"), // x + new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y + mod_q); + + params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, ecParams); + + mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823193"); //p + mod_q = new BigInteger("57896044618658097711785492504343953927102133160255826820068844496087732066703"); //q + + curve = new ECCurve.Fp( + mod_p, // p + new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a + new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595")); // b + + ecParams = new ECDomainParameters( + curve, + curve.createPoint( + new BigInteger("1"), // x + new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124")), // y + mod_q); // q + + params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, ecParams); + + mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); + mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601"); + + curve = new ECCurve.Fp( + mod_p, // p + new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), + new BigInteger("32858")); + + ecParams = new ECDomainParameters( + curve, + curve.createPoint( + new BigInteger("0"), + new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), + mod_q); + + params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, ecParams); + + mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); //p + mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601"); //q + curve = new ECCurve.Fp( + mod_p, // p + new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a + new BigInteger("32858")); // b + + ecParams = new ECDomainParameters( + curve, + curve.createPoint( + new BigInteger("0"), // x + new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), // y + mod_q); // q + + params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, ecParams); + + objIds.put("GostR3410-2001-CryptoPro-A", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A); + objIds.put("GostR3410-2001-CryptoPro-B", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B); + objIds.put("GostR3410-2001-CryptoPro-C", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C); + objIds.put("GostR3410-2001-CryptoPro-XchA", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA); + objIds.put("GostR3410-2001-CryptoPro-XchB", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB); + + names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, "GostR3410-2001-CryptoPro-A"); + names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, "GostR3410-2001-CryptoPro-B"); + names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, "GostR3410-2001-CryptoPro-C"); + names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, "GostR3410-2001-CryptoPro-XchA"); + names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, "GostR3410-2001-CryptoPro-XchB"); + } + + /** + * return the ECDomainParameters object for the given OID, null if it + * isn't present. + * + * @param oid an object identifier representing a named parameters, if present. + */ + public static ECDomainParameters getByOID( + ASN1ObjectIdentifier oid) + { + return (ECDomainParameters)params.get(oid); + } + + /** + * returns an enumeration containing the name strings for parameters + * contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } + + public static ECDomainParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(name); + + if (oid != null) + { + return (ECDomainParameters)params.get(oid); + } + + return null; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName( + ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + public static ASN1ObjectIdentifier getOID(String name) + { + return (ASN1ObjectIdentifier)objIds.get(name); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java new file mode 100644 index 000000000..3cd676c1f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java @@ -0,0 +1,99 @@ +package org.spongycastle.asn1.cryptopro; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class ECGOST3410ParamSetParameters + extends ASN1Object +{ + ASN1Integer p, q, a, b, x, y; + + public static ECGOST3410ParamSetParameters getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static ECGOST3410ParamSetParameters getInstance( + Object obj) + { + if(obj == null || obj instanceof ECGOST3410ParamSetParameters) + { + return (ECGOST3410ParamSetParameters)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new ECGOST3410ParamSetParameters((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName()); + } + + public ECGOST3410ParamSetParameters( + BigInteger a, + BigInteger b, + BigInteger p, + BigInteger q, + int x, + BigInteger y) + { + this.a = new ASN1Integer(a); + this.b = new ASN1Integer(b); + this.p = new ASN1Integer(p); + this.q = new ASN1Integer(q); + this.x = new ASN1Integer(x); + this.y = new ASN1Integer(y); + } + + public ECGOST3410ParamSetParameters( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + a = (ASN1Integer)e.nextElement(); + b = (ASN1Integer)e.nextElement(); + p = (ASN1Integer)e.nextElement(); + q = (ASN1Integer)e.nextElement(); + x = (ASN1Integer)e.nextElement(); + y = (ASN1Integer)e.nextElement(); + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getQ() + { + return q.getPositiveValue(); + } + + public BigInteger getA() + { + return a.getPositiveValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(a); + v.add(b); + v.add(p); + v.add(q); + v.add(x); + v.add(y); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST28147Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST28147Parameters.java new file mode 100644 index 000000000..7db9e235e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST28147Parameters.java @@ -0,0 +1,98 @@ +package org.spongycastle.asn1.cryptopro; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +/** + * ASN.1 algorithm identifier parameters for GOST-28147 + */ +public class GOST28147Parameters + extends ASN1Object +{ + private ASN1OctetString iv; + private ASN1ObjectIdentifier paramSet; + + public static GOST28147Parameters getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static GOST28147Parameters getInstance( + Object obj) + { + if (obj instanceof GOST28147Parameters) + { + return (GOST28147Parameters)obj; + } + + if (obj != null) + { + return new GOST28147Parameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * @deprecated use the getInstance() method. This constructor will vanish! + */ + public GOST28147Parameters( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + iv = (ASN1OctetString)e.nextElement(); + paramSet = (ASN1ObjectIdentifier)e.nextElement(); + } + + /** + *
+ * Gost28147-89-Parameters ::= + * SEQUENCE { + * iv Gost28147-89-IV, + * encryptionParamSet OBJECT IDENTIFIER + * } + * + * Gost28147-89-IV ::= OCTET STRING (SIZE (8)) + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(iv); + v.add(paramSet); + + return new DERSequence(v); + } + + /** + * Return the OID representing the sBox to use. + * + * @return the sBox OID. + */ + public ASN1ObjectIdentifier getEncryptionParamSet() + { + return paramSet; + } + + /** + * Return the initialisation vector to use. + * + * @return the IV. + */ + public byte[] getIV() + { + return iv.getOctets(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410NamedParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410NamedParameters.java new file mode 100644 index 000000000..f6c6f3cac --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410NamedParameters.java @@ -0,0 +1,116 @@ +package org.spongycastle.asn1.cryptopro; + +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * table of the available named parameters for GOST 3410-94. + */ +public class GOST3410NamedParameters +{ + static final Hashtable objIds = new Hashtable(); + static final Hashtable params = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static private GOST3410ParamSetParameters cryptoProA = new GOST3410ParamSetParameters( + 1024, + new BigInteger("127021248288932417465907042777176443525787653508916535812817507265705031260985098497423188333483401180925999995120988934130659205614996724254121049274349357074920312769561451689224110579311248812610229678534638401693520013288995000362260684222750813532307004517341633685004541062586971416883686778842537820383"), + new BigInteger("68363196144955700784444165611827252895102170888761442055095051287550314083023"), + new BigInteger("100997906755055304772081815535925224869841082572053457874823515875577147990529272777244152852699298796483356699682842027972896052747173175480590485607134746852141928680912561502802222185647539190902656116367847270145019066794290930185446216399730872221732889830323194097355403213400972588322876850946740663962") +// validationAlgorithm { +// algorithm +// id-GostR3410-94-bBis, +// parameters +// GostR3410-94-ValidationBisParameters: { +// x0 1376285941, +// c 3996757427 +// } +// } + + ); + + static private GOST3410ParamSetParameters cryptoProB = new GOST3410ParamSetParameters( + 1024, + new BigInteger("139454871199115825601409655107690713107041707059928031797758001454375765357722984094124368522288239833039114681648076688236921220737322672160740747771700911134550432053804647694904686120113087816240740184800477047157336662926249423571248823968542221753660143391485680840520336859458494803187341288580489525163"), + new BigInteger("79885141663410976897627118935756323747307951916507639758300472692338873533959"), + new BigInteger("42941826148615804143873447737955502392672345968607143066798112994089471231420027060385216699563848719957657284814898909770759462613437669456364882730370838934791080835932647976778601915343474400961034231316672578686920482194932878633360203384797092684342247621055760235016132614780652761028509445403338652341") +// validationAlgorithm { +// algorithm +// id-GostR3410-94-bBis, +// parameters +// GostR3410-94-ValidationBisParameters: { +// x0 1536654555, +// c 1855361757, +// d 14408629386140014567655 +//4902939282056547857802241461782996702017713059974755104394739915140 +//6115284791024439062735788342744854120601660303926203867703556828005 +//8957203818114895398976594425537561271800850306 +// } +// } +//} + ); + + static private GOST3410ParamSetParameters cryptoProXchA = new GOST3410ParamSetParameters( + 1024, + new BigInteger("142011741597563481196368286022318089743276138395243738762872573441927459393512718973631166078467600360848946623567625795282774719212241929071046134208380636394084512691828894000571524625445295769349356752728956831541775441763139384457191755096847107846595662547942312293338483924514339614727760681880609734239"), + new BigInteger("91771529896554605945588149018382750217296858393520724172743325725474374979801"), + new BigInteger("133531813272720673433859519948319001217942375967847486899482359599369642528734712461590403327731821410328012529253871914788598993103310567744136196364803064721377826656898686468463277710150809401182608770201615324990468332931294920912776241137878030224355746606283971659376426832674269780880061631528163475887") + ); + + static + { + params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A, cryptoProA); + params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_B, cryptoProB); +// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_C, cryptoProC); +// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_D, cryptoProD); + params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchA, cryptoProXchA); +// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchB, cryptoProXchA); +// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchC, cryptoProXchA); + + objIds.put("GostR3410-94-CryptoPro-A", CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A); + objIds.put("GostR3410-94-CryptoPro-B", CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_B); + objIds.put("GostR3410-94-CryptoPro-XchA", CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchA); + } + + /** + * return the GOST3410ParamSetParameters object for the given OID, null if it + * isn't present. + * + * @param oid an object identifier representing a named parameters, if present. + */ + public static GOST3410ParamSetParameters getByOID( + ASN1ObjectIdentifier oid) + { + return (GOST3410ParamSetParameters)params.get(oid); + } + + /** + * returns an enumeration containing the name strings for parameters + * contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } + + public static GOST3410ParamSetParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(name); + + if (oid != null) + { + return (GOST3410ParamSetParameters)params.get(oid); + } + + return null; + } + + public static ASN1ObjectIdentifier getOID(String name) + { + return (ASN1ObjectIdentifier)objIds.get(name); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410ParamSetParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410ParamSetParameters.java new file mode 100644 index 000000000..783bb6873 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410ParamSetParameters.java @@ -0,0 +1,105 @@ +package org.spongycastle.asn1.cryptopro; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class GOST3410ParamSetParameters + extends ASN1Object +{ + int keySize; + ASN1Integer p, q, a; + + public static GOST3410ParamSetParameters getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static GOST3410ParamSetParameters getInstance( + Object obj) + { + if(obj == null || obj instanceof GOST3410ParamSetParameters) + { + return (GOST3410ParamSetParameters)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new GOST3410ParamSetParameters((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName()); + } + + public GOST3410ParamSetParameters( + int keySize, + BigInteger p, + BigInteger q, + BigInteger a) + { + this.keySize = keySize; + this.p = new ASN1Integer(p); + this.q = new ASN1Integer(q); + this.a = new ASN1Integer(a); + } + + public GOST3410ParamSetParameters( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + keySize = ((ASN1Integer)e.nextElement()).getValue().intValue(); + p = (ASN1Integer)e.nextElement(); + q = (ASN1Integer)e.nextElement(); + a = (ASN1Integer)e.nextElement(); + } + + /** + * @deprecated use getKeySize + */ + public int getLKeySize() + { + return keySize; + } + + public int getKeySize() + { + return keySize; + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getQ() + { + return q.getPositiveValue(); + } + + public BigInteger getA() + { + return a.getPositiveValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(keySize)); + v.add(p); + v.add(q); + v.add(a); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java new file mode 100644 index 000000000..d17ac1e2f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java @@ -0,0 +1,104 @@ +package org.spongycastle.asn1.cryptopro; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class GOST3410PublicKeyAlgParameters + extends ASN1Object +{ + private ASN1ObjectIdentifier publicKeyParamSet; + private ASN1ObjectIdentifier digestParamSet; + private ASN1ObjectIdentifier encryptionParamSet; + + public static GOST3410PublicKeyAlgParameters getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static GOST3410PublicKeyAlgParameters getInstance( + Object obj) + { + if (obj instanceof GOST3410PublicKeyAlgParameters) + { + return (GOST3410PublicKeyAlgParameters)obj; + } + + if(obj != null) + { + return new GOST3410PublicKeyAlgParameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public GOST3410PublicKeyAlgParameters( + ASN1ObjectIdentifier publicKeyParamSet, + ASN1ObjectIdentifier digestParamSet) + { + this.publicKeyParamSet = publicKeyParamSet; + this.digestParamSet = digestParamSet; + this.encryptionParamSet = null; + } + + public GOST3410PublicKeyAlgParameters( + ASN1ObjectIdentifier publicKeyParamSet, + ASN1ObjectIdentifier digestParamSet, + ASN1ObjectIdentifier encryptionParamSet) + { + this.publicKeyParamSet = publicKeyParamSet; + this.digestParamSet = digestParamSet; + this.encryptionParamSet = encryptionParamSet; + } + + /** + * @deprecated use getInstance() + */ + public GOST3410PublicKeyAlgParameters( + ASN1Sequence seq) + { + this.publicKeyParamSet = (ASN1ObjectIdentifier)seq.getObjectAt(0); + this.digestParamSet = (ASN1ObjectIdentifier)seq.getObjectAt(1); + + if (seq.size() > 2) + { + this.encryptionParamSet = (ASN1ObjectIdentifier)seq.getObjectAt(2); + } + } + + public ASN1ObjectIdentifier getPublicKeyParamSet() + { + return publicKeyParamSet; + } + + public ASN1ObjectIdentifier getDigestParamSet() + { + return digestParamSet; + } + + public ASN1ObjectIdentifier getEncryptionParamSet() + { + return encryptionParamSet; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(publicKeyParamSet); + v.add(digestParamSet); + + if (encryptionParamSet != null) + { + v.add(encryptionParamSet); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/CertEtcToken.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/CertEtcToken.java new file mode 100644 index 000000000..7774ade8c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/CertEtcToken.java @@ -0,0 +1,171 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cmp.PKIStatusInfo; +import org.spongycastle.asn1.cms.ContentInfo; +import org.spongycastle.asn1.ess.ESSCertID; +import org.spongycastle.asn1.ocsp.CertID; +import org.spongycastle.asn1.ocsp.CertStatus; +import org.spongycastle.asn1.ocsp.OCSPResponse; +import org.spongycastle.asn1.smime.SMIMECapabilities; +import org.spongycastle.asn1.x509.Certificate; +import org.spongycastle.asn1.x509.CertificateList; +import org.spongycastle.asn1.x509.Extension; + +/** + *
+ * CertEtcToken ::= CHOICE { + * certificate [0] IMPLICIT Certificate , + * esscertid [1] ESSCertId , + * pkistatus [2] IMPLICIT PKIStatusInfo , + * assertion [3] ContentInfo , + * crl [4] IMPLICIT CertificateList, + * ocspcertstatus [5] CertStatus, + * oscpcertid [6] IMPLICIT CertId , + * oscpresponse [7] IMPLICIT OCSPResponse, + * capabilities [8] SMIMECapabilities, + * extension Extension + * } + *+ */ +public class CertEtcToken + extends ASN1Object + implements ASN1Choice +{ + public static final int TAG_CERTIFICATE = 0; + public static final int TAG_ESSCERTID = 1; + public static final int TAG_PKISTATUS = 2; + public static final int TAG_ASSERTION = 3; + public static final int TAG_CRL = 4; + public static final int TAG_OCSPCERTSTATUS = 5; + public static final int TAG_OCSPCERTID = 6; + public static final int TAG_OCSPRESPONSE = 7; + public static final int TAG_CAPABILITIES = 8; + + private static final boolean[] explicit = new boolean[] + { + false, true, false, true, false, true, false, false, true + }; + + private int tagNo; + private ASN1Encodable value; + private Extension extension; + + public CertEtcToken(int tagNo, ASN1Encodable value) + { + this.tagNo = tagNo; + this.value = value; + } + + public CertEtcToken(Extension extension) + { + this.tagNo = -1; + this.extension = extension; + } + + private CertEtcToken(ASN1TaggedObject choice) + { + this.tagNo = choice.getTagNo(); + + switch (tagNo) + { + case TAG_CERTIFICATE: + value = Certificate.getInstance(choice, false); + break; + case TAG_ESSCERTID: + value = ESSCertID.getInstance(choice.getObject()); + break; + case TAG_PKISTATUS: + value = PKIStatusInfo.getInstance(choice, false); + break; + case TAG_ASSERTION: + value = ContentInfo.getInstance(choice.getObject()); + break; + case TAG_CRL: + value = CertificateList.getInstance(choice, false); + break; + case TAG_OCSPCERTSTATUS: + value = CertStatus.getInstance(choice.getObject()); + break; + case TAG_OCSPCERTID: + value = CertID.getInstance(choice, false); + break; + case TAG_OCSPRESPONSE: + value = OCSPResponse.getInstance(choice, false); + break; + case TAG_CAPABILITIES: + value = SMIMECapabilities.getInstance(choice.getObject()); + break; + default: + throw new IllegalArgumentException("Unknown tag: " + tagNo); + } + } + + public static CertEtcToken getInstance(Object obj) + { + if (obj instanceof CertEtcToken) + { + return (CertEtcToken)obj; + } + else if (obj instanceof ASN1TaggedObject) + { + return new CertEtcToken((ASN1TaggedObject)obj); + } + else if (obj != null) + { + return new CertEtcToken(Extension.getInstance(obj)); + } + + return null; + } + + public ASN1Primitive toASN1Primitive() + { + if (extension == null) + { + return new DERTaggedObject(explicit[tagNo], tagNo, value); + } + else + { + return extension.toASN1Primitive(); + } + } + + public int getTagNo() + { + return tagNo; + } + + public ASN1Encodable getValue() + { + return value; + } + + public Extension getExtension() + { + return extension; + } + + public String toString() + { + return "CertEtcToken {\n" + value + "}\n"; + } + + public static CertEtcToken[] arrayFromSequence(ASN1Sequence seq) + { + CertEtcToken[] tmp = new CertEtcToken[seq.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = CertEtcToken.getInstance(seq.getObjectAt(i)); + } + + return tmp; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSCertInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSCertInfo.java new file mode 100644 index 000000000..3c231b44f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSCertInfo.java @@ -0,0 +1,302 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cmp.PKIStatusInfo; +import org.spongycastle.asn1.x509.DigestInfo; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.PolicyInformation; + +/** + *
+ * DVCSCertInfo::= SEQUENCE { + * version Integer DEFAULT 1 , + * dvReqInfo DVCSRequestInformation, + * messageImprint DigestInfo, + * serialNumber Integer, + * responseTime DVCSTime, + * dvStatus [0] PKIStatusInfo OPTIONAL, + * policy [1] PolicyInformation OPTIONAL, + * reqSignature [2] SignerInfos OPTIONAL, + * certs [3] SEQUENCE SIZE (1..MAX) OF + * TargetEtcChain OPTIONAL, + * extensions Extensions OPTIONAL + * } + *+ */ + +public class DVCSCertInfo + extends ASN1Object +{ + + private int version = DEFAULT_VERSION; + private DVCSRequestInformation dvReqInfo; + private DigestInfo messageImprint; + private ASN1Integer serialNumber; + private DVCSTime responseTime; + private PKIStatusInfo dvStatus; + private PolicyInformation policy; + private ASN1Set reqSignature; + private ASN1Sequence certs; + private Extensions extensions; + + private static final int DEFAULT_VERSION = 1; + private static final int TAG_DV_STATUS = 0; + private static final int TAG_POLICY = 1; + private static final int TAG_REQ_SIGNATURE = 2; + private static final int TAG_CERTS = 3; + + public DVCSCertInfo( + DVCSRequestInformation dvReqInfo, + DigestInfo messageImprint, + ASN1Integer serialNumber, + DVCSTime responseTime) + { + this.dvReqInfo = dvReqInfo; + this.messageImprint = messageImprint; + this.serialNumber = serialNumber; + this.responseTime = responseTime; + } + + private DVCSCertInfo(ASN1Sequence seq) + { + int i = 0; + ASN1Encodable x = seq.getObjectAt(i++); + try + { + ASN1Integer encVersion = ASN1Integer.getInstance(x); + this.version = encVersion.getValue().intValue(); + x = seq.getObjectAt(i++); + } + catch (IllegalArgumentException e) + { + } + + this.dvReqInfo = DVCSRequestInformation.getInstance(x); + x = seq.getObjectAt(i++); + this.messageImprint = DigestInfo.getInstance(x); + x = seq.getObjectAt(i++); + this.serialNumber = ASN1Integer.getInstance(x); + x = seq.getObjectAt(i++); + this.responseTime = DVCSTime.getInstance(x); + + while (i < seq.size()) + { + + x = seq.getObjectAt(i++); + + try + { + ASN1TaggedObject t = ASN1TaggedObject.getInstance(x); + int tagNo = t.getTagNo(); + + switch (tagNo) + { + case TAG_DV_STATUS: + this.dvStatus = PKIStatusInfo.getInstance(t, false); + break; + case TAG_POLICY: + this.policy = PolicyInformation.getInstance(ASN1Sequence.getInstance(t, false)); + break; + case TAG_REQ_SIGNATURE: + this.reqSignature = ASN1Set.getInstance(t, false); + break; + case TAG_CERTS: + this.certs = ASN1Sequence.getInstance(t, false); + break; + } + + continue; + + } + catch (IllegalArgumentException e) + { + } + + try + { + this.extensions = Extensions.getInstance(x); + } + catch (IllegalArgumentException e) + { + } + + } + + } + + public static DVCSCertInfo getInstance(Object obj) + { + if (obj instanceof DVCSCertInfo) + { + return (DVCSCertInfo)obj; + } + else if (obj != null) + { + return new DVCSCertInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static DVCSCertInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public ASN1Primitive toASN1Primitive() + { + + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != DEFAULT_VERSION) + { + v.add(new ASN1Integer(version)); + } + v.add(dvReqInfo); + v.add(messageImprint); + v.add(serialNumber); + v.add(responseTime); + if (dvStatus != null) + { + v.add(new DERTaggedObject(false, TAG_DV_STATUS, dvStatus)); + } + if (policy != null) + { + v.add(new DERTaggedObject(false, TAG_POLICY, policy)); + } + if (reqSignature != null) + { + v.add(new DERTaggedObject(false, TAG_REQ_SIGNATURE, reqSignature)); + } + if (certs != null) + { + v.add(new DERTaggedObject(false, TAG_CERTS, certs)); + } + if (extensions != null) + { + v.add(extensions); + } + + return new DERSequence(v); + } + + public String toString() + { + StringBuffer s = new StringBuffer(); + + s.append("DVCSCertInfo {\n"); + + if (version != DEFAULT_VERSION) + { + s.append("version: " + version + "\n"); + } + s.append("dvReqInfo: " + dvReqInfo + "\n"); + s.append("messageImprint: " + messageImprint + "\n"); + s.append("serialNumber: " + serialNumber + "\n"); + s.append("responseTime: " + responseTime + "\n"); + if (dvStatus != null) + { + s.append("dvStatus: " + dvStatus + "\n"); + } + if (policy != null) + { + s.append("policy: " + policy + "\n"); + } + if (reqSignature != null) + { + s.append("reqSignature: " + reqSignature + "\n"); + } + if (certs != null) + { + s.append("certs: " + certs + "\n"); + } + if (extensions != null) + { + s.append("extensions: " + extensions + "\n"); + } + + s.append("}\n"); + return s.toString(); + } + + public int getVersion() + { + return version; + } + + private void setVersion(int version) + { + this.version = version; + } + + public DVCSRequestInformation getDvReqInfo() + { + return dvReqInfo; + } + + private void setDvReqInfo(DVCSRequestInformation dvReqInfo) + { + this.dvReqInfo = dvReqInfo; + } + + public DigestInfo getMessageImprint() + { + return messageImprint; + } + + private void setMessageImprint(DigestInfo messageImprint) + { + this.messageImprint = messageImprint; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + public DVCSTime getResponseTime() + { + return responseTime; + } + + public PKIStatusInfo getDvStatus() + { + return dvStatus; + } + + public PolicyInformation getPolicy() + { + return policy; + } + + public ASN1Set getReqSignature() + { + return reqSignature; + } + + public TargetEtcChain[] getCerts() + { + if (certs != null) + { + return TargetEtcChain.arrayFromSequence(certs); + } + + return null; + } + + public Extensions getExtensions() + { + return extensions; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSCertInfoBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSCertInfoBuilder.java new file mode 100644 index 000000000..47ba84e4e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSCertInfoBuilder.java @@ -0,0 +1,151 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cmp.PKIStatusInfo; +import org.spongycastle.asn1.x509.DigestInfo; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.PolicyInformation; + +/** + *
+ * DVCSCertInfo::= SEQUENCE { + * version Integer DEFAULT 1 , + * dvReqInfo DVCSRequestInformation, + * messageImprint DigestInfo, + * serialNumber Integer, + * responseTime DVCSTime, + * dvStatus [0] PKIStatusInfo OPTIONAL, + * policy [1] PolicyInformation OPTIONAL, + * reqSignature [2] SignerInfos OPTIONAL, + * certs [3] SEQUENCE SIZE (1..MAX) OF + * TargetEtcChain OPTIONAL, + * extensions Extensions OPTIONAL + * } + *+ */ + +public class DVCSCertInfoBuilder +{ + + private int version = DEFAULT_VERSION; + private DVCSRequestInformation dvReqInfo; + private DigestInfo messageImprint; + private ASN1Integer serialNumber; + private DVCSTime responseTime; + private PKIStatusInfo dvStatus; + private PolicyInformation policy; + private ASN1Set reqSignature; + private ASN1Sequence certs; + private Extensions extensions; + + private static final int DEFAULT_VERSION = 1; + private static final int TAG_DV_STATUS = 0; + private static final int TAG_POLICY = 1; + private static final int TAG_REQ_SIGNATURE = 2; + private static final int TAG_CERTS = 3; + + public DVCSCertInfoBuilder( + DVCSRequestInformation dvReqInfo, + DigestInfo messageImprint, + ASN1Integer serialNumber, + DVCSTime responseTime) + { + this.dvReqInfo = dvReqInfo; + this.messageImprint = messageImprint; + this.serialNumber = serialNumber; + this.responseTime = responseTime; + } + + public DVCSCertInfo build() + { + + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != DEFAULT_VERSION) + { + v.add(new ASN1Integer(version)); + } + v.add(dvReqInfo); + v.add(messageImprint); + v.add(serialNumber); + v.add(responseTime); + if (dvStatus != null) + { + v.add(new DERTaggedObject(false, TAG_DV_STATUS, dvStatus)); + } + if (policy != null) + { + v.add(new DERTaggedObject(false, TAG_POLICY, policy)); + } + if (reqSignature != null) + { + v.add(new DERTaggedObject(false, TAG_REQ_SIGNATURE, reqSignature)); + } + if (certs != null) + { + v.add(new DERTaggedObject(false, TAG_CERTS, certs)); + } + if (extensions != null) + { + v.add(extensions); + } + + return DVCSCertInfo.getInstance(new DERSequence(v)); + } + + public void setVersion(int version) + { + this.version = version; + } + + public void setDvReqInfo(DVCSRequestInformation dvReqInfo) + { + this.dvReqInfo = dvReqInfo; + } + + public void setMessageImprint(DigestInfo messageImprint) + { + this.messageImprint = messageImprint; + } + + public void setSerialNumber(ASN1Integer serialNumber) + { + this.serialNumber = serialNumber; + } + + public void setResponseTime(DVCSTime responseTime) + { + this.responseTime = responseTime; + } + + public void setDvStatus(PKIStatusInfo dvStatus) + { + this.dvStatus = dvStatus; + } + + public void setPolicy(PolicyInformation policy) + { + this.policy = policy; + } + + public void setReqSignature(ASN1Set reqSignature) + { + this.reqSignature = reqSignature; + } + + public void setCerts(TargetEtcChain[] certs) + { + this.certs = new DERSequence(certs); + } + + public void setExtensions(Extensions extensions) + { + this.extensions = extensions; + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSErrorNotice.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSErrorNotice.java new file mode 100644 index 000000000..2bd13ea92 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSErrorNotice.java @@ -0,0 +1,96 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.cmp.PKIStatusInfo; +import org.spongycastle.asn1.x509.GeneralName; + +/** + *
+ * DVCSErrorNotice ::= SEQUENCE { + * transactionStatus PKIStatusInfo , + * transactionIdentifier GeneralName OPTIONAL + * } + *+ */ +public class DVCSErrorNotice + extends ASN1Object +{ + private PKIStatusInfo transactionStatus; + private GeneralName transactionIdentifier; + + public DVCSErrorNotice(PKIStatusInfo status) + { + this(status, null); + } + + public DVCSErrorNotice(PKIStatusInfo status, GeneralName transactionIdentifier) + { + this.transactionStatus = status; + this.transactionIdentifier = transactionIdentifier; + } + + private DVCSErrorNotice(ASN1Sequence seq) + { + this.transactionStatus = PKIStatusInfo.getInstance(seq.getObjectAt(0)); + if (seq.size() > 1) + { + this.transactionIdentifier = GeneralName.getInstance(seq.getObjectAt(1)); + } + } + + public static DVCSErrorNotice getInstance(Object obj) + { + if (obj instanceof DVCSErrorNotice) + { + return (DVCSErrorNotice)obj; + } + else if (obj != null) + { + return new DVCSErrorNotice(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static DVCSErrorNotice getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(transactionStatus); + if (transactionIdentifier != null) + { + v.add(transactionIdentifier); + } + return new DERSequence(v); + } + + public String toString() + { + return "DVCSErrorNotice {\n" + + "transactionStatus: " + transactionStatus + "\n" + + (transactionIdentifier != null ? "transactionIdentifier: " + transactionIdentifier + "\n" : "") + + "}\n"; + } + + + public PKIStatusInfo getTransactionStatus() + { + return transactionStatus; + } + + public GeneralName getTransactionIdentifier() + { + return transactionIdentifier; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSObjectIdentifiers.java new file mode 100644 index 000000000..029150898 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSObjectIdentifiers.java @@ -0,0 +1,29 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * OIDs for RFC 3029 + * Data Validation and Certification Server Protocols + */ +public interface DVCSObjectIdentifiers +{ + /** Base OID id-pkix: 1.3.6.1.5.5.7 */ + static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7"); + /** Base OID id-smime: 1.2.840.113549.1.9.16 */ + static final ASN1ObjectIdentifier id_smime = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16"); + + /** Authority Information Access for DVCS; id-ad-dcvs; OID: 1.3.6.1.5.5.7.48.4 */ + static final ASN1ObjectIdentifier id_ad_dvcs = id_pkix.branch("48.4"); + + /** Key Purpose for DVCS; id-kp-dvcs; OID: 1.3.6.1.5.5.7.3.10 */ + static final ASN1ObjectIdentifier id_kp_dvcs = id_pkix.branch("3.10"); + + /** SMIME eContentType id-ct-DVCSRequestData; OID: 1.2.840.113549.1.9.16.1.7 */ + static final ASN1ObjectIdentifier id_ct_DVCSRequestData = id_smime.branch("1.7"); + /** SMIME eContentType id-ct-DVCSResponseData; OID: 1.2.840.113549.1.9.16.1.8 */ + static final ASN1ObjectIdentifier id_ct_DVCSResponseData = id_smime.branch("1.8"); + + /** SMIME DataValidation certificate attribute id-aa-dvcs-dvc; OID: 1.2.840.113549.1.9.16.2,29 */ + static final ASN1ObjectIdentifier id_aa_dvcs_dvc = id_smime.branch("2.29"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequest.java new file mode 100644 index 000000000..7be8221cd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequest.java @@ -0,0 +1,107 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.GeneralName; + +/** + *
+ * DVCSRequest ::= SEQUENCE { + * requestInformation DVCSRequestInformation, + * data Data, + * transactionIdentifier GeneralName OPTIONAL + * } + *+ */ + +public class DVCSRequest + extends ASN1Object +{ + + private DVCSRequestInformation requestInformation; + private Data data; + private GeneralName transactionIdentifier; + + public DVCSRequest(DVCSRequestInformation requestInformation, Data data) + { + this(requestInformation, data, null); + } + + public DVCSRequest(DVCSRequestInformation requestInformation, Data data, GeneralName transactionIdentifier) + { + this.requestInformation = requestInformation; + this.data = data; + this.transactionIdentifier = transactionIdentifier; + } + + private DVCSRequest(ASN1Sequence seq) + { + requestInformation = DVCSRequestInformation.getInstance(seq.getObjectAt(0)); + data = Data.getInstance(seq.getObjectAt(1)); + if (seq.size() > 2) + { + transactionIdentifier = GeneralName.getInstance(seq.getObjectAt(2)); + } + } + + public static DVCSRequest getInstance(Object obj) + { + if (obj instanceof DVCSRequest) + { + return (DVCSRequest)obj; + } + else if (obj != null) + { + return new DVCSRequest(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static DVCSRequest getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(requestInformation); + v.add(data); + if (transactionIdentifier != null) + { + v.add(transactionIdentifier); + } + return new DERSequence(v); + } + + public String toString() + { + return "DVCSRequest {\n" + + "requestInformation: " + requestInformation + "\n" + + "data: " + data + "\n" + + (transactionIdentifier != null ? "transactionIdentifier: " + transactionIdentifier + "\n" : "") + + "}\n"; + } + + public Data getData() + { + return data; + } + + public DVCSRequestInformation getRequestInformation() + { + return requestInformation; + } + + public GeneralName getTransactionIdentifier() + { + return transactionIdentifier; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequestInformation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequestInformation.java new file mode 100644 index 000000000..374df9dbb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequestInformation.java @@ -0,0 +1,271 @@ +package org.spongycastle.asn1.dvcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.PolicyInformation; + +/** + *
+ * DVCSRequestInformation ::= SEQUENCE { + * version INTEGER DEFAULT 1 , + * service ServiceType, + * nonce Nonce OPTIONAL, + * requestTime DVCSTime OPTIONAL, + * requester [0] GeneralNames OPTIONAL, + * requestPolicy [1] PolicyInformation OPTIONAL, + * dvcs [2] GeneralNames OPTIONAL, + * dataLocations [3] GeneralNames OPTIONAL, + * extensions [4] IMPLICIT Extensions OPTIONAL + * } + *+ */ + +public class DVCSRequestInformation + extends ASN1Object +{ + private int version = DEFAULT_VERSION; + private ServiceType service; + private BigInteger nonce; + private DVCSTime requestTime; + private GeneralNames requester; + private PolicyInformation requestPolicy; + private GeneralNames dvcs; + private GeneralNames dataLocations; + private Extensions extensions; + + private static final int DEFAULT_VERSION = 1; + private static final int TAG_REQUESTER = 0; + private static final int TAG_REQUEST_POLICY = 1; + private static final int TAG_DVCS = 2; + private static final int TAG_DATA_LOCATIONS = 3; + private static final int TAG_EXTENSIONS = 4; + + private DVCSRequestInformation(ASN1Sequence seq) + { + int i = 0; + + if (seq.getObjectAt(0) instanceof ASN1Integer) + { + ASN1Integer encVersion = ASN1Integer.getInstance(seq.getObjectAt(i++)); + this.version = encVersion.getValue().intValue(); + } + else + { + this.version = 1; + } + + this.service = ServiceType.getInstance(seq.getObjectAt(i++)); + + while (i < seq.size()) + { + ASN1Encodable x = seq.getObjectAt(i); + + if (x instanceof ASN1Integer) + { + this.nonce = ASN1Integer.getInstance(x).getValue(); + } + else if (x instanceof ASN1GeneralizedTime) + { + this.requestTime = DVCSTime.getInstance(x); + } + else if (x instanceof ASN1TaggedObject) + { + ASN1TaggedObject t = ASN1TaggedObject.getInstance(x); + int tagNo = t.getTagNo(); + + switch (tagNo) + { + case TAG_REQUESTER: + this.requester = GeneralNames.getInstance(t, false); + break; + case TAG_REQUEST_POLICY: + this.requestPolicy = PolicyInformation.getInstance(ASN1Sequence.getInstance(t, false)); + break; + case TAG_DVCS: + this.dvcs = GeneralNames.getInstance(t, false); + break; + case TAG_DATA_LOCATIONS: + this.dataLocations = GeneralNames.getInstance(t, false); + break; + case TAG_EXTENSIONS: + this.extensions = Extensions.getInstance(t, false); + break; + } + } + else + { + this.requestTime = DVCSTime.getInstance(x); + } + + i++; + } + } + + public static DVCSRequestInformation getInstance(Object obj) + { + if (obj instanceof DVCSRequestInformation) + { + return (DVCSRequestInformation)obj; + } + else if (obj != null) + { + return new DVCSRequestInformation(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static DVCSRequestInformation getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != DEFAULT_VERSION) + { + v.add(new ASN1Integer(version)); + } + v.add(service); + if (nonce != null) + { + v.add(new ASN1Integer(nonce)); + } + if (requestTime != null) + { + v.add(requestTime); + } + + int[] tags = new int[]{ + TAG_REQUESTER, + TAG_REQUEST_POLICY, + TAG_DVCS, + TAG_DATA_LOCATIONS, + TAG_EXTENSIONS + }; + ASN1Encodable[] taggedObjects = new ASN1Encodable[]{ + requester, + requestPolicy, + dvcs, + dataLocations, + extensions + }; + for (int i = 0; i < tags.length; i++) + { + int tag = tags[i]; + ASN1Encodable taggedObject = taggedObjects[i]; + if (taggedObject != null) + { + v.add(new DERTaggedObject(false, tag, taggedObject)); + } + } + + return new DERSequence(v); + } + + public String toString() + { + + StringBuffer s = new StringBuffer(); + + s.append("DVCSRequestInformation {\n"); + + if (version != DEFAULT_VERSION) + { + s.append("version: " + version + "\n"); + } + s.append("service: " + service + "\n"); + if (nonce != null) + { + s.append("nonce: " + nonce + "\n"); + } + if (requestTime != null) + { + s.append("requestTime: " + requestTime + "\n"); + } + if (requester != null) + { + s.append("requester: " + requester + "\n"); + } + if (requestPolicy != null) + { + s.append("requestPolicy: " + requestPolicy + "\n"); + } + if (dvcs != null) + { + s.append("dvcs: " + dvcs + "\n"); + } + if (dataLocations != null) + { + s.append("dataLocations: " + dataLocations + "\n"); + } + if (extensions != null) + { + s.append("extensions: " + extensions + "\n"); + } + + s.append("}\n"); + return s.toString(); + } + + public int getVersion() + { + return version; + } + + public ServiceType getService() + { + return service; + } + + public BigInteger getNonce() + { + return nonce; + } + + public DVCSTime getRequestTime() + { + return requestTime; + } + + public GeneralNames getRequester() + { + return requester; + } + + public PolicyInformation getRequestPolicy() + { + return requestPolicy; + } + + public GeneralNames getDVCS() + { + return dvcs; + } + + public GeneralNames getDataLocations() + { + return dataLocations; + } + + public Extensions getExtensions() + { + return extensions; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequestInformationBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequestInformationBuilder.java new file mode 100644 index 000000000..8815fe37d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSRequestInformationBuilder.java @@ -0,0 +1,224 @@ +package org.spongycastle.asn1.dvcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.PolicyInformation; +import org.spongycastle.util.BigIntegers; + +/** + *
+ * DVCSRequestInformation ::= SEQUENCE { + * version INTEGER DEFAULT 1 , + * service ServiceType, + * nonce Nonce OPTIONAL, + * requestTime DVCSTime OPTIONAL, + * requester [0] GeneralNames OPTIONAL, + * requestPolicy [1] PolicyInformation OPTIONAL, + * dvcs [2] GeneralNames OPTIONAL, + * dataLocations [3] GeneralNames OPTIONAL, + * extensions [4] IMPLICIT Extensions OPTIONAL + * } + *+ */ +public class DVCSRequestInformationBuilder +{ + private int version = DEFAULT_VERSION; + + private final ServiceType service; + private DVCSRequestInformation initialInfo; + + private BigInteger nonce; + private DVCSTime requestTime; + private GeneralNames requester; + private PolicyInformation requestPolicy; + private GeneralNames dvcs; + private GeneralNames dataLocations; + private Extensions extensions; + + private static final int DEFAULT_VERSION = 1; + private static final int TAG_REQUESTER = 0; + private static final int TAG_REQUEST_POLICY = 1; + private static final int TAG_DVCS = 2; + private static final int TAG_DATA_LOCATIONS = 3; + private static final int TAG_EXTENSIONS = 4; + + public DVCSRequestInformationBuilder(ServiceType service) + { + this.service = service; + } + + public DVCSRequestInformationBuilder(DVCSRequestInformation initialInfo) + { + this.initialInfo = initialInfo; + this.service = initialInfo.getService(); + this.version = initialInfo.getVersion(); + this.nonce = initialInfo.getNonce(); + this.requestTime = initialInfo.getRequestTime(); + this.requestPolicy = initialInfo.getRequestPolicy(); + this.dvcs = initialInfo.getDVCS(); + this.dataLocations = initialInfo.getDataLocations(); + } + + public DVCSRequestInformation build() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != DEFAULT_VERSION) + { + v.add(new ASN1Integer(version)); + } + v.add(service); + if (nonce != null) + { + v.add(new ASN1Integer(nonce)); + } + if (requestTime != null) + { + v.add(requestTime); + } + + int[] tags = new int[]{ + TAG_REQUESTER, + TAG_REQUEST_POLICY, + TAG_DVCS, + TAG_DATA_LOCATIONS, + TAG_EXTENSIONS + }; + ASN1Encodable[] taggedObjects = new ASN1Encodable[]{ + requester, + requestPolicy, + dvcs, + dataLocations, + extensions + }; + for (int i = 0; i < tags.length; i++) + { + int tag = tags[i]; + ASN1Encodable taggedObject = taggedObjects[i]; + if (taggedObject != null) + { + v.add(new DERTaggedObject(false, tag, taggedObject)); + } + } + + return DVCSRequestInformation.getInstance(new DERSequence(v)); + } + + public void setVersion(int version) + { + if (initialInfo != null) + { + throw new IllegalStateException("cannot change version in existing DVCSRequestInformation"); + } + + this.version = version; + } + + public void setNonce(BigInteger nonce) + { + // RFC 3029, 9.1: The DVCS MAY modify the fields + // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure + + // RFC 3029, 9.1: The only modification + // allowed to a 'nonce' is the inclusion of a new field if it was not + // present, or to concatenate other data to the end (right) of an + // existing value. + if (initialInfo != null) + { + if (initialInfo.getNonce() == null) + { + this.nonce = nonce; + } + else + { + byte[] initialBytes = initialInfo.getNonce().toByteArray(); + byte[] newBytes = BigIntegers.asUnsignedByteArray(nonce); + byte[] nonceBytes = new byte[initialBytes.length + newBytes.length]; + + System.arraycopy(initialBytes, 0, nonceBytes, 0, initialBytes.length); + System.arraycopy(newBytes, 0, nonceBytes, initialBytes.length, newBytes.length); + + this.nonce = new BigInteger(nonceBytes); + } + } + + this.nonce = nonce; + } + + public void setRequestTime(DVCSTime requestTime) + { + if (initialInfo != null) + { + throw new IllegalStateException("cannot change request time in existing DVCSRequestInformation"); + } + + this.requestTime = requestTime; + } + + public void setRequester(GeneralName requester) + { + this.setRequester(new GeneralNames(requester)); + } + + public void setRequester(GeneralNames requester) + { + // RFC 3029, 9.1: The DVCS MAY modify the fields + // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure + + this.requester = requester; + } + + public void setRequestPolicy(PolicyInformation requestPolicy) + { + if (initialInfo != null) + { + throw new IllegalStateException("cannot change request policy in existing DVCSRequestInformation"); + } + + this.requestPolicy = requestPolicy; + } + + public void setDVCS(GeneralName dvcs) + { + this.setDVCS(new GeneralNames(dvcs)); + } + + public void setDVCS(GeneralNames dvcs) + { + // RFC 3029, 9.1: The DVCS MAY modify the fields + // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure + + this.dvcs = dvcs; + } + + public void setDataLocations(GeneralName dataLocation) + { + this.setDataLocations(new GeneralNames(dataLocation)); + } + + public void setDataLocations(GeneralNames dataLocations) + { + // RFC 3029, 9.1: The DVCS MAY modify the fields + // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure + + this.dataLocations = dataLocations; + } + + public void setExtensions(Extensions extensions) + { + if (initialInfo != null) + { + throw new IllegalStateException("cannot change extensions in existing DVCSRequestInformation"); + } + + this.extensions = extensions; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSResponse.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSResponse.java new file mode 100644 index 000000000..6100bd307 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSResponse.java @@ -0,0 +1,117 @@ +package org.spongycastle.asn1.dvcs; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +/** + *
+ * DVCSResponse ::= CHOICE + * { + * dvCertInfo DVCSCertInfo , + * dvErrorNote [0] DVCSErrorNotice + * } + *+ */ + +public class DVCSResponse + extends ASN1Object + implements ASN1Choice +{ + private DVCSCertInfo dvCertInfo; + private DVCSErrorNotice dvErrorNote; + + public DVCSResponse(DVCSCertInfo dvCertInfo) + { + this.dvCertInfo = dvCertInfo; + } + + public DVCSResponse(DVCSErrorNotice dvErrorNote) + { + this.dvErrorNote = dvErrorNote; + } + + public static DVCSResponse getInstance(Object obj) + { + if (obj == null || obj instanceof DVCSResponse) + { + return (DVCSResponse)obj; + } + else + { + if (obj instanceof byte[]) + { + try + { + return getInstance(ASN1Primitive.fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage()); + } + } + if (obj instanceof ASN1Sequence) + { + DVCSCertInfo dvCertInfo = DVCSCertInfo.getInstance(obj); + + return new DVCSResponse(dvCertInfo); + } + if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject t = ASN1TaggedObject.getInstance(obj); + DVCSErrorNotice dvErrorNote = DVCSErrorNotice.getInstance(t, false); + + return new DVCSResponse(dvErrorNote); + } + } + + throw new IllegalArgumentException("Couldn't convert from object to DVCSResponse: " + obj.getClass().getName()); + } + + public static DVCSResponse getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public DVCSCertInfo getCertInfo() + { + return dvCertInfo; + } + + public DVCSErrorNotice getErrorNotice() + { + return dvErrorNote; + } + + public ASN1Primitive toASN1Primitive() + { + if (dvCertInfo != null) + { + return dvCertInfo.toASN1Primitive(); + } + else + { + return new DERTaggedObject(0, dvErrorNote); + } + } + + public String toString() + { + if (dvCertInfo != null) + { + return "DVCSResponse {\ndvCertInfo: " + dvCertInfo.toString() + "}\n"; + } + if (dvErrorNote != null) + { + return "DVCSResponse {\ndvErrorNote: " + dvErrorNote.toString() + "}\n"; + } + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSTime.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSTime.java new file mode 100644 index 000000000..f8bf2f90d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/DVCSTime.java @@ -0,0 +1,111 @@ +package org.spongycastle.asn1.dvcs; + +import java.util.Date; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.cms.ContentInfo; + +/** + *
+ * DVCSTime ::= CHOICE { + * genTime GeneralizedTime, + * timeStampToken ContentInfo + * } + *+ */ +public class DVCSTime + extends ASN1Object + implements ASN1Choice +{ + private ASN1GeneralizedTime genTime; + private ContentInfo timeStampToken; + private Date time; + + // constructors: + + public DVCSTime(Date time) + { + this(new ASN1GeneralizedTime(time)); + } + + public DVCSTime(ASN1GeneralizedTime genTime) + { + this.genTime = genTime; + } + + public DVCSTime(ContentInfo timeStampToken) + { + this.timeStampToken = timeStampToken; + } + + public static DVCSTime getInstance(Object obj) + { + if (obj instanceof DVCSTime) + { + return (DVCSTime)obj; + } + else if (obj instanceof ASN1GeneralizedTime) + { + return new DVCSTime(ASN1GeneralizedTime.getInstance(obj)); + } + else if (obj != null) + { + return new DVCSTime(ContentInfo.getInstance(obj)); + } + + return null; + } + + public static DVCSTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + + // selectors: + + public ASN1GeneralizedTime getGenTime() + { + return genTime; + } + + public ContentInfo getTimeStampToken() + { + return timeStampToken; + } + + public ASN1Primitive toASN1Primitive() + { + + if (genTime != null) + { + return genTime; + } + + if (timeStampToken != null) + { + return timeStampToken.toASN1Primitive(); + } + + return null; + } + + public String toString() + { + if (genTime != null) + { + return genTime.toString(); + } + if (timeStampToken != null) + { + return timeStampToken.toString(); + } + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/Data.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/Data.java new file mode 100644 index 000000000..8d00f33fa --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/Data.java @@ -0,0 +1,149 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.DigestInfo; + +/** + *
+ * Data ::= CHOICE { + * message OCTET STRING , + * messageImprint DigestInfo, + * certs [0] SEQUENCE SIZE (1..MAX) OF + * TargetEtcChain + * } + *+ */ + +public class Data + extends ASN1Object + implements ASN1Choice +{ + private ASN1OctetString message; + private DigestInfo messageImprint; + private ASN1Sequence certs; + + public Data(byte[] messageBytes) + { + this.message = new DEROctetString(messageBytes); + } + + public Data(ASN1OctetString message) + { + this.message = message; + } + + public Data(DigestInfo messageImprint) + { + this.messageImprint = messageImprint; + } + + public Data(TargetEtcChain cert) + { + this.certs = new DERSequence(cert); + } + + public Data(TargetEtcChain[] certs) + { + this.certs = new DERSequence(certs); + } + + private Data(ASN1Sequence certs) + { + this.certs = certs; + } + + public static Data getInstance(Object obj) + { + if (obj instanceof Data) + { + return (Data)obj; + } + else if (obj instanceof ASN1OctetString) + { + return new Data((ASN1OctetString)obj); + } + else if (obj instanceof ASN1Sequence) + { + return new Data(DigestInfo.getInstance(obj)); + } + else if (obj instanceof ASN1TaggedObject) + { + return new Data(ASN1Sequence.getInstance((ASN1TaggedObject)obj, false)); + } + throw new IllegalArgumentException("Unknown object submitted to getInstance: " + obj.getClass().getName()); + } + + public static Data getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public ASN1Primitive toASN1Primitive() + { + if (message != null) + { + return message.toASN1Primitive(); + } + if (messageImprint != null) + { + return messageImprint.toASN1Primitive(); + } + else + { + return new DERTaggedObject(false, 0, certs); + } + } + + public String toString() + { + if (message != null) + { + return "Data {\n" + message + "}\n"; + } + if (messageImprint != null) + { + return "Data {\n" + messageImprint + "}\n"; + } + else + { + return "Data {\n" + certs + "}\n"; + } + } + + public ASN1OctetString getMessage() + { + return message; + } + + public DigestInfo getMessageImprint() + { + return messageImprint; + } + + public TargetEtcChain[] getCerts() + { + if (certs == null) + { + return null; + } + + TargetEtcChain[] tmp = new TargetEtcChain[certs.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = TargetEtcChain.getInstance(certs.getObjectAt(i)); + } + + return tmp; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/PathProcInput.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/PathProcInput.java new file mode 100644 index 000000000..2c5b8875c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/PathProcInput.java @@ -0,0 +1,180 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.PolicyInformation; + +/** + *
+ * PathProcInput ::= SEQUENCE { + * acceptablePolicySet SEQUENCE SIZE (1..MAX) OF + * PolicyInformation, + * inhibitPolicyMapping BOOLEAN DEFAULT FALSE, + * explicitPolicyReqd [0] BOOLEAN DEFAULT FALSE , + * inhibitAnyPolicy [1] BOOLEAN DEFAULT FALSE + * } + *+ */ +public class PathProcInput + extends ASN1Object +{ + + private PolicyInformation[] acceptablePolicySet; + private boolean inhibitPolicyMapping = false; + private boolean explicitPolicyReqd = false; + private boolean inhibitAnyPolicy = false; + + public PathProcInput(PolicyInformation[] acceptablePolicySet) + { + this.acceptablePolicySet = acceptablePolicySet; + } + + public PathProcInput(PolicyInformation[] acceptablePolicySet, boolean inhibitPolicyMapping, boolean explicitPolicyReqd, boolean inhibitAnyPolicy) + { + this.acceptablePolicySet = acceptablePolicySet; + this.inhibitPolicyMapping = inhibitPolicyMapping; + this.explicitPolicyReqd = explicitPolicyReqd; + this.inhibitAnyPolicy = inhibitAnyPolicy; + } + + private static PolicyInformation[] fromSequence(ASN1Sequence seq) + { + PolicyInformation[] tmp = new PolicyInformation[seq.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = PolicyInformation.getInstance(seq.getObjectAt(i)); + } + + return tmp; + } + + public static PathProcInput getInstance(Object obj) + { + if (obj instanceof PathProcInput) + { + return (PathProcInput)obj; + } + else if (obj != null) + { + ASN1Sequence seq = ASN1Sequence.getInstance(obj); + ASN1Sequence policies = ASN1Sequence.getInstance(seq.getObjectAt(0)); + PathProcInput result = new PathProcInput(fromSequence(policies)); + + for (int i = 1; i < seq.size(); i++) + { + Object o = seq.getObjectAt(i); + + if (o instanceof ASN1Boolean) + { + ASN1Boolean x = ASN1Boolean.getInstance(o); + result.setInhibitPolicyMapping(x.isTrue()); + } + else if (o instanceof ASN1TaggedObject) + { + ASN1TaggedObject t = ASN1TaggedObject.getInstance(o); + ASN1Boolean x; + switch (t.getTagNo()) + { + case 0: + x = ASN1Boolean.getInstance(t, false); + result.setExplicitPolicyReqd(x.isTrue()); + break; + case 1: + x = ASN1Boolean.getInstance(t, false); + result.setInhibitAnyPolicy(x.isTrue()); + } + } + } + return result; + } + + return null; + } + + public static PathProcInput getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + ASN1EncodableVector pV = new ASN1EncodableVector(); + + for (int i = 0; i != acceptablePolicySet.length; i++) + { + pV.add(acceptablePolicySet[i]); + } + + v.add(new DERSequence(pV)); + + if (inhibitPolicyMapping) + { + v.add(new ASN1Boolean(inhibitPolicyMapping)); + } + if (explicitPolicyReqd) + { + v.add(new DERTaggedObject(false, 0, new ASN1Boolean(explicitPolicyReqd))); + } + if (inhibitAnyPolicy) + { + v.add(new DERTaggedObject(false, 1, new ASN1Boolean(inhibitAnyPolicy))); + } + + return new DERSequence(v); + } + + public String toString() + { + return "PathProcInput: {\n" + + "acceptablePolicySet: " + acceptablePolicySet + "\n" + + "inhibitPolicyMapping: " + inhibitPolicyMapping + "\n" + + "explicitPolicyReqd: " + explicitPolicyReqd + "\n" + + "inhibitAnyPolicy: " + inhibitAnyPolicy + "\n" + + "}\n"; + } + + public PolicyInformation[] getAcceptablePolicySet() + { + return acceptablePolicySet; + } + + public boolean isInhibitPolicyMapping() + { + return inhibitPolicyMapping; + } + + private void setInhibitPolicyMapping(boolean inhibitPolicyMapping) + { + this.inhibitPolicyMapping = inhibitPolicyMapping; + } + + public boolean isExplicitPolicyReqd() + { + return explicitPolicyReqd; + } + + private void setExplicitPolicyReqd(boolean explicitPolicyReqd) + { + this.explicitPolicyReqd = explicitPolicyReqd; + } + + public boolean isInhibitAnyPolicy() + { + return inhibitAnyPolicy; + } + + private void setInhibitAnyPolicy(boolean inhibitAnyPolicy) + { + this.inhibitAnyPolicy = inhibitAnyPolicy; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/ServiceType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/ServiceType.java new file mode 100644 index 000000000..0cc4acc1f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/ServiceType.java @@ -0,0 +1,92 @@ +package org.spongycastle.asn1.dvcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Enumerated; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; + + +/** + * ServiceType ::= ENUMERATED { cpd(1), vsd(2), cpkc(3), ccpd(4) } + */ + +public class ServiceType + extends ASN1Object +{ + /** + * Identifier of CPD service (Certify Possession of Data). + */ + public static final ServiceType CPD = new ServiceType(1); + + /** + * Identifier of VSD service (Verify Signed Document). + */ + public static final ServiceType VSD = new ServiceType(2); + + /** + * Identifier of VPKC service (Verify Public Key Certificates (also referred to as CPKC)). + */ + public static final ServiceType VPKC = new ServiceType(3); + + /** + * Identifier of CCPD service (Certify Claim of Possession of Data). + */ + public static final ServiceType CCPD = new ServiceType(4); + + private ASN1Enumerated value; + + public ServiceType(int value) + { + this.value = new ASN1Enumerated(value); + } + + private ServiceType(ASN1Enumerated value) + { + this.value = value; + } + + public static ServiceType getInstance(Object obj) + { + if (obj instanceof ServiceType) + { + return (ServiceType)obj; + } + else if (obj != null) + { + return new ServiceType(ASN1Enumerated.getInstance(obj)); + } + + return null; + } + + public static ServiceType getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Enumerated.getInstance(obj, explicit)); + } + + public BigInteger getValue() + { + return value.getValue(); + } + + public ASN1Primitive toASN1Primitive() + { + return value; + } + + public String toString() + { + int num = value.getValue().intValue(); + return "" + num + ( + num == CPD.getValue().intValue() ? "(CPD)" : + num == VSD.getValue().intValue() ? "(VSD)" : + num == VPKC.getValue().intValue() ? "(VPKC)" : + num == CCPD.getValue().intValue() ? "(CCPD)" : + "?"); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/TargetEtcChain.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/TargetEtcChain.java new file mode 100644 index 000000000..264a6c262 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/dvcs/TargetEtcChain.java @@ -0,0 +1,191 @@ +package org.spongycastle.asn1.dvcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + *
+ * TargetEtcChain ::= SEQUENCE { + * target CertEtcToken, + * chain SEQUENCE SIZE (1..MAX) OF + * CertEtcToken OPTIONAL, + * pathProcInput [0] PathProcInput OPTIONAL + * } + *+ */ + +public class TargetEtcChain + extends ASN1Object +{ + private CertEtcToken target; + private ASN1Sequence chain; + private PathProcInput pathProcInput; + + public TargetEtcChain(CertEtcToken target) + { + this(target, null, null); + } + + public TargetEtcChain(CertEtcToken target, CertEtcToken[] chain) + { + this(target, chain, null); + } + + public TargetEtcChain(CertEtcToken target, PathProcInput pathProcInput) + { + this(target, null, pathProcInput); + } + + public TargetEtcChain(CertEtcToken target, CertEtcToken[] chain, PathProcInput pathProcInput) + { + this.target = target; + + if (chain != null) + { + this.chain = new DERSequence(chain); + } + + this.pathProcInput = pathProcInput; + } + + private TargetEtcChain(ASN1Sequence seq) + { + int i = 0; + ASN1Encodable obj = seq.getObjectAt(i++); + this.target = CertEtcToken.getInstance(obj); + + try + { + obj = seq.getObjectAt(i++); + this.chain = ASN1Sequence.getInstance(obj); + } + catch (IllegalArgumentException e) + { + } + catch (IndexOutOfBoundsException e) + { + return; + } + + try + { + obj = seq.getObjectAt(i++); + ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj); + switch (tagged.getTagNo()) + { + case 0: + this.pathProcInput = PathProcInput.getInstance(tagged, false); + break; + } + } + catch (IllegalArgumentException e) + { + } + catch (IndexOutOfBoundsException e) + { + } + } + + public static TargetEtcChain getInstance(Object obj) + { + if (obj instanceof TargetEtcChain) + { + return (TargetEtcChain)obj; + } + else if (obj != null) + { + return new TargetEtcChain(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static TargetEtcChain getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(target); + if (chain != null) + { + v.add(chain); + } + if (pathProcInput != null) + { + v.add(new DERTaggedObject(false, 0, pathProcInput)); + } + + return new DERSequence(v); + } + + public String toString() + { + StringBuffer s = new StringBuffer(); + s.append("TargetEtcChain {\n"); + s.append("target: " + target + "\n"); + if (chain != null) + { + s.append("chain: " + chain + "\n"); + } + if (pathProcInput != null) + { + s.append("pathProcInput: " + pathProcInput + "\n"); + } + s.append("}\n"); + return s.toString(); + } + + + public CertEtcToken getTarget() + { + return target; + } + + public CertEtcToken[] getChain() + { + if (chain != null) + { + return CertEtcToken.arrayFromSequence(chain); + } + + return null; + } + + private void setChain(ASN1Sequence chain) + { + this.chain = chain; + } + + public PathProcInput getPathProcInput() + { + return pathProcInput; + } + + private void setPathProcInput(PathProcInput pathProcInput) + { + this.pathProcInput = pathProcInput; + } + + public static TargetEtcChain[] arrayFromSequence(ASN1Sequence seq) + { + TargetEtcChain[] tmp = new TargetEtcChain[seq.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = TargetEtcChain.getInstance(seq.getObjectAt(i)); + } + + return tmp; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/BidirectionalMap.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/BidirectionalMap.java new file mode 100644 index 000000000..479677c09 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/BidirectionalMap.java @@ -0,0 +1,23 @@ +package org.spongycastle.asn1.eac; + +import java.util.Hashtable; + +public class BidirectionalMap + extends Hashtable +{ + private static final long serialVersionUID = -7457289971962812909L; + + Hashtable reverseMap = new Hashtable(); + + public Object getReverse(Object o) + { + return reverseMap.get(o); + } + + public Object put(Object key, Object o) + { + reverseMap.put(o, key); + return super.put(key, o); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CVCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CVCertificate.java new file mode 100644 index 000000000..1c6f5b80b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CVCertificate.java @@ -0,0 +1,317 @@ +package org.spongycastle.asn1.eac; + + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1ParsingException; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERApplicationSpecific; +import org.spongycastle.asn1.DEROctetString; + + +/** + * an iso7816Certificate structure. + * + *
+ * Certificate ::= SEQUENCE { + * CertificateBody Iso7816CertificateBody, + * signature DER Application specific + * } + *+ */ +public class CVCertificate + extends ASN1Object +{ + private CertificateBody certificateBody; + private byte[] signature; + private int valid; + private static int bodyValid = 0x01; + private static int signValid = 0x02; + public static final byte version_1 = 0x0; + + public static String ReferenceEncoding = "ISO-8859-1"; + + /** + * Sets the values of the certificate (body and signature). + * + * @param appSpe is a DERApplicationSpecific object containing body and signature. + * @throws IOException if tags or value are incorrect. + */ + private void setPrivateData(DERApplicationSpecific appSpe) + throws IOException + { + valid = 0; + if (appSpe.getApplicationTag() == EACTags.CARDHOLDER_CERTIFICATE) + { + ASN1InputStream content = new ASN1InputStream(appSpe.getContents()); + ASN1Primitive tmpObj; + while ((tmpObj = content.readObject()) != null) + { + DERApplicationSpecific aSpe; + if (tmpObj instanceof DERApplicationSpecific) + { + aSpe = (DERApplicationSpecific)tmpObj; + switch (aSpe.getApplicationTag()) + { + case EACTags.CERTIFICATE_CONTENT_TEMPLATE: + certificateBody = CertificateBody.getInstance(aSpe); + valid |= bodyValid; + break; + case EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP: + signature = aSpe.getContents(); + valid |= signValid; + break; + default: + throw new IOException("Invalid tag, not an Iso7816CertificateStructure :" + aSpe.getApplicationTag()); + } + } + else + { + throw new IOException("Invalid Object, not an Iso7816CertificateStructure"); + } + } + } + else + { + throw new IOException("not a CARDHOLDER_CERTIFICATE :" + appSpe.getApplicationTag()); + } + } + + /** + * Create an iso7816Certificate structure from an ASN1InputStream. + * + * @param aIS the byte stream to parse. + * @return the Iso7816CertificateStructure represented by the byte stream. + * @throws IOException if there is a problem parsing the data. + */ + public CVCertificate(ASN1InputStream aIS) + throws IOException + { + initFrom(aIS); + } + + private void initFrom(ASN1InputStream aIS) + throws IOException + { + ASN1Primitive obj; + while ((obj = aIS.readObject()) != null) + { + if (obj instanceof DERApplicationSpecific) + { + setPrivateData((DERApplicationSpecific)obj); + } + else + { + throw new IOException("Invalid Input Stream for creating an Iso7816CertificateStructure"); + } + } + } + + /** + * Create an iso7816Certificate structure from a DERApplicationSpecific. + * + * @param appSpe the DERApplicationSpecific object. + * @return the Iso7816CertificateStructure represented by the DERApplicationSpecific object. + * @throws IOException if there is a problem parsing the data. + */ + private CVCertificate(DERApplicationSpecific appSpe) + throws IOException + { + setPrivateData(appSpe); + } + + /** + * Create an iso7816Certificate structure from a body and its signature. + * + * @param body the Iso7816CertificateBody object containing the body. + * @param signature the byte array containing the signature + * @return the Iso7816CertificateStructure + * @throws IOException if there is a problem parsing the data. + */ + public CVCertificate(CertificateBody body, byte[] signature) + throws IOException + { + certificateBody = body; + this.signature = signature; + // patch remi + valid |= bodyValid; + valid |= signValid; + } + + /** + * Create an iso7816Certificate structure from an object. + * + * @param obj the Object to extract the certificate from. + * @return the Iso7816CertificateStructure represented by the byte stream. + * @throws IOException if there is a problem parsing the data. + */ + public static CVCertificate getInstance(Object obj) + { + if (obj instanceof CVCertificate) + { + return (CVCertificate)obj; + } + else if (obj != null) + { + try + { + return new CVCertificate(DERApplicationSpecific.getInstance(obj)); + } + catch (IOException e) + { + throw new ASN1ParsingException("unable to parse data: " + e.getMessage(), e); + } + } + + return null; + } + + /** + * Gives the signature of the whole body. Type of signature is given in + * the Iso7816CertificateBody.Iso7816PublicKey.ASN1ObjectIdentifier + * + * @return the signature of the body. + */ + public byte[] getSignature() + { + return signature; + } + + /** + * Gives the body of the certificate. + * + * @return the body. + */ + public CertificateBody getBody() + { + return certificateBody; + } + + /** + * @see org.spongycastle.asn1.ASN1Object#toASN1Primitive() + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (valid != (signValid | bodyValid)) + { + return null; + } + v.add(certificateBody); + + try + { + v.add(new DERApplicationSpecific(false, EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP, new DEROctetString(signature))); + } + catch (IOException e) + { + throw new IllegalStateException("unable to convert signature!"); + } + + return new DERApplicationSpecific(EACTags.CARDHOLDER_CERTIFICATE, v); + } + + /** + * @return the Holder authorization and role (CVCA, DV, IS). + */ + public ASN1ObjectIdentifier getHolderAuthorization() + throws IOException + { + CertificateHolderAuthorization cha = certificateBody.getCertificateHolderAuthorization(); + return cha.getOid(); + } + + /** + * @return the date of the certificate generation + */ + public PackedDate getEffectiveDate() + throws IOException + { + return certificateBody.getCertificateEffectiveDate(); + } + + + /** + * @return the type of certificate (request or profile) + * value is either Iso7816CertificateBody.profileType + * or Iso7816CertificateBody.requestType. Any other value + * is not valid. + */ + public int getCertificateType() + { + return this.certificateBody.getCertificateType(); + } + + /** + * @return the date of the certificate generation + */ + public PackedDate getExpirationDate() + throws IOException + { + return certificateBody.getCertificateExpirationDate(); + } + + + /** + * return a bits field coded on one byte. For signification of the + * several bit see Iso7816CertificateHolderAuthorization + * + * @return role and access rigth + * @throws IOException + * @see CertificateHolderAuthorization + */ + public int getRole() + throws IOException + { + CertificateHolderAuthorization cha = certificateBody.getCertificateHolderAuthorization(); + return cha.getAccessRights(); + } + + /** + * @return the Authority Reference field of the certificate + * @throws IOException + */ + public CertificationAuthorityReference getAuthorityReference() + throws IOException + { + return certificateBody.getCertificationAuthorityReference(); + } + + /** + * @return the Holder Reference Field of the certificate + * @throws IOException + */ + public CertificateHolderReference getHolderReference() + throws IOException + { + return certificateBody.getCertificateHolderReference(); + } + + /** + * @return the bits corresponding to the role intented for the certificate + * See Iso7816CertificateHolderAuthorization static int for values + * @throws IOException + */ + public int getHolderAuthorizationRole() + throws IOException + { + int rights = certificateBody.getCertificateHolderAuthorization().getAccessRights(); + return rights & 0xC0; + } + + /** + * @return the bits corresponding the authorizations contained in the certificate + * See Iso7816CertificateHolderAuthorization static int for values + * @throws IOException + */ + public Flags getHolderAuthorizationRights() + throws IOException + { + return new Flags(certificateBody.getCertificateHolderAuthorization().getAccessRights() & 0x1F); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CVCertificateRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CVCertificateRequest.java new file mode 100644 index 000000000..4170d6e40 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CVCertificateRequest.java @@ -0,0 +1,170 @@ +package org.spongycastle.asn1.eac; + +import java.io.IOException; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1ParsingException; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.BERTags; +import org.spongycastle.asn1.DERApplicationSpecific; +import org.spongycastle.asn1.DEROctetString; + +//import java.math.BigInteger; + + +public class CVCertificateRequest + extends ASN1Object +{ + private CertificateBody certificateBody; + + private byte[] innerSignature = null; + private byte[] outerSignature = null; + + private int valid; + + private static int bodyValid = 0x01; + private static int signValid = 0x02; + + private CVCertificateRequest(DERApplicationSpecific request) + throws IOException + { + if (request.getApplicationTag() == EACTags.AUTHENTIFICATION_DATA) + { + ASN1Sequence seq = ASN1Sequence.getInstance(request.getObject(BERTags.SEQUENCE)); + + initCertBody(DERApplicationSpecific.getInstance(seq.getObjectAt(0))); + + outerSignature = DERApplicationSpecific.getInstance(seq.getObjectAt(seq.size() - 1)).getContents(); + } + else + { + initCertBody(request); + } + } + + private void initCertBody(DERApplicationSpecific request) + throws IOException + { + if (request.getApplicationTag() == EACTags.CARDHOLDER_CERTIFICATE) + { + ASN1Sequence seq = ASN1Sequence.getInstance(request.getObject(BERTags.SEQUENCE)); + for (Enumeration en = seq.getObjects(); en.hasMoreElements();) + { + DERApplicationSpecific obj = DERApplicationSpecific.getInstance(en.nextElement()); + switch (obj.getApplicationTag()) + { + case EACTags.CERTIFICATE_CONTENT_TEMPLATE: + certificateBody = CertificateBody.getInstance(obj); + valid |= bodyValid; + break; + case EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP: + innerSignature = obj.getContents(); + valid |= signValid; + break; + default: + throw new IOException("Invalid tag, not an CV Certificate Request element:" + obj.getApplicationTag()); + } + } + } + else + { + throw new IOException("not a CARDHOLDER_CERTIFICATE in request:" + request.getApplicationTag()); + } + } + + public static CVCertificateRequest getInstance(Object obj) + { + if (obj instanceof CVCertificateRequest) + { + return (CVCertificateRequest)obj; + } + else if (obj != null) + { + try + { + return new CVCertificateRequest(DERApplicationSpecific.getInstance(obj)); + } + catch (IOException e) + { + throw new ASN1ParsingException("unable to parse data: " + e.getMessage(), e); + } + } + + return null; + } + + ASN1ObjectIdentifier signOid = null; + ASN1ObjectIdentifier keyOid = null; + + public static byte[] ZeroArray = new byte[]{0}; + + + String strCertificateHolderReference; + + byte[] encodedAuthorityReference; + + int ProfileId; + + /** + * Returns the body of the certificate template + * + * @return the body. + */ + public CertificateBody getCertificateBody() + { + return certificateBody; + } + + /** + * Return the public key data object carried in the request + * @return the public key + */ + public PublicKeyDataObject getPublicKey() + { + return certificateBody.getPublicKey(); + } + + public byte[] getInnerSignature() + { + return innerSignature; + } + + public byte[] getOuterSignature() + { + return outerSignature; + } + + byte[] certificate = null; + protected String overSignerReference = null; + + public boolean hasOuterSignature() + { + return outerSignature != null; + } + + byte[] encoded; + + PublicKeyDataObject iso7816PubKey = null; + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certificateBody); + + try + { + v.add(new DERApplicationSpecific(false, EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP, new DEROctetString(innerSignature))); + } + catch (IOException e) + { + throw new IllegalStateException("unable to convert signature!"); + } + + return new DERApplicationSpecific(EACTags.CARDHOLDER_CERTIFICATE, v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateBody.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateBody.java new file mode 100644 index 000000000..3c834fdc0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateBody.java @@ -0,0 +1,475 @@ +package org.spongycastle.asn1.eac; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.BERTags; +import org.spongycastle.asn1.DERApplicationSpecific; +import org.spongycastle.asn1.DEROctetString; + + +/** + * an Iso7816CertificateBody structure. + * + *
+ * CertificateBody ::= SEQUENCE { + * // version of the certificate format. Must be 0 (version 1) + * CertificateProfileIdentifer DERApplicationSpecific, + * //uniquely identifies the issuinng CA's signature key pair + * // contains the iso3166-1 alpha2 encoded country code, the + * // name of issuer and the sequence number of the key pair. + * CertificationAuthorityReference DERApplicationSpecific, + * // stores the encoded public key + * PublicKey Iso7816PublicKey, + * //associates the public key contained in the certificate with a unique name + * // contains the iso3166-1 alpha2 encoded country code, the + * // name of the holder and the sequence number of the key pair. + * certificateHolderReference DERApplicationSpecific, + * // Encodes the role of the holder (i.e. CVCA, DV, IS) and assigns read/write + * // access rights to data groups storing sensitive data + * certificateHolderAuthorization Iso7816CertificateHolderAuthorization, + * // the date of the certificate generation + * CertificateEffectiveDate DERApplicationSpecific, + * // the date after wich the certificate expires + * certificateExpirationDate DERApplicationSpecific + * } + *+ */ +public class CertificateBody + extends ASN1Object +{ + ASN1InputStream seq; + private DERApplicationSpecific certificateProfileIdentifier;// version of the certificate format. Must be 0 (version 1) + private DERApplicationSpecific certificationAuthorityReference;//uniquely identifies the issuinng CA's signature key pair + private PublicKeyDataObject publicKey;// stores the encoded public key + private DERApplicationSpecific certificateHolderReference;//associates the public key contained in the certificate with a unique name + private CertificateHolderAuthorization certificateHolderAuthorization;// Encodes the role of the holder (i.e. CVCA, DV, IS) and assigns read/write access rights to data groups storing sensitive data + private DERApplicationSpecific certificateEffectiveDate;// the date of the certificate generation + private DERApplicationSpecific certificateExpirationDate;// the date after wich the certificate expires + private int certificateType = 0;// bit field of initialized data. This will tell us if the data are valid. + private static final int CPI = 0x01;//certificate Profile Identifier + private static final int CAR = 0x02;//certification Authority Reference + private static final int PK = 0x04;//public Key + private static final int CHR = 0x08;//certificate Holder Reference + private static final int CHA = 0x10;//certificate Holder Authorization + private static final int CEfD = 0x20;//certificate Effective Date + private static final int CExD = 0x40;//certificate Expiration Date + + public static final int profileType = 0x7f;//Profile type Certificate + public static final int requestType = 0x0D;// Request type Certificate + + private void setIso7816CertificateBody(DERApplicationSpecific appSpe) + throws IOException + { + byte[] content; + if (appSpe.getApplicationTag() == EACTags.CERTIFICATE_CONTENT_TEMPLATE) + { + content = appSpe.getContents(); + } + else + { + throw new IOException("Bad tag : not an iso7816 CERTIFICATE_CONTENT_TEMPLATE"); + } + ASN1InputStream aIS = new ASN1InputStream(content); + ASN1Primitive obj; + while ((obj = aIS.readObject()) != null) + { + DERApplicationSpecific aSpe; + + if (obj instanceof DERApplicationSpecific) + { + aSpe = (DERApplicationSpecific)obj; + } + else + { + throw new IOException("Not a valid iso7816 content : not a DERApplicationSpecific Object :" + EACTags.encodeTag(appSpe) + obj.getClass()); + } + switch (aSpe.getApplicationTag()) + { + case EACTags.INTERCHANGE_PROFILE: + setCertificateProfileIdentifier(aSpe); + break; + case EACTags.ISSUER_IDENTIFICATION_NUMBER: + setCertificationAuthorityReference(aSpe); + break; + case EACTags.CARDHOLDER_PUBLIC_KEY_TEMPLATE: + setPublicKey(PublicKeyDataObject.getInstance(aSpe.getObject(BERTags.SEQUENCE))); + break; + case EACTags.CARDHOLDER_NAME: + setCertificateHolderReference(aSpe); + break; + case EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE: + setCertificateHolderAuthorization(new CertificateHolderAuthorization(aSpe)); + break; + case EACTags.APPLICATION_EFFECTIVE_DATE: + setCertificateEffectiveDate(aSpe); + break; + case EACTags.APPLICATION_EXPIRATION_DATE: + setCertificateExpirationDate(aSpe); + break; + default: + certificateType = 0; + throw new IOException("Not a valid iso7816 DERApplicationSpecific tag " + aSpe.getApplicationTag()); + } + } + } + + /** + * builds an Iso7816CertificateBody by settings each parameters. + * + * @param certificateProfileIdentifier + * @param certificationAuthorityReference + * + * @param publicKey + * @param certificateHolderReference + * @param certificateHolderAuthorization + * @param certificateEffectiveDate + * @param certificateExpirationDate + * @throws IOException + */ + public CertificateBody( + DERApplicationSpecific certificateProfileIdentifier, + CertificationAuthorityReference certificationAuthorityReference, + PublicKeyDataObject publicKey, + CertificateHolderReference certificateHolderReference, + CertificateHolderAuthorization certificateHolderAuthorization, + PackedDate certificateEffectiveDate, + PackedDate certificateExpirationDate + ) + { + setCertificateProfileIdentifier(certificateProfileIdentifier); + setCertificationAuthorityReference(new DERApplicationSpecific( + EACTags.ISSUER_IDENTIFICATION_NUMBER, certificationAuthorityReference.getEncoded())); + setPublicKey(publicKey); + setCertificateHolderReference(new DERApplicationSpecific( + EACTags.CARDHOLDER_NAME, certificateHolderReference.getEncoded())); + setCertificateHolderAuthorization(certificateHolderAuthorization); + try + { + setCertificateEffectiveDate(new DERApplicationSpecific( + false, EACTags.APPLICATION_EFFECTIVE_DATE, new DEROctetString(certificateEffectiveDate.getEncoding()))); + setCertificateExpirationDate(new DERApplicationSpecific( + false, EACTags.APPLICATION_EXPIRATION_DATE, new DEROctetString(certificateExpirationDate.getEncoding()))); + } + catch (IOException e) + { + throw new IllegalArgumentException("unable to encode dates: " + e.getMessage()); + } + } + + /** + * builds an Iso7816CertificateBody with an ASN1InputStream. + * + * @param obj DERApplicationSpecific containing the whole body. + * @throws IOException if the body is not valid. + */ + private CertificateBody(DERApplicationSpecific obj) + throws IOException + { + setIso7816CertificateBody(obj); + } + + /** + * create a profile type Iso7816CertificateBody. + * + * @return return the "profile" type certificate body. + * @throws IOException if the DERApplicationSpecific cannot be created. + */ + private ASN1Primitive profileToASN1Object() + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certificateProfileIdentifier); + v.add(certificationAuthorityReference); + v.add(new DERApplicationSpecific(false, EACTags.CARDHOLDER_PUBLIC_KEY_TEMPLATE, publicKey)); + v.add(certificateHolderReference); + v.add(certificateHolderAuthorization); + v.add(certificateEffectiveDate); + v.add(certificateExpirationDate); + return new DERApplicationSpecific(EACTags.CERTIFICATE_CONTENT_TEMPLATE, v); + } + + private void setCertificateProfileIdentifier(DERApplicationSpecific certificateProfileIdentifier) + throws IllegalArgumentException + { + if (certificateProfileIdentifier.getApplicationTag() == EACTags.INTERCHANGE_PROFILE) + { + this.certificateProfileIdentifier = certificateProfileIdentifier; + certificateType |= CPI; + } + else + { + throw new IllegalArgumentException("Not an Iso7816Tags.INTERCHANGE_PROFILE tag :" + EACTags.encodeTag(certificateProfileIdentifier)); + } + } + + private void setCertificateHolderReference(DERApplicationSpecific certificateHolderReference) + throws IllegalArgumentException + { + if (certificateHolderReference.getApplicationTag() == EACTags.CARDHOLDER_NAME) + { + this.certificateHolderReference = certificateHolderReference; + certificateType |= CHR; + } + else + { + throw new IllegalArgumentException("Not an Iso7816Tags.CARDHOLDER_NAME tag"); + } + } + + /** + * set the CertificationAuthorityReference. + * + * @param certificationAuthorityReference + * the DERApplicationSpecific containing the CertificationAuthorityReference. + * @throws IllegalArgumentException if the DERApplicationSpecific is not valid. + */ + private void setCertificationAuthorityReference( + DERApplicationSpecific certificationAuthorityReference) + throws IllegalArgumentException + { + if (certificationAuthorityReference.getApplicationTag() == EACTags.ISSUER_IDENTIFICATION_NUMBER) + { + this.certificationAuthorityReference = certificationAuthorityReference; + certificateType |= CAR; + } + else + { + throw new IllegalArgumentException("Not an Iso7816Tags.ISSUER_IDENTIFICATION_NUMBER tag"); + } + } + + /** + * set the public Key + * + * @param publicKey : the DERApplicationSpecific containing the public key + * @throws java.io.IOException + */ + private void setPublicKey(PublicKeyDataObject publicKey) + { + this.publicKey = PublicKeyDataObject.getInstance(publicKey); + this.certificateType |= PK; + } + + /** + * create a request type Iso7816CertificateBody. + * + * @return return the "request" type certificate body. + * @throws IOException if the DERApplicationSpecific cannot be created. + */ + private ASN1Primitive requestToASN1Object() + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certificateProfileIdentifier); + v.add(new DERApplicationSpecific(false, EACTags.CARDHOLDER_PUBLIC_KEY_TEMPLATE, publicKey)); + v.add(certificateHolderReference); + return new DERApplicationSpecific(EACTags.CERTIFICATE_CONTENT_TEMPLATE, v); + } + + /** + * create a "request" or "profile" type Iso7816CertificateBody according to the variables sets. + * + * @return return the ASN1Primitive representing the "request" or "profile" type certificate body. + * @throws IOException if the DERApplicationSpecific cannot be created or if data are missings to create a valid certificate. + */ + public ASN1Primitive toASN1Primitive() + { + try + { + if (certificateType == profileType) + { + return profileToASN1Object(); + } + if (certificateType == requestType) + { + return requestToASN1Object(); + } + } + catch (IOException e) + { + return null; + } + return null; + } + + /** + * gives the type of the certificate (value should be profileType or requestType if all data are set). + * + * @return the int representing the data already set. + */ + public int getCertificateType() + { + return certificateType; + } + + /** + * Gives an instance of Iso7816CertificateBody taken from Object obj + * + * @param obj is the Object to extract the certificate body from. + * @return the Iso7816CertificateBody taken from Object obj. + * @throws IOException if object is not valid. + */ + public static CertificateBody getInstance(Object obj) + throws IOException + { + if (obj instanceof CertificateBody) + { + return (CertificateBody)obj; + } + else if (obj != null) + { + return new CertificateBody(DERApplicationSpecific.getInstance(obj)); + } + + return null; + } + + /** + * @return the date of the certificate generation + */ + public PackedDate getCertificateEffectiveDate() + { + if ((this.certificateType & CertificateBody.CEfD) == + CertificateBody.CEfD) + { + return new PackedDate(certificateEffectiveDate.getContents()); + } + return null; + } + + /** + * set the date of the certificate generation + * + * @param ced DERApplicationSpecific containing the date of the certificate generation + * @throws IllegalArgumentException if the tag is not Iso7816Tags.APPLICATION_EFFECTIVE_DATE + */ + private void setCertificateEffectiveDate(DERApplicationSpecific ced) + throws IllegalArgumentException + { + if (ced.getApplicationTag() == EACTags.APPLICATION_EFFECTIVE_DATE) + { + this.certificateEffectiveDate = ced; + certificateType |= CEfD; + } + else + { + throw new IllegalArgumentException("Not an Iso7816Tags.APPLICATION_EFFECTIVE_DATE tag :" + EACTags.encodeTag(ced)); + } + } + + /** + * @return the date after wich the certificate expires + */ + public PackedDate getCertificateExpirationDate() + throws IOException + { + if ((this.certificateType & CertificateBody.CExD) == + CertificateBody.CExD) + { + return new PackedDate(certificateExpirationDate.getContents()); + } + throw new IOException("certificate Expiration Date not set"); + } + + /** + * set the date after wich the certificate expires + * + * @param ced DERApplicationSpecific containing the date after wich the certificate expires + * @throws IllegalArgumentException if the tag is not Iso7816Tags.APPLICATION_EXPIRATION_DATE + */ + private void setCertificateExpirationDate(DERApplicationSpecific ced) + throws IllegalArgumentException + { + if (ced.getApplicationTag() == EACTags.APPLICATION_EXPIRATION_DATE) + { + this.certificateExpirationDate = ced; + certificateType |= CExD; + } + else + { + throw new IllegalArgumentException("Not an Iso7816Tags.APPLICATION_EXPIRATION_DATE tag"); + } + } + + /** + * the Iso7816CertificateHolderAuthorization encodes the role of the holder + * (i.e. CVCA, DV, IS) and assigns read/write access rights to data groups + * storing sensitive data. This functions returns the Certificate Holder + * Authorization + * + * @return the Iso7816CertificateHolderAuthorization + */ + public CertificateHolderAuthorization getCertificateHolderAuthorization() + throws IOException + { + if ((this.certificateType & CertificateBody.CHA) == + CertificateBody.CHA) + { + return certificateHolderAuthorization; + } + throw new IOException("Certificate Holder Authorisation not set"); + } + + /** + * set the CertificateHolderAuthorization + * + * @param cha the Certificate Holder Authorization + */ + private void setCertificateHolderAuthorization( + CertificateHolderAuthorization cha) + { + this.certificateHolderAuthorization = cha; + certificateType |= CHA; + } + + /** + * certificateHolderReference : associates the public key contained in the certificate with a unique name + * + * @return the certificateHolderReference. + */ + public CertificateHolderReference getCertificateHolderReference() + { + return new CertificateHolderReference(certificateHolderReference.getContents()); + } + + /** + * CertificateProfileIdentifier : version of the certificate format. Must be 0 (version 1) + * + * @return the CertificateProfileIdentifier + */ + public DERApplicationSpecific getCertificateProfileIdentifier() + { + return certificateProfileIdentifier; + } + + /** + * get the certificationAuthorityReference + * certificationAuthorityReference : uniquely identifies the issuinng CA's signature key pair + * + * @return the certificationAuthorityReference + */ + public CertificationAuthorityReference getCertificationAuthorityReference() + throws IOException + { + if ((this.certificateType & CertificateBody.CAR) == + CertificateBody.CAR) + { + return new CertificationAuthorityReference(certificationAuthorityReference.getContents()); + } + throw new IOException("Certification authority reference not set"); + } + + /** + * @return the PublicKey + */ + public PublicKeyDataObject getPublicKey() + { + return publicKey; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateHolderAuthorization.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateHolderAuthorization.java new file mode 100644 index 000000000..5f23f973f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateHolderAuthorization.java @@ -0,0 +1,185 @@ +package org.spongycastle.asn1.eac; + +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERApplicationSpecific; +import org.spongycastle.util.Integers; + +/** + * an Iso7816CertificateHolderAuthorization structure. + * + *
+ * Certificate Holder Authorization ::= SEQUENCE { + * // specifies the format and the rules for the evaluation of the authorization + * // level + * ASN1ObjectIdentifier oid, + * // access rights + * DERApplicationSpecific accessRights, + * } + *+ */ +public class CertificateHolderAuthorization + extends ASN1Object +{ + ASN1ObjectIdentifier oid; + DERApplicationSpecific accessRights; + public static final ASN1ObjectIdentifier id_role_EAC = EACObjectIdentifiers.bsi_de.branch("3.1.2.1"); + public static final int CVCA = 0xC0; + public static final int DV_DOMESTIC = 0x80; + public static final int DV_FOREIGN = 0x40; + public static final int IS = 0; + public static final int RADG4 = 0x02;//Read Access to DG4 (Iris) + public static final int RADG3 = 0x01;//Read Access to DG3 (fingerprint) + + static Hashtable RightsDecodeMap = new Hashtable(); + static BidirectionalMap AuthorizationRole = new BidirectionalMap(); + static Hashtable ReverseMap = new Hashtable(); + + static + { + RightsDecodeMap.put(Integers.valueOf(RADG4), "RADG4"); + RightsDecodeMap.put(Integers.valueOf(RADG3), "RADG3"); + + AuthorizationRole.put(Integers.valueOf(CVCA), "CVCA"); + AuthorizationRole.put(Integers.valueOf(DV_DOMESTIC), "DV_DOMESTIC"); + AuthorizationRole.put(Integers.valueOf(DV_FOREIGN), "DV_FOREIGN"); + AuthorizationRole.put(Integers.valueOf(IS), "IS"); + + /* + for (int i : RightsDecodeMap.keySet()) + ReverseMap.put(RightsDecodeMap.get(i), i); + + for (int i : AuthorizationRole.keySet()) + ReverseMap.put(AuthorizationRole.get(i), i); + */ + } + + public static String GetRoleDescription(int i) + { + return (String)AuthorizationRole.get(Integers.valueOf(i)); + } + + public static int GetFlag(String description) + { + Integer i = (Integer)AuthorizationRole.getReverse(description); + if (i == null) + { + throw new IllegalArgumentException("Unknown value " + description); + } + + return i.intValue(); + } + + private void setPrivateData(ASN1InputStream cha) + throws IOException + { + ASN1Primitive obj; + obj = cha.readObject(); + if (obj instanceof ASN1ObjectIdentifier) + { + this.oid = (ASN1ObjectIdentifier)obj; + } + else + { + throw new IllegalArgumentException("no Oid in CerticateHolderAuthorization"); + } + obj = cha.readObject(); + if (obj instanceof DERApplicationSpecific) + { + this.accessRights = (DERApplicationSpecific)obj; + } + else + { + throw new IllegalArgumentException("No access rights in CerticateHolderAuthorization"); + } + } + + + /** + * create an Iso7816CertificateHolderAuthorization according to the parameters + * + * @param oid Object Identifier : specifies the format and the rules for the + * evaluatioin of the authorization level. + * @param rights specifies the access rights + * @throws IOException + */ + public CertificateHolderAuthorization(ASN1ObjectIdentifier oid, int rights) + throws IOException + { + setOid(oid); + setAccessRights((byte)rights); + } + + /** + * create an Iso7816CertificateHolderAuthorization according to the {@link DERApplicationSpecific} + * + * @param aSpe the DERApplicationSpecific containing the data + * @throws IOException + */ + public CertificateHolderAuthorization(DERApplicationSpecific aSpe) + throws IOException + { + if (aSpe.getApplicationTag() == EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE) + { + setPrivateData(new ASN1InputStream(aSpe.getContents())); + } + } + + /** + * @return containing the access rights + */ + public int getAccessRights() + { + return accessRights.getContents()[0] & 0xff; + } + + /** + * create a DERApplicationSpecific and set the access rights to "rights" + * + * @param rights byte containing the rights. + */ + private void setAccessRights(byte rights) + { + byte[] accessRights = new byte[1]; + accessRights[0] = rights; + this.accessRights = new DERApplicationSpecific( + EACTags.getTag(EACTags.DISCRETIONARY_DATA), accessRights); + } + + /** + * @return the Object identifier + */ + public ASN1ObjectIdentifier getOid() + { + return oid; + } + + /** + * set the Object Identifier + * + * @param oid {@link ASN1ObjectIdentifier} containing the Object Identifier + */ + private void setOid(ASN1ObjectIdentifier oid) + { + this.oid = oid; + } + + /** + * return the Certificate Holder Authorization as a DERApplicationSpecific Object + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(oid); + v.add(accessRights); + + return new DERApplicationSpecific(EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE, v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateHolderReference.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateHolderReference.java new file mode 100644 index 000000000..01651ff77 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificateHolderReference.java @@ -0,0 +1,66 @@ +package org.spongycastle.asn1.eac; + +import java.io.UnsupportedEncodingException; + +public class CertificateHolderReference +{ + private static final String ReferenceEncoding = "ISO-8859-1"; + + private String countryCode; + private String holderMnemonic; + private String sequenceNumber; + + public CertificateHolderReference(String countryCode, String holderMnemonic, String sequenceNumber) + { + this.countryCode = countryCode; + this.holderMnemonic = holderMnemonic; + this.sequenceNumber = sequenceNumber; + } + + CertificateHolderReference(byte[] contents) + { + try + { + String concat = new String(contents, ReferenceEncoding); + + this.countryCode = concat.substring(0, 2); + this.holderMnemonic = concat.substring(2, concat.length() - 5); + + this.sequenceNumber = concat.substring(concat.length() - 5); + } + catch (UnsupportedEncodingException e) + { + throw new IllegalStateException(e.toString()); + } + } + + public String getCountryCode() + { + return countryCode; + } + + public String getHolderMnemonic() + { + return holderMnemonic; + } + + public String getSequenceNumber() + { + return sequenceNumber; + } + + + public byte[] getEncoded() + { + String ref = countryCode + holderMnemonic + sequenceNumber; + + try + { + return ref.getBytes(ReferenceEncoding); + } + catch (UnsupportedEncodingException e) + { + throw new IllegalStateException(e.toString()); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificationAuthorityReference.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificationAuthorityReference.java new file mode 100644 index 000000000..993cf7295 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/CertificationAuthorityReference.java @@ -0,0 +1,15 @@ +package org.spongycastle.asn1.eac; + +public class CertificationAuthorityReference + extends CertificateHolderReference +{ + public CertificationAuthorityReference(String countryCode, String holderMnemonic, String sequenceNumber) + { + super(countryCode, holderMnemonic, sequenceNumber); + } + + CertificationAuthorityReference(byte[] contents) + { + super(contents); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/EACObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/EACObjectIdentifiers.java new file mode 100644 index 000000000..2c76e1ecd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/EACObjectIdentifiers.java @@ -0,0 +1,110 @@ +package org.spongycastle.asn1.eac; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * German Federal Office for Information Security + * (Bundesamt fĂŒr Sicherheit in der Informationstechnik) + * http://www.bsi.bund.de/ + *
+ * BSI TR-03110 + * Technical Guideline Advanced Security Mechanisms for Machine Readable Travel Documents + *
+ * Technical Guideline TR-03110-3 + * Advanced Security Mechanisms for Machine Readable Travel Documents; + * Part 3: Common Specifications. + */ + +public interface EACObjectIdentifiers +{ + /** + *
+ * bsi-de OBJECT IDENTIFIER ::= { + * itu-t(0) identified-organization(4) etsi(0) + * reserved(127) etsi-identified-organization(0) 7 + * } + *+ * OID: 0.4.0.127.0.7 + */ + static final ASN1ObjectIdentifier bsi_de = new ASN1ObjectIdentifier("0.4.0.127.0.7"); + + /** + *
+ * id-PK OBJECT IDENTIFIER ::= { + * bsi-de protocols(2) smartcard(2) 1 + * } + *+ * OID: 0.4.0.127.0.7.2.2.1 + */ + static final ASN1ObjectIdentifier id_PK = bsi_de.branch("2.2.1"); + + /** OID: 0.4.0.127.0.7.2.2.1.1 */ + static final ASN1ObjectIdentifier id_PK_DH = id_PK.branch("1"); + /** OID: 0.4.0.127.0.7.2.2.1.2 */ + static final ASN1ObjectIdentifier id_PK_ECDH = id_PK.branch("2"); + + /** + *
+ * id-CA OBJECT IDENTIFIER ::= { + * bsi-de protocols(2) smartcard(2) 3 + * } + *+ * OID: 0.4.0.127.0.7.2.2.3 + */ + static final ASN1ObjectIdentifier id_CA = bsi_de.branch("2.2.3"); + /** OID: 0.4.0.127.0.7.2.2.3.1 */ + static final ASN1ObjectIdentifier id_CA_DH = id_CA.branch("1"); + /** OID: 0.4.0.127.0.7.2.2.3.1.1 */ + static final ASN1ObjectIdentifier id_CA_DH_3DES_CBC_CBC = id_CA_DH.branch("1"); + /** OID: 0.4.0.127.0.7.2.2.3.2 */ + static final ASN1ObjectIdentifier id_CA_ECDH = id_CA.branch("2"); + /** OID: 0.4.0.127.0.7.2.2.3.2.1 */ + static final ASN1ObjectIdentifier id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1"); + + /** + *
+ * id-TA OBJECT IDENTIFIER ::= { + * bsi-de protocols(2) smartcard(2) 2 + * } + *+ * OID: 0.4.0.127.0.7.2.2.2 + */ + static final ASN1ObjectIdentifier id_TA = bsi_de.branch("2.2.2"); + + /** OID: 0.4.0.127.0.7.2.2.2.1 */ + static final ASN1ObjectIdentifier id_TA_RSA = id_TA.branch("1"); + /** OID: 0.4.0.127.0.7.2.2.2.1.1 */ + static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_1 = id_TA_RSA.branch("1"); + /** OID: 0.4.0.127.0.7.2.2.2.1.2 */ + static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2"); + /** OID: 0.4.0.127.0.7.2.2.2.1.3 */ + static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_1 = id_TA_RSA.branch("3"); + /** OID: 0.4.0.127.0.7.2.2.2.1.4 */ + static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_256 = id_TA_RSA.branch("4"); + /** OID: 0.4.0.127.0.7.2.2.2.1.5 */ + static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5"); + /** OID: 0.4.0.127.0.7.2.2.2.1.6 */ + static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_512 = id_TA_RSA.branch("6"); + /** OID: 0.4.0.127.0.7.2.2.2.2 */ + static final ASN1ObjectIdentifier id_TA_ECDSA = id_TA.branch("2"); + /** OID: 0.4.0.127.0.7.2.2.2.2.1 */ + static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_1 = id_TA_ECDSA.branch("1"); + /** OID: 0.4.0.127.0.7.2.2.2.2.2 */ + static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_224 = id_TA_ECDSA.branch("2"); + /** OID: 0.4.0.127.0.7.2.2.2.2.3 */ + static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_256 = id_TA_ECDSA.branch("3"); + /** OID: 0.4.0.127.0.7.2.2.2.2.4 */ + static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_384 = id_TA_ECDSA.branch("4"); + /** OID: 0.4.0.127.0.7.2.2.2.2.5 */ + static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_512 = id_TA_ECDSA.branch("5"); + + /** + *
+ * id-EAC-ePassport OBJECT IDENTIFIER ::= { + * bsi-de applications(3) mrtd(1) roles(2) 1 + * } + *+ * OID: 0.4.0.127.0.7.3.1.2.1 + */ + static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/EACTags.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/EACTags.java new file mode 100644 index 000000000..f061ebe65 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/EACTags.java @@ -0,0 +1,209 @@ +package org.spongycastle.asn1.eac; + +import org.spongycastle.asn1.BERTags; +import org.spongycastle.asn1.DERApplicationSpecific; + +public class EACTags +{ + public static final int OBJECT_IDENTIFIER = 0x06; + public static final int COUNTRY_CODE_NATIONAL_DATA = 0x41; + public static final int ISSUER_IDENTIFICATION_NUMBER = 0x02; //0x42; + public static final int CARD_SERVICE_DATA = 0x43; + public static final int INITIAL_ACCESS_DATA = 0x44; + public static final int CARD_ISSUER_DATA = 0x45; + public static final int PRE_ISSUING_DATA = 0x46; + public static final int CARD_CAPABILITIES = 0x47; + public static final int STATUS_INFORMATION = 0x48; + public static final int EXTENDED_HEADER_LIST = 0x4D; + public static final int APPLICATION_IDENTIFIER = 0x4F; + public static final int APPLICATION_LABEL = 0x50; + public static final int FILE_REFERENCE = 0x51; + public static final int COMMAND_TO_PERFORM = 0x52; + public static final int DISCRETIONARY_DATA = 0x53; + public static final int OFFSET_DATA_OBJECT = 0x54; + public static final int TRACK1_APPLICATION = 0x56; + public static final int TRACK2_APPLICATION = 0x57; + public static final int TRACK3_APPLICATION = 0x58; + public static final int CARD_EXPIRATION_DATA = 0x59; + public static final int PRIMARY_ACCOUNT_NUMBER = 0x5A;// PAN + public static final int NAME = 0x5B; + public static final int TAG_LIST = 0x5C; + public static final int HEADER_LIST = 0x5D; + public static final int LOGIN_DATA = 0x5E; + public static final int CARDHOLDER_NAME = 0x20; // 0x5F20; + public static final int TRACK1_CARD = 0x5F21; + public static final int TRACK2_CARD = 0x5F22; + public static final int TRACK3_CARD = 0x5F23; + public static final int APPLICATION_EXPIRATION_DATE = 0x24; // 0x5F24; + public static final int APPLICATION_EFFECTIVE_DATE = 0x25; // 0x5F25; + public static final int CARD_EFFECTIVE_DATE = 0x5F26; + public static final int INTERCHANGE_CONTROL = 0x5F27; + public static final int COUNTRY_CODE = 0x5F28; + public static final int INTERCHANGE_PROFILE = 0x29; // 0x5F29; + public static final int CURRENCY_CODE = 0x5F2A; + public static final int DATE_OF_BIRTH = 0x5F2B; + public static final int CARDHOLDER_NATIONALITY = 0x5F2C; + public static final int LANGUAGE_PREFERENCES = 0x5F2D; + public static final int CARDHOLDER_BIOMETRIC_DATA = 0x5F2E; + public static final int PIN_USAGE_POLICY = 0x5F2F; + public static final int SERVICE_CODE = 0x5F30; + public static final int TRANSACTION_COUNTER = 0x5F32; + public static final int TRANSACTION_DATE = 0x5F33; + public static final int CARD_SEQUENCE_NUMBER = 0x5F34; + public static final int SEX = 0x5F35; + public static final int CURRENCY_EXPONENT = 0x5F36; + public static final int STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP = 0x37; // 0x5F37; + public static final int SIGNATURE = 0x5F37; + public static final int STATIC_INTERNAL_AUTHENTIFICATION_FIRST_DATA = 0x5F38; + public static final int STATIC_INTERNAL_AUTHENTIFICATION_SECOND_DATA = 0x5F39; + public static final int DYNAMIC_INTERNAL_AUTHENTIFICATION = 0x5F3A; + public static final int DYNAMIC_EXTERNAL_AUTHENTIFICATION = 0x5F3B; + public static final int DYNAMIC_MUTUAL_AUTHENTIFICATION = 0x5F3C; + public static final int CARDHOLDER_PORTRAIT_IMAGE = 0x5F40; + public static final int ELEMENT_LIST = 0x5F41; + public static final int ADDRESS = 0x5F42; + public static final int CARDHOLDER_HANDWRITTEN_SIGNATURE = 0x5F43; + public static final int APPLICATION_IMAGE = 0x5F44; + public static final int DISPLAY_IMAGE = 0x5F45; + public static final int TIMER = 0x5F46; + public static final int MESSAGE_REFERENCE = 0x5F47; + public static final int CARDHOLDER_PRIVATE_KEY = 0x5F48; + public static final int CARDHOLDER_PUBLIC_KEY = 0x5F49; + public static final int CERTIFICATION_AUTHORITY_PUBLIC_KEY = 0x5F4A; + public static final int DEPRECATED = 0x5F4B; + public static final int CERTIFICATE_HOLDER_AUTHORIZATION = 0x5F4C;// Not yet defined in iso7816. The allocation is requested + public static final int INTEGRATED_CIRCUIT_MANUFACTURER_ID = 0x5F4D; + public static final int CERTIFICATE_CONTENT = 0x5F4E; + public static final int UNIFORM_RESOURCE_LOCATOR = 0x5F50; + public static final int ANSWER_TO_RESET = 0x5F51; + public static final int HISTORICAL_BYTES = 0x5F52; + public static final int DIGITAL_SIGNATURE = 0x5F3D; + public static final int APPLICATION_TEMPLATE = 0x61; + public static final int FCP_TEMPLATE = 0x62; + public static final int WRAPPER = 0x63; + public static final int FMD_TEMPLATE = 0x64; + public static final int CARDHOLDER_RELATIVE_DATA = 0x65; + public static final int CARD_DATA = 0x66; + public static final int AUTHENTIFICATION_DATA = 0x67; + public static final int SPECIAL_USER_REQUIREMENTS = 0x68; + public static final int LOGIN_TEMPLATE = 0x6A; + public static final int QUALIFIED_NAME = 0x6B; + public static final int CARDHOLDER_IMAGE_TEMPLATE = 0x6C; + public static final int APPLICATION_IMAGE_TEMPLATE = 0x6D; + public static final int APPLICATION_RELATED_DATA = 0x6E; + public static final int FCI_TEMPLATE = 0x6F; + public static final int DISCRETIONARY_DATA_OBJECTS = 0x73; + public static final int COMPATIBLE_TAG_ALLOCATION_AUTHORITY = 0x78; + public static final int COEXISTANT_TAG_ALLOCATION_AUTHORITY = 0x79; + public static final int SECURITY_SUPPORT_TEMPLATE = 0x7A; + public static final int SECURITY_ENVIRONMENT_TEMPLATE = 0x7B; + public static final int DYNAMIC_AUTHENTIFICATION_TEMPLATE = 0x7C; + public static final int SECURE_MESSAGING_TEMPLATE = 0x7D; + public static final int NON_INTERINDUSTRY_DATA_OBJECT_NESTING_TEMPLATE = 0x7E; + public static final int DISPLAY_CONTROL = 0x7F20; + public static final int CARDHOLDER_CERTIFICATE = 0x21; // 0x7F21; + public static final int CV_CERTIFICATE = 0x7F21; + public static final int CARDHOLER_REQUIREMENTS_INCLUDED_FEATURES = 0x7F22; + public static final int CARDHOLER_REQUIREMENTS_EXCLUDED_FEATURES = 0x7F23; + public static final int BIOMETRIC_DATA_TEMPLATE = 0x7F2E; + public static final int DIGITAL_SIGNATURE_BLOCK = 0x7F3D; + public static final int CARDHOLDER_PRIVATE_KEY_TEMPLATE = 0x7F48; + public static final int CARDHOLDER_PUBLIC_KEY_TEMPLATE = 0x49; // 0x7F49; + public static final int CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE = 0x4C; // 0x7F4C; + public static final int CERTIFICATE_CONTENT_TEMPLATE = 0x4E; // 0x7F4E; + public static final int CERTIFICATE_BODY = 0x4E; // 0x7F4E; + public static final int BIOMETRIC_INFORMATION_TEMPLATE = 0x7F60; + public static final int BIOMETRIC_INFORMATION_GROUP_TEMPLATE = 0x7F61; + + public static int getTag(int encodedTag) + { + /* + int i; + for (i = 24; i>=0; i-=8) { + if (((0xFF<> i); + } + return 0; + */ + return decodeTag(encodedTag); + } + + public static int getTagNo(int tag) + { + int i; + for (i = 24; i >= 0; i -= 8) + { + if (((0xFF << i) & tag) != 0) + { + return ((~(0xFF << i)) & tag); + } + } + return 0; + } + + public static int encodeTag(DERApplicationSpecific spec) + { + int retValue = BERTags.APPLICATION; + boolean constructed = spec.isConstructed(); + if (constructed) + { + retValue |= BERTags.CONSTRUCTED; + } + + int tag = spec.getApplicationTag(); + + if (tag > 31) + { + retValue |= 0x1F; + retValue <<= 8; + + int currentByte = tag & 0x7F; + retValue |= currentByte; + tag >>= 7; + + while (tag > 0) + { + retValue |= 0x80; + retValue <<= 8; + + currentByte = tag & 0x7F; + tag >>= 7; + } + } + else + { + retValue |= tag; + } + + return retValue; + } + + public static int decodeTag(int tag) + { + int retValue = 0; + boolean multiBytes = false; + for (int i = 24; i >= 0; i -= 8) + { + int currentByte = tag >> i & 0xFF; + if (currentByte == 0) + { + continue; + } + + if (multiBytes) + { + retValue <<= 7; + retValue |= currentByte & 0x7F; + } + else if ((currentByte & 0x1F) == 0x1F) + { + multiBytes = true; + } + else + { + return currentByte & 0x1F; // higher order bit are for DER.Constructed and type + } + } + return retValue; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/ECDSAPublicKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/ECDSAPublicKey.java new file mode 100644 index 000000000..376dcfa89 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/ECDSAPublicKey.java @@ -0,0 +1,341 @@ +package org.spongycastle.asn1.eac; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * an Iso7816ECDSAPublicKeyStructure structure. + * + *
+ * Certificate Holder Authorization ::= SEQUENCE { + * ASN1TaggedObject primeModulusP; // OPTIONAL + * ASN1TaggedObject firstCoefA; // OPTIONAL + * ASN1TaggedObject secondCoefB; // OPTIONAL + * ASN1TaggedObject basePointG; // OPTIONAL + * ASN1TaggedObject orderOfBasePointR; // OPTIONAL + * ASN1TaggedObject publicPointY; //REQUIRED + * ASN1TaggedObject cofactorF; // OPTIONAL + * } + *+ */ +public class ECDSAPublicKey + extends PublicKeyDataObject +{ + private ASN1ObjectIdentifier usage; + private BigInteger primeModulusP; // OPTIONAL + private BigInteger firstCoefA; // OPTIONAL + private BigInteger secondCoefB; // OPTIONAL + private byte[] basePointG; // OPTIONAL + private BigInteger orderOfBasePointR; // OPTIONAL + private byte[] publicPointY; //REQUIRED + private BigInteger cofactorF; // OPTIONAL + private int options; + private static final int P = 0x01; + private static final int A = 0x02; + private static final int B = 0x04; + private static final int G = 0x08; + private static final int R = 0x10; + private static final int Y = 0x20; + private static final int F = 0x40; + + ECDSAPublicKey(ASN1Sequence seq) + throws IllegalArgumentException + { + Enumeration en = seq.getObjects(); + + this.usage = ASN1ObjectIdentifier.getInstance(en.nextElement()); + + options = 0; + while (en.hasMoreElements()) + { + Object obj = en.nextElement(); + + if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject to = (ASN1TaggedObject)obj; + switch (to.getTagNo()) + { + case 0x1: + setPrimeModulusP(UnsignedInteger.getInstance(to).getValue()); + break; + case 0x2: + setFirstCoefA(UnsignedInteger.getInstance(to).getValue()); + break; + case 0x3: + setSecondCoefB(UnsignedInteger.getInstance(to).getValue()); + break; + case 0x4: + setBasePointG(ASN1OctetString.getInstance(to, false)); + break; + case 0x5: + setOrderOfBasePointR(UnsignedInteger.getInstance(to).getValue()); + break; + case 0x6: + setPublicPointY(ASN1OctetString.getInstance(to, false)); + break; + case 0x7: + setCofactorF(UnsignedInteger.getInstance(to).getValue()); + break; + default: + options = 0; + throw new IllegalArgumentException("Unknown Object Identifier!"); + } + } + else + { + throw new IllegalArgumentException("Unknown Object Identifier!"); + } + } + if (options != 0x20 && options != 0x7F) + { + throw new IllegalArgumentException("All options must be either present or absent!"); + } + } + + public ECDSAPublicKey(ASN1ObjectIdentifier usage, byte[] ppY) + throws IllegalArgumentException + { + this.usage = usage; + setPublicPointY(new DEROctetString(ppY)); + } + + public ECDSAPublicKey(ASN1ObjectIdentifier usage, BigInteger p, BigInteger a, BigInteger b, byte[] basePoint, BigInteger order, byte[] publicPoint, int cofactor) + { + this.usage = usage; + setPrimeModulusP(p); + setFirstCoefA(a); + setSecondCoefB(b); + setBasePointG(new DEROctetString(basePoint)); + setOrderOfBasePointR(order); + setPublicPointY(new DEROctetString(publicPoint)); + setCofactorF(BigInteger.valueOf(cofactor)); + } + + public ASN1ObjectIdentifier getUsage() + { + return usage; + } + + public byte[] getBasePointG() + { + if ((options & G) != 0) + { + return basePointG; + } + else + { + return null; + } + } + + private void setBasePointG(ASN1OctetString basePointG) + throws IllegalArgumentException + { + if ((options & G) == 0) + { + options |= G; + this.basePointG = basePointG.getOctets(); + } + else + { + throw new IllegalArgumentException("Base Point G already set"); + } + } + + public BigInteger getCofactorF() + { + if ((options & F) != 0) + { + return cofactorF; + } + else + { + return null; + } + } + + private void setCofactorF(BigInteger cofactorF) + throws IllegalArgumentException + { + if ((options & F) == 0) + { + options |= F; + this.cofactorF = cofactorF; + } + else + { + throw new IllegalArgumentException("Cofactor F already set"); + } + } + + public BigInteger getFirstCoefA() + { + if ((options & A) != 0) + { + return firstCoefA; + } + else + { + return null; + } + } + + private void setFirstCoefA(BigInteger firstCoefA) + throws IllegalArgumentException + { + if ((options & A) == 0) + { + options |= A; + this.firstCoefA = firstCoefA; + } + else + { + throw new IllegalArgumentException("First Coef A already set"); + } + } + + public BigInteger getOrderOfBasePointR() + { + if ((options & R) != 0) + { + return orderOfBasePointR; + } + else + { + return null; + } + } + + private void setOrderOfBasePointR(BigInteger orderOfBasePointR) + throws IllegalArgumentException + { + if ((options & R) == 0) + { + options |= R; + this.orderOfBasePointR = orderOfBasePointR; + } + else + { + throw new IllegalArgumentException("Order of base point R already set"); + } + } + + public BigInteger getPrimeModulusP() + { + if ((options & P) != 0) + { + return primeModulusP; + } + else + { + return null; + } + } + + private void setPrimeModulusP(BigInteger primeModulusP) + { + if ((options & P) == 0) + { + options |= P; + this.primeModulusP = primeModulusP; + } + else + { + throw new IllegalArgumentException("Prime Modulus P already set"); + } + } + + public byte[] getPublicPointY() + { + if ((options & Y) != 0) + { + return publicPointY; + } + else + { + return null; + } + } + + private void setPublicPointY(ASN1OctetString publicPointY) + throws IllegalArgumentException + { + if ((options & Y) == 0) + { + options |= Y; + this.publicPointY = publicPointY.getOctets(); + } + else + { + throw new IllegalArgumentException("Public Point Y already set"); + } + } + + public BigInteger getSecondCoefB() + { + if ((options & B) != 0) + { + return secondCoefB; + } + else + { + return null; + } + } + + private void setSecondCoefB(BigInteger secondCoefB) + throws IllegalArgumentException + { + if ((options & B) == 0) + { + options |= B; + this.secondCoefB = secondCoefB; + } + else + { + throw new IllegalArgumentException("Second Coef B already set"); + } + } + + public boolean hasParameters() + { + return primeModulusP != null; + } + + public ASN1EncodableVector getASN1EncodableVector(ASN1ObjectIdentifier oid, boolean publicPointOnly) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(oid); + + if (!publicPointOnly) + { + v.add(new UnsignedInteger(0x01, getPrimeModulusP())); + v.add(new UnsignedInteger(0x02, getFirstCoefA())); + v.add(new UnsignedInteger(0x03, getSecondCoefB())); + v.add(new DERTaggedObject(false, 0x04, new DEROctetString(getBasePointG()))); + v.add(new UnsignedInteger(0x05, getOrderOfBasePointR())); + } + v.add(new DERTaggedObject(false, 0x06, new DEROctetString(getPublicPointY()))); + if (!publicPointOnly) + { + v.add(new UnsignedInteger(0x07, getCofactorF())); + } + + return v; + } + + public ASN1Primitive toASN1Primitive() + { + return new DERSequence(getASN1EncodableVector(usage, false)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/Flags.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/Flags.java new file mode 100644 index 000000000..13b56a935 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/Flags.java @@ -0,0 +1,96 @@ +package org.spongycastle.asn1.eac; + +import java.util.Enumeration; +import java.util.Hashtable; + + +public class Flags +{ + + int value = 0; + + public Flags() + { + + } + + public Flags(int v) + { + value = v; + } + + public void set(int flag) + { + value |= flag; + } + + public boolean isSet(int flag) + { + return (value & flag) != 0; + } + + public int getFlags() + { + return value; + } + + /* Java 1.5 + String decode(Map
+ * Certificate Holder Authorization ::= SEQUENCE { + * // modulus should be at least 1024bit and a multiple of 512. + * DERTaggedObject modulus, + * // access rights exponent + * DERTaggedObject accessRights, + * } + *+ */ +public class RSAPublicKey + extends PublicKeyDataObject +{ + private ASN1ObjectIdentifier usage; + private BigInteger modulus; + private BigInteger exponent; + private int valid = 0; + private static int modulusValid = 0x01; + private static int exponentValid = 0x02; + + RSAPublicKey(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + + this.usage = ASN1ObjectIdentifier.getInstance(en.nextElement()); + + while (en.hasMoreElements()) + { + UnsignedInteger val = UnsignedInteger.getInstance(en.nextElement()); + + switch (val.getTagNo()) + { + case 0x1: + setModulus(val); + break; + case 0x2: + setExponent(val); + break; + default: + throw new IllegalArgumentException("Unknown DERTaggedObject :" + val.getTagNo() + "-> not an Iso7816RSAPublicKeyStructure"); + } + } + if (valid != 0x3) + { + throw new IllegalArgumentException("missing argument -> not an Iso7816RSAPublicKeyStructure"); + } + } + + public RSAPublicKey(ASN1ObjectIdentifier usage, BigInteger modulus, BigInteger exponent) + { + this.usage = usage; + this.modulus = modulus; + this.exponent = exponent; + } + + public ASN1ObjectIdentifier getUsage() + { + return usage; + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return exponent; + } + + private void setModulus(UnsignedInteger modulus) + { + if ((valid & modulusValid) == 0) + { + valid |= modulusValid; + this.modulus = modulus.getValue(); + } + else + { + throw new IllegalArgumentException("Modulus already set"); + } + } + + private void setExponent(UnsignedInteger exponent) + { + if ((valid & exponentValid) == 0) + { + valid |= exponentValid; + this.exponent = exponent.getValue(); + } + else + { + throw new IllegalArgumentException("Exponent already set"); + } + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(usage); + v.add(new UnsignedInteger(0x01, getModulus())); + v.add(new UnsignedInteger(0x02, getPublicExponent())); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/UnsignedInteger.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/UnsignedInteger.java new file mode 100644 index 000000000..96fc739a8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/eac/UnsignedInteger.java @@ -0,0 +1,74 @@ +package org.spongycastle.asn1.eac; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERTaggedObject; + +public class UnsignedInteger + extends ASN1Object +{ + private int tagNo; + private BigInteger value; + + public UnsignedInteger(int tagNo, BigInteger value) + { + this.tagNo = tagNo; + this.value = value; + } + + private UnsignedInteger(ASN1TaggedObject obj) + { + this.tagNo = obj.getTagNo(); + this.value = new BigInteger(1, ASN1OctetString.getInstance(obj, false).getOctets()); + } + + public static UnsignedInteger getInstance(Object obj) + { + if (obj instanceof UnsignedInteger) + { + return (UnsignedInteger)obj; + } + if (obj != null) + { + return new UnsignedInteger(ASN1TaggedObject.getInstance(obj)); + } + + return null; + } + + private byte[] convertValue() + { + byte[] v = value.toByteArray(); + + if (v[0] == 0) + { + byte[] tmp = new byte[v.length - 1]; + + System.arraycopy(v, 1, tmp, 0, tmp.length); + + return tmp; + } + + return v; + } + + public int getTagNo() + { + return tagNo; + } + + public BigInteger getValue() + { + return value; + } + + public ASN1Primitive toASN1Primitive() + { + return new DERTaggedObject(false, tagNo, new DEROctetString(convertValue())); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeIdentifier.java new file mode 100644 index 000000000..7941bcd0c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeIdentifier.java @@ -0,0 +1,14 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +public interface CommitmentTypeIdentifier +{ + public static final ASN1ObjectIdentifier proofOfOrigin = PKCSObjectIdentifiers.id_cti_ets_proofOfOrigin; + public static final ASN1ObjectIdentifier proofOfReceipt = PKCSObjectIdentifiers.id_cti_ets_proofOfReceipt; + public static final ASN1ObjectIdentifier proofOfDelivery = PKCSObjectIdentifiers.id_cti_ets_proofOfDelivery; + public static final ASN1ObjectIdentifier proofOfSender = PKCSObjectIdentifiers.id_cti_ets_proofOfSender; + public static final ASN1ObjectIdentifier proofOfApproval = PKCSObjectIdentifiers.id_cti_ets_proofOfApproval; + public static final ASN1ObjectIdentifier proofOfCreation = PKCSObjectIdentifiers.id_cti_ets_proofOfCreation; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeIndication.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeIndication.java new file mode 100644 index 000000000..1caec133e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeIndication.java @@ -0,0 +1,83 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class CommitmentTypeIndication + extends ASN1Object +{ + private ASN1ObjectIdentifier commitmentTypeId; + private ASN1Sequence commitmentTypeQualifier; + + private CommitmentTypeIndication( + ASN1Sequence seq) + { + commitmentTypeId = (ASN1ObjectIdentifier)seq.getObjectAt(0); + + if (seq.size() > 1) + { + commitmentTypeQualifier = (ASN1Sequence)seq.getObjectAt(1); + } + } + + public CommitmentTypeIndication( + ASN1ObjectIdentifier commitmentTypeId) + { + this.commitmentTypeId = commitmentTypeId; + } + + public CommitmentTypeIndication( + ASN1ObjectIdentifier commitmentTypeId, + ASN1Sequence commitmentTypeQualifier) + { + this.commitmentTypeId = commitmentTypeId; + this.commitmentTypeQualifier = commitmentTypeQualifier; + } + + public static CommitmentTypeIndication getInstance( + Object obj) + { + if (obj == null || obj instanceof CommitmentTypeIndication) + { + return (CommitmentTypeIndication)obj; + } + + return new CommitmentTypeIndication(ASN1Sequence.getInstance(obj)); + } + + public ASN1ObjectIdentifier getCommitmentTypeId() + { + return commitmentTypeId; + } + + public ASN1Sequence getCommitmentTypeQualifier() + { + return commitmentTypeQualifier; + } + + /** + *
+ * CommitmentTypeIndication ::= SEQUENCE { + * commitmentTypeId CommitmentTypeIdentifier, + * commitmentTypeQualifier SEQUENCE SIZE (1..MAX) OF + * CommitmentTypeQualifier OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(commitmentTypeId); + + if (commitmentTypeQualifier != null) + { + v.add(commitmentTypeQualifier); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeQualifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeQualifier.java new file mode 100644 index 000000000..d4b909c25 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CommitmentTypeQualifier.java @@ -0,0 +1,108 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * Commitment type qualifiers, used in the Commitment-Type-Indication attribute (RFC3126). + * + *
+ * CommitmentTypeQualifier ::= SEQUENCE { + * commitmentTypeIdentifier CommitmentTypeIdentifier, + * qualifier ANY DEFINED BY commitmentTypeIdentifier OPTIONAL } + *+ */ +public class CommitmentTypeQualifier + extends ASN1Object +{ + private ASN1ObjectIdentifier commitmentTypeIdentifier; + private ASN1Encodable qualifier; + + /** + * Creates a new
CommitmentTypeQualifier
instance.
+ *
+ * @param commitmentTypeIdentifier a CommitmentTypeIdentifier
value
+ */
+ public CommitmentTypeQualifier(
+ ASN1ObjectIdentifier commitmentTypeIdentifier)
+ {
+ this(commitmentTypeIdentifier, null);
+ }
+
+ /**
+ * Creates a new CommitmentTypeQualifier
instance.
+ *
+ * @param commitmentTypeIdentifier a CommitmentTypeIdentifier
value
+ * @param qualifier the qualifier, defined by the above field.
+ */
+ public CommitmentTypeQualifier(
+ ASN1ObjectIdentifier commitmentTypeIdentifier,
+ ASN1Encodable qualifier)
+ {
+ this.commitmentTypeIdentifier = commitmentTypeIdentifier;
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * Creates a new CommitmentTypeQualifier
instance.
+ *
+ * @param as CommitmentTypeQualifier
structure
+ * encoded as an ASN1Sequence.
+ */
+ private CommitmentTypeQualifier(
+ ASN1Sequence as)
+ {
+ commitmentTypeIdentifier = (ASN1ObjectIdentifier)as.getObjectAt(0);
+
+ if (as.size() > 1)
+ {
+ qualifier = as.getObjectAt(1);
+ }
+ }
+
+ public static CommitmentTypeQualifier getInstance(Object as)
+ {
+ if (as instanceof CommitmentTypeQualifier)
+ {
+ return (CommitmentTypeQualifier)as;
+ }
+ else if (as != null)
+ {
+ return new CommitmentTypeQualifier(ASN1Sequence.getInstance(as));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getCommitmentTypeIdentifier()
+ {
+ return commitmentTypeIdentifier;
+ }
+
+ public ASN1Encodable getQualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Returns a DER-encodable representation of this instance.
+ *
+ * @return a ASN1Primitive
value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ dev.add(commitmentTypeIdentifier);
+ if (qualifier != null)
+ {
+ dev.add(qualifier);
+ }
+
+ return new DERSequence(dev);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CompleteRevocationRefs.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CompleteRevocationRefs.java
new file mode 100644
index 000000000..1fb4771ba
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CompleteRevocationRefs.java
@@ -0,0 +1,65 @@
+package org.spongycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * + * CompleteRevocationRefs ::= SEQUENCE OF CrlOcspRef + *+ */ +public class CompleteRevocationRefs + extends ASN1Object +{ + + private ASN1Sequence crlOcspRefs; + + public static CompleteRevocationRefs getInstance(Object obj) + { + if (obj instanceof CompleteRevocationRefs) + { + return (CompleteRevocationRefs)obj; + } + else if (obj != null) + { + return new CompleteRevocationRefs(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CompleteRevocationRefs(ASN1Sequence seq) + { + Enumeration seqEnum = seq.getObjects(); + while (seqEnum.hasMoreElements()) + { + CrlOcspRef.getInstance(seqEnum.nextElement()); + } + this.crlOcspRefs = seq; + } + + public CompleteRevocationRefs(CrlOcspRef[] crlOcspRefs) + { + this.crlOcspRefs = new DERSequence(crlOcspRefs); + } + + public CrlOcspRef[] getCrlOcspRefs() + { + CrlOcspRef[] result = new CrlOcspRef[this.crlOcspRefs.size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = CrlOcspRef.getInstance(this.crlOcspRefs + .getObjectAt(idx)); + } + return result; + } + + public ASN1Primitive toASN1Primitive() + { + return this.crlOcspRefs; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlIdentifier.java new file mode 100644 index 000000000..10968b97f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlIdentifier.java @@ -0,0 +1,106 @@ +package org.spongycastle.asn1.esf; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1UTCTime; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.X500Name; + +/** + *
+ * CrlIdentifier ::= SEQUENCE + * { + * crlissuer Name, + * crlIssuedTime UTCTime, + * crlNumber INTEGER OPTIONAL + * } + *+ */ +public class CrlIdentifier + extends ASN1Object +{ + private X500Name crlIssuer; + private ASN1UTCTime crlIssuedTime; + private ASN1Integer crlNumber; + + public static CrlIdentifier getInstance(Object obj) + { + if (obj instanceof CrlIdentifier) + { + return (CrlIdentifier)obj; + } + else if (obj != null) + { + return new CrlIdentifier(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CrlIdentifier(ASN1Sequence seq) + { + if (seq.size() < 2 || seq.size() > 3) + { + throw new IllegalArgumentException(); + } + this.crlIssuer = X500Name.getInstance(seq.getObjectAt(0)); + this.crlIssuedTime = ASN1UTCTime.getInstance(seq.getObjectAt(1)); + if (seq.size() > 2) + { + this.crlNumber = ASN1Integer.getInstance(seq.getObjectAt(2)); + } + } + + public CrlIdentifier(X500Name crlIssuer, ASN1UTCTime crlIssuedTime) + { + this(crlIssuer, crlIssuedTime, null); + } + + public CrlIdentifier(X500Name crlIssuer, ASN1UTCTime crlIssuedTime, + BigInteger crlNumber) + { + this.crlIssuer = crlIssuer; + this.crlIssuedTime = crlIssuedTime; + if (null != crlNumber) + { + this.crlNumber = new ASN1Integer(crlNumber); + } + } + + public X500Name getCrlIssuer() + { + return this.crlIssuer; + } + + public ASN1UTCTime getCrlIssuedTime() + { + return this.crlIssuedTime; + } + + public BigInteger getCrlNumber() + { + if (null == this.crlNumber) + { + return null; + } + return this.crlNumber.getValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.crlIssuer.toASN1Primitive()); + v.add(this.crlIssuedTime); + if (null != this.crlNumber) + { + v.add(this.crlNumber); + } + return new DERSequence(v); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlListID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlListID.java new file mode 100644 index 000000000..f474d8339 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlListID.java @@ -0,0 +1,66 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
+ * CRLListID ::= SEQUENCE { + * crls SEQUENCE OF CrlValidatedID } + *+ */ +public class CrlListID + extends ASN1Object +{ + + private ASN1Sequence crls; + + public static CrlListID getInstance(Object obj) + { + if (obj instanceof CrlListID) + { + return (CrlListID)obj; + } + else if (obj != null) + { + return new CrlListID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CrlListID(ASN1Sequence seq) + { + this.crls = (ASN1Sequence)seq.getObjectAt(0); + Enumeration e = this.crls.getObjects(); + while (e.hasMoreElements()) + { + CrlValidatedID.getInstance(e.nextElement()); + } + } + + public CrlListID(CrlValidatedID[] crls) + { + this.crls = new DERSequence(crls); + } + + public CrlValidatedID[] getCrls() + { + CrlValidatedID[] result = new CrlValidatedID[this.crls.size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = CrlValidatedID + .getInstance(this.crls.getObjectAt(idx)); + } + return result; + } + + public ASN1Primitive toASN1Primitive() + { + return new DERSequence(this.crls); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlOcspRef.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlOcspRef.java new file mode 100644 index 000000000..6d57769fb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlOcspRef.java @@ -0,0 +1,106 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + *
+ * CrlOcspRef ::= SEQUENCE { + * crlids [0] CRLListID OPTIONAL, + * ocspids [1] OcspListID OPTIONAL, + * otherRev [2] OtherRevRefs OPTIONAL + * } + *+ */ +public class CrlOcspRef + extends ASN1Object +{ + + private CrlListID crlids; + private OcspListID ocspids; + private OtherRevRefs otherRev; + + public static CrlOcspRef getInstance(Object obj) + { + if (obj instanceof CrlOcspRef) + { + return (CrlOcspRef)obj; + } + else if (obj != null) + { + return new CrlOcspRef(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CrlOcspRef(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + while (e.hasMoreElements()) + { + DERTaggedObject o = (DERTaggedObject)e.nextElement(); + switch (o.getTagNo()) + { + case 0: + this.crlids = CrlListID.getInstance(o.getObject()); + break; + case 1: + this.ocspids = OcspListID.getInstance(o.getObject()); + break; + case 2: + this.otherRev = OtherRevRefs.getInstance(o.getObject()); + break; + default: + throw new IllegalArgumentException("illegal tag"); + } + } + } + + public CrlOcspRef(CrlListID crlids, OcspListID ocspids, + OtherRevRefs otherRev) + { + this.crlids = crlids; + this.ocspids = ocspids; + this.otherRev = otherRev; + } + + public CrlListID getCrlids() + { + return this.crlids; + } + + public OcspListID getOcspids() + { + return this.ocspids; + } + + public OtherRevRefs getOtherRev() + { + return this.otherRev; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + if (null != this.crlids) + { + v.add(new DERTaggedObject(true, 0, this.crlids.toASN1Primitive())); + } + if (null != this.ocspids) + { + v.add(new DERTaggedObject(true, 1, this.ocspids.toASN1Primitive())); + } + if (null != this.otherRev) + { + v.add(new DERTaggedObject(true, 2, this.otherRev.toASN1Primitive())); + } + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlValidatedID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlValidatedID.java new file mode 100644 index 000000000..32f12284f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/CrlValidatedID.java @@ -0,0 +1,82 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
+ * CrlValidatedID ::= SEQUENCE { + * crlHash OtherHash, + * crlIdentifier CrlIdentifier OPTIONAL } + *+ */ +public class CrlValidatedID + extends ASN1Object +{ + + private OtherHash crlHash; + private CrlIdentifier crlIdentifier; + + public static CrlValidatedID getInstance(Object obj) + { + if (obj instanceof CrlValidatedID) + { + return (CrlValidatedID)obj; + } + else if (obj != null) + { + return new CrlValidatedID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CrlValidatedID(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.crlHash = OtherHash.getInstance(seq.getObjectAt(0)); + if (seq.size() > 1) + { + this.crlIdentifier = CrlIdentifier.getInstance(seq.getObjectAt(1)); + } + } + + public CrlValidatedID(OtherHash crlHash) + { + this(crlHash, null); + } + + public CrlValidatedID(OtherHash crlHash, CrlIdentifier crlIdentifier) + { + this.crlHash = crlHash; + this.crlIdentifier = crlIdentifier; + } + + public OtherHash getCrlHash() + { + return this.crlHash; + } + + public CrlIdentifier getCrlIdentifier() + { + return this.crlIdentifier; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.crlHash.toASN1Primitive()); + if (null != this.crlIdentifier) + { + v.add(this.crlIdentifier.toASN1Primitive()); + } + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/ESFAttributes.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/ESFAttributes.java new file mode 100644 index 000000000..2d7d2e87a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/ESFAttributes.java @@ -0,0 +1,22 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +public interface ESFAttributes +{ + public static final ASN1ObjectIdentifier sigPolicyId = PKCSObjectIdentifiers.id_aa_ets_sigPolicyId; + public static final ASN1ObjectIdentifier commitmentType = PKCSObjectIdentifiers.id_aa_ets_commitmentType; + public static final ASN1ObjectIdentifier signerLocation = PKCSObjectIdentifiers.id_aa_ets_signerLocation; + public static final ASN1ObjectIdentifier signerAttr = PKCSObjectIdentifiers.id_aa_ets_signerAttr; + public static final ASN1ObjectIdentifier otherSigCert = PKCSObjectIdentifiers.id_aa_ets_otherSigCert; + public static final ASN1ObjectIdentifier contentTimestamp = PKCSObjectIdentifiers.id_aa_ets_contentTimestamp; + public static final ASN1ObjectIdentifier certificateRefs = PKCSObjectIdentifiers.id_aa_ets_certificateRefs; + public static final ASN1ObjectIdentifier revocationRefs = PKCSObjectIdentifiers.id_aa_ets_revocationRefs; + public static final ASN1ObjectIdentifier certValues = PKCSObjectIdentifiers.id_aa_ets_certValues; + public static final ASN1ObjectIdentifier revocationValues = PKCSObjectIdentifiers.id_aa_ets_revocationValues; + public static final ASN1ObjectIdentifier escTimeStamp = PKCSObjectIdentifiers.id_aa_ets_escTimeStamp; + public static final ASN1ObjectIdentifier certCRLTimestamp = PKCSObjectIdentifiers.id_aa_ets_certCRLTimestamp; + public static final ASN1ObjectIdentifier archiveTimestamp = PKCSObjectIdentifiers.id_aa_ets_archiveTimestamp; + public static final ASN1ObjectIdentifier archiveTimestampV2 = PKCSObjectIdentifiers.id_aa.branch("48"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspIdentifier.java new file mode 100644 index 000000000..809892494 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspIdentifier.java @@ -0,0 +1,73 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.ocsp.ResponderID; + +/** + *
+ * OcspIdentifier ::= SEQUENCE { + * ocspResponderID ResponderID, -- As in OCSP response data + * producedAt GeneralizedTime -- As in OCSP response data + * } + *+ */ +public class OcspIdentifier + extends ASN1Object +{ + private ResponderID ocspResponderID; + private ASN1GeneralizedTime producedAt; + + public static OcspIdentifier getInstance(Object obj) + { + if (obj instanceof OcspIdentifier) + { + return (OcspIdentifier)obj; + } + else if (obj != null) + { + return new OcspIdentifier(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private OcspIdentifier(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.ocspResponderID = ResponderID.getInstance(seq.getObjectAt(0)); + this.producedAt = (ASN1GeneralizedTime)seq.getObjectAt(1); + } + + public OcspIdentifier(ResponderID ocspResponderID, ASN1GeneralizedTime producedAt) + { + this.ocspResponderID = ocspResponderID; + this.producedAt = producedAt; + } + + public ResponderID getOcspResponderID() + { + return this.ocspResponderID; + } + + public ASN1GeneralizedTime getProducedAt() + { + return this.producedAt; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.ocspResponderID); + v.add(this.producedAt); + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspListID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspListID.java new file mode 100644 index 000000000..d0e2a5bad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspListID.java @@ -0,0 +1,72 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
+ * OcspListID ::= SEQUENCE { + * ocspResponses SEQUENCE OF OcspResponsesID + * } + *+ */ +public class OcspListID + extends ASN1Object +{ + private ASN1Sequence ocspResponses; + + public static OcspListID getInstance(Object obj) + { + if (obj instanceof OcspListID) + { + return (OcspListID)obj; + } + else if (obj != null) + { + return new OcspListID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private OcspListID(ASN1Sequence seq) + { + if (seq.size() != 1) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.ocspResponses = (ASN1Sequence)seq.getObjectAt(0); + Enumeration e = this.ocspResponses.getObjects(); + while (e.hasMoreElements()) + { + OcspResponsesID.getInstance(e.nextElement()); + } + } + + public OcspListID(OcspResponsesID[] ocspResponses) + { + this.ocspResponses = new DERSequence(ocspResponses); + } + + public OcspResponsesID[] getOcspResponses() + { + OcspResponsesID[] result = new OcspResponsesID[this.ocspResponses + .size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = OcspResponsesID.getInstance(this.ocspResponses + .getObjectAt(idx)); + } + return result; + } + + public ASN1Primitive toASN1Primitive() + { + return new DERSequence(this.ocspResponses); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspResponsesID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspResponsesID.java new file mode 100644 index 000000000..4dc0be1e0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OcspResponsesID.java @@ -0,0 +1,83 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
+ * OcspResponsesID ::= SEQUENCE { + * ocspIdentifier OcspIdentifier, + * ocspRepHash OtherHash OPTIONAL + * } + *+ */ +public class OcspResponsesID + extends ASN1Object +{ + + private OcspIdentifier ocspIdentifier; + private OtherHash ocspRepHash; + + public static OcspResponsesID getInstance(Object obj) + { + if (obj instanceof OcspResponsesID) + { + return (OcspResponsesID)obj; + } + else if (obj != null) + { + return new OcspResponsesID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private OcspResponsesID(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.ocspIdentifier = OcspIdentifier.getInstance(seq.getObjectAt(0)); + if (seq.size() > 1) + { + this.ocspRepHash = OtherHash.getInstance(seq.getObjectAt(1)); + } + } + + public OcspResponsesID(OcspIdentifier ocspIdentifier) + { + this(ocspIdentifier, null); + } + + public OcspResponsesID(OcspIdentifier ocspIdentifier, OtherHash ocspRepHash) + { + this.ocspIdentifier = ocspIdentifier; + this.ocspRepHash = ocspRepHash; + } + + public OcspIdentifier getOcspIdentifier() + { + return this.ocspIdentifier; + } + + public OtherHash getOcspRepHash() + { + return this.ocspRepHash; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.ocspIdentifier); + if (null != this.ocspRepHash) + { + v.add(this.ocspRepHash); + } + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherHash.java new file mode 100644 index 000000000..2b8a355e6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherHash.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + *
+ * OtherHash ::= CHOICE { + * sha1Hash OtherHashValue, -- This contains a SHA-1 hash + * otherHash OtherHashAlgAndValue + * } + *+ */ +public class OtherHash + extends ASN1Object + implements ASN1Choice +{ + + private ASN1OctetString sha1Hash; + private OtherHashAlgAndValue otherHash; + + public static OtherHash getInstance(Object obj) + { + if (obj instanceof OtherHash) + { + return (OtherHash)obj; + } + if (obj instanceof ASN1OctetString) + { + return new OtherHash((ASN1OctetString)obj); + } + return new OtherHash(OtherHashAlgAndValue.getInstance(obj)); + } + + private OtherHash(ASN1OctetString sha1Hash) + { + this.sha1Hash = sha1Hash; + } + + public OtherHash(OtherHashAlgAndValue otherHash) + { + this.otherHash = otherHash; + } + + public OtherHash(byte[] sha1Hash) + { + this.sha1Hash = new DEROctetString(sha1Hash); + } + + public AlgorithmIdentifier getHashAlgorithm() + { + if (null == this.otherHash) + { + return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1); + } + return this.otherHash.getHashAlgorithm(); + } + + public byte[] getHashValue() + { + if (null == this.otherHash) + { + return this.sha1Hash.getOctets(); + } + return this.otherHash.getHashValue().getOctets(); + } + + public ASN1Primitive toASN1Primitive() + { + if (null == this.otherHash) + { + return this.sha1Hash; + } + return this.otherHash.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherHashAlgAndValue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherHashAlgAndValue.java new file mode 100644 index 000000000..6c445bf48 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherHashAlgAndValue.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class OtherHashAlgAndValue + extends ASN1Object +{ + private AlgorithmIdentifier hashAlgorithm; + private ASN1OctetString hashValue; + + + public static OtherHashAlgAndValue getInstance( + Object obj) + { + if (obj instanceof OtherHashAlgAndValue) + { + return (OtherHashAlgAndValue) obj; + } + else if (obj != null) + { + return new OtherHashAlgAndValue(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private OtherHashAlgAndValue( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + hashValue = ASN1OctetString.getInstance(seq.getObjectAt(1)); + } + + public OtherHashAlgAndValue( + AlgorithmIdentifier hashAlgorithm, + ASN1OctetString hashValue) + { + this.hashAlgorithm = hashAlgorithm; + this.hashValue = hashValue; + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public ASN1OctetString getHashValue() + { + return hashValue; + } + + /** + *
+ * OtherHashAlgAndValue ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * hashValue OtherHashValue } + * + * OtherHashValue ::= OCTET STRING + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(hashAlgorithm); + v.add(hashValue); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherRevRefs.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherRevRefs.java new file mode 100644 index 000000000..82d449d08 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherRevRefs.java @@ -0,0 +1,87 @@ +package org.spongycastle.asn1.esf; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
+ * OtherRevRefs ::= SEQUENCE { + * otherRevRefType OtherRevRefType, + * otherRevRefs ANY DEFINED BY otherRevRefType + * } + * + * OtherRevRefType ::= OBJECT IDENTIFIER + *+ */ +public class OtherRevRefs + extends ASN1Object +{ + + private ASN1ObjectIdentifier otherRevRefType; + private ASN1Encodable otherRevRefs; + + public static OtherRevRefs getInstance(Object obj) + { + if (obj instanceof OtherRevRefs) + { + return (OtherRevRefs)obj; + } + else if (obj != null) + { + return new OtherRevRefs(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private OtherRevRefs(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.otherRevRefType = new ASN1ObjectIdentifier(((ASN1ObjectIdentifier)seq.getObjectAt(0)).getId()); + try + { + this.otherRevRefs = ASN1Primitive.fromByteArray(seq.getObjectAt(1) + .toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + catch (IOException e) + { + throw new IllegalStateException(); + } + } + + public OtherRevRefs(ASN1ObjectIdentifier otherRevRefType, ASN1Encodable otherRevRefs) + { + this.otherRevRefType = otherRevRefType; + this.otherRevRefs = otherRevRefs; + } + + public ASN1ObjectIdentifier getOtherRevRefType() + { + return this.otherRevRefType; + } + + public ASN1Encodable getOtherRevRefs() + { + return this.otherRevRefs; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.otherRevRefType); + v.add(this.otherRevRefs); + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherRevVals.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherRevVals.java new file mode 100644 index 000000000..bd26799e1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/OtherRevVals.java @@ -0,0 +1,89 @@ +package org.spongycastle.asn1.esf; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
+ * OtherRevVals ::= SEQUENCE { + * otherRevValType OtherRevValType, + * otherRevVals ANY DEFINED BY OtherRevValType + * } + * + * OtherRevValType ::= OBJECT IDENTIFIER + *+ */ +public class OtherRevVals + extends ASN1Object +{ + + private ASN1ObjectIdentifier otherRevValType; + + private ASN1Encodable otherRevVals; + + public static OtherRevVals getInstance(Object obj) + { + if (obj instanceof OtherRevVals) + { + return (OtherRevVals)obj; + } + if (obj != null) + { + return new OtherRevVals(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private OtherRevVals(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.otherRevValType = (ASN1ObjectIdentifier)seq.getObjectAt(0); + try + { + this.otherRevVals = ASN1Primitive.fromByteArray(seq.getObjectAt(1) + .toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + catch (IOException e) + { + throw new IllegalStateException(); + } + } + + public OtherRevVals(ASN1ObjectIdentifier otherRevValType, + ASN1Encodable otherRevVals) + { + this.otherRevValType = otherRevValType; + this.otherRevVals = otherRevVals; + } + + public ASN1ObjectIdentifier getOtherRevValType() + { + return this.otherRevValType; + } + + public ASN1Encodable getOtherRevVals() + { + return this.otherRevVals; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.otherRevValType); + v.add(this.otherRevVals); + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/RevocationValues.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/RevocationValues.java new file mode 100644 index 000000000..87e0217df --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/RevocationValues.java @@ -0,0 +1,151 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.ocsp.BasicOCSPResponse; +import org.spongycastle.asn1.x509.CertificateList; + +/** + *
+ * RevocationValues ::= SEQUENCE { + * crlVals [0] SEQUENCE OF CertificateList OPTIONAL, + * ocspVals [1] SEQUENCE OF BasicOCSPResponse OPTIONAL, + * otherRevVals [2] OtherRevVals OPTIONAL} + *+ */ +public class RevocationValues + extends ASN1Object +{ + + private ASN1Sequence crlVals; + private ASN1Sequence ocspVals; + private OtherRevVals otherRevVals; + + public static RevocationValues getInstance(Object obj) + { + if (obj instanceof RevocationValues) + { + return (RevocationValues)obj; + } + else if (obj != null) + { + return new RevocationValues(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private RevocationValues(ASN1Sequence seq) + { + if (seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + Enumeration e = seq.getObjects(); + while (e.hasMoreElements()) + { + DERTaggedObject o = (DERTaggedObject)e.nextElement(); + switch (o.getTagNo()) + { + case 0: + ASN1Sequence crlValsSeq = (ASN1Sequence)o.getObject(); + Enumeration crlValsEnum = crlValsSeq.getObjects(); + while (crlValsEnum.hasMoreElements()) + { + CertificateList.getInstance(crlValsEnum.nextElement()); + } + this.crlVals = crlValsSeq; + break; + case 1: + ASN1Sequence ocspValsSeq = (ASN1Sequence)o.getObject(); + Enumeration ocspValsEnum = ocspValsSeq.getObjects(); + while (ocspValsEnum.hasMoreElements()) + { + BasicOCSPResponse.getInstance(ocspValsEnum.nextElement()); + } + this.ocspVals = ocspValsSeq; + break; + case 2: + this.otherRevVals = OtherRevVals.getInstance(o.getObject()); + break; + default: + throw new IllegalArgumentException("invalid tag: " + + o.getTagNo()); + } + } + } + + public RevocationValues(CertificateList[] crlVals, + BasicOCSPResponse[] ocspVals, OtherRevVals otherRevVals) + { + if (null != crlVals) + { + this.crlVals = new DERSequence(crlVals); + } + if (null != ocspVals) + { + this.ocspVals = new DERSequence(ocspVals); + } + this.otherRevVals = otherRevVals; + } + + public CertificateList[] getCrlVals() + { + if (null == this.crlVals) + { + return new CertificateList[0]; + } + CertificateList[] result = new CertificateList[this.crlVals.size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = CertificateList.getInstance(this.crlVals + .getObjectAt(idx)); + } + return result; + } + + public BasicOCSPResponse[] getOcspVals() + { + if (null == this.ocspVals) + { + return new BasicOCSPResponse[0]; + } + BasicOCSPResponse[] result = new BasicOCSPResponse[this.ocspVals.size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = BasicOCSPResponse.getInstance(this.ocspVals + .getObjectAt(idx)); + } + return result; + } + + public OtherRevVals getOtherRevVals() + { + return this.otherRevVals; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + if (null != this.crlVals) + { + v.add(new DERTaggedObject(true, 0, this.crlVals)); + } + if (null != this.ocspVals) + { + v.add(new DERTaggedObject(true, 1, this.ocspVals)); + } + if (null != this.otherRevVals) + { + v.add(new DERTaggedObject(true, 2, this.otherRevVals.toASN1Primitive())); + } + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SPUserNotice.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SPUserNotice.java new file mode 100644 index 000000000..759227fde --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SPUserNotice.java @@ -0,0 +1,99 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.DisplayText; +import org.spongycastle.asn1.x509.NoticeReference; + +public class SPUserNotice + extends ASN1Object +{ + private NoticeReference noticeRef; + private DisplayText explicitText; + + public static SPUserNotice getInstance( + Object obj) + { + if (obj instanceof SPUserNotice) + { + return (SPUserNotice)obj; + } + else if (obj != null) + { + return new SPUserNotice(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private SPUserNotice( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + while (e.hasMoreElements()) + { + ASN1Encodable object = (ASN1Encodable)e.nextElement(); + if (object instanceof DisplayText || object instanceof ASN1String) + { + explicitText = DisplayText.getInstance(object); + } + else if (object instanceof NoticeReference || object instanceof ASN1Sequence) + { + noticeRef = NoticeReference.getInstance(object); + } + else + { + throw new IllegalArgumentException("Invalid element in 'SPUserNotice': " + object.getClass().getName()); + } + } + } + + public SPUserNotice( + NoticeReference noticeRef, + DisplayText explicitText) + { + this.noticeRef = noticeRef; + this.explicitText = explicitText; + } + + public NoticeReference getNoticeRef() + { + return noticeRef; + } + + public DisplayText getExplicitText() + { + return explicitText; + } + + /** + *
+ * SPUserNotice ::= SEQUENCE { + * noticeRef NoticeReference OPTIONAL, + * explicitText DisplayText OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (noticeRef != null) + { + v.add(noticeRef); + } + + if (explicitText != null) + { + v.add(explicitText); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SPuri.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SPuri.java new file mode 100644 index 000000000..1de952275 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SPuri.java @@ -0,0 +1,45 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERIA5String; + +public class SPuri +{ + private DERIA5String uri; + + public static SPuri getInstance( + Object obj) + { + if (obj instanceof SPuri) + { + return (SPuri) obj; + } + else if (obj instanceof DERIA5String) + { + return new SPuri(DERIA5String.getInstance(obj)); + } + + return null; + } + + public SPuri( + DERIA5String uri) + { + this.uri = uri; + } + + public DERIA5String getUri() + { + return uri; + } + + /** + *
+ * SPuri ::= IA5String + *+ */ + public ASN1Primitive toASN1Primitive() + { + return uri.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SigPolicyQualifierInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SigPolicyQualifierInfo.java new file mode 100644 index 000000000..3e88ab4ac --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SigPolicyQualifierInfo.java @@ -0,0 +1,75 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class SigPolicyQualifierInfo + extends ASN1Object +{ + private ASN1ObjectIdentifier sigPolicyQualifierId; + private ASN1Encodable sigQualifier; + + public SigPolicyQualifierInfo( + ASN1ObjectIdentifier sigPolicyQualifierId, + ASN1Encodable sigQualifier) + { + this.sigPolicyQualifierId = sigPolicyQualifierId; + this.sigQualifier = sigQualifier; + } + + private SigPolicyQualifierInfo( + ASN1Sequence seq) + { + sigPolicyQualifierId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + sigQualifier = seq.getObjectAt(1); + } + + public static SigPolicyQualifierInfo getInstance( + Object obj) + { + if (obj instanceof SigPolicyQualifierInfo) + { + return (SigPolicyQualifierInfo) obj; + } + else if (obj != null) + { + return new SigPolicyQualifierInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1ObjectIdentifier getSigPolicyQualifierId() + { + return new ASN1ObjectIdentifier(sigPolicyQualifierId.getId()); + } + + public ASN1Encodable getSigQualifier() + { + return sigQualifier; + } + + /** + *
+ * SigPolicyQualifierInfo ::= SEQUENCE { + * sigPolicyQualifierId SigPolicyQualifierId, + * sigQualifier ANY DEFINED BY sigPolicyQualifierId } + * + * SigPolicyQualifierId ::= OBJECT IDENTIFIER + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(sigPolicyQualifierId); + v.add(sigQualifier); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SigPolicyQualifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SigPolicyQualifiers.java new file mode 100644 index 000000000..dd53b0aa1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SigPolicyQualifiers.java @@ -0,0 +1,77 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class SigPolicyQualifiers + extends ASN1Object +{ + ASN1Sequence qualifiers; + + public static SigPolicyQualifiers getInstance( + Object obj) + { + if (obj instanceof SigPolicyQualifiers) + { + return (SigPolicyQualifiers) obj; + } + else if (obj instanceof ASN1Sequence) + { + return new SigPolicyQualifiers(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private SigPolicyQualifiers( + ASN1Sequence seq) + { + qualifiers = seq; + } + + public SigPolicyQualifiers( + SigPolicyQualifierInfo[] qualifierInfos) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i=0; i < qualifierInfos.length; i++) + { + v.add(qualifierInfos[i]); + } + qualifiers = new DERSequence(v); + } + + /** + * Return the number of qualifier info elements present. + * + * @return number of elements present. + */ + public int size() + { + return qualifiers.size(); + } + + /** + * Return the SigPolicyQualifierInfo at index i. + * + * @param i index of the info of interest + * @return the info at index i. + */ + public SigPolicyQualifierInfo getInfoAt( + int i) + { + return SigPolicyQualifierInfo.getInstance(qualifiers.getObjectAt(i)); + } + + /** + *
+ * SigPolicyQualifiers ::= SEQUENCE SIZE (1..MAX) OF SigPolicyQualifierInfo + *+ */ + public ASN1Primitive toASN1Primitive() + { + return qualifiers; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignaturePolicyId.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignaturePolicyId.java new file mode 100644 index 000000000..9df4f3ed1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignaturePolicyId.java @@ -0,0 +1,103 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class SignaturePolicyId + extends ASN1Object +{ + private ASN1ObjectIdentifier sigPolicyId; + private OtherHashAlgAndValue sigPolicyHash; + private SigPolicyQualifiers sigPolicyQualifiers; + + + public static SignaturePolicyId getInstance( + Object obj) + { + if (obj instanceof SignaturePolicyId) + { + return (SignaturePolicyId)obj; + } + else if (obj != null) + { + return new SignaturePolicyId(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private SignaturePolicyId( + ASN1Sequence seq) + { + if (seq.size() != 2 && seq.size() != 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + sigPolicyId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + sigPolicyHash = OtherHashAlgAndValue.getInstance(seq.getObjectAt(1)); + + if (seq.size() == 3) + { + sigPolicyQualifiers = SigPolicyQualifiers.getInstance(seq.getObjectAt(2)); + } + } + + public SignaturePolicyId( + ASN1ObjectIdentifier sigPolicyIdentifier, + OtherHashAlgAndValue sigPolicyHash) + { + this(sigPolicyIdentifier, sigPolicyHash, null); + } + + public SignaturePolicyId( + ASN1ObjectIdentifier sigPolicyId, + OtherHashAlgAndValue sigPolicyHash, + SigPolicyQualifiers sigPolicyQualifiers) + { + this.sigPolicyId = sigPolicyId; + this.sigPolicyHash = sigPolicyHash; + this.sigPolicyQualifiers = sigPolicyQualifiers; + } + + public ASN1ObjectIdentifier getSigPolicyId() + { + return new ASN1ObjectIdentifier(sigPolicyId.getId()); + } + + public OtherHashAlgAndValue getSigPolicyHash() + { + return sigPolicyHash; + } + + public SigPolicyQualifiers getSigPolicyQualifiers() + { + return sigPolicyQualifiers; + } + + /** + *
+ * SignaturePolicyId ::= SEQUENCE { + * sigPolicyId SigPolicyId, + * sigPolicyHash SigPolicyHash, + * sigPolicyQualifiers SEQUENCE SIZE (1..MAX) OF SigPolicyQualifierInfo OPTIONAL} + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(sigPolicyId); + v.add(sigPolicyHash); + if (sigPolicyQualifiers != null) + { + v.add(sigPolicyQualifiers); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignaturePolicyIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignaturePolicyIdentifier.java new file mode 100644 index 000000000..ace0860ce --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignaturePolicyIdentifier.java @@ -0,0 +1,76 @@ +package org.spongycastle.asn1.esf; + +import org.spongycastle.asn1.ASN1Null; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.BERTags; +import org.spongycastle.asn1.DERNull; + +public class SignaturePolicyIdentifier + extends ASN1Object +{ + private SignaturePolicyId signaturePolicyId; + private boolean isSignaturePolicyImplied; + + public static SignaturePolicyIdentifier getInstance( + Object obj) + { + if (obj instanceof SignaturePolicyIdentifier) + { + return (SignaturePolicyIdentifier)obj; + } + else if (obj instanceof ASN1Null || hasEncodedTagValue(obj, BERTags.NULL)) + { + return new SignaturePolicyIdentifier(); + } + else if (obj != null) + { + return new SignaturePolicyIdentifier(SignaturePolicyId.getInstance(obj)); + } + + return null; + } + + public SignaturePolicyIdentifier() + { + this.isSignaturePolicyImplied = true; + } + + public SignaturePolicyIdentifier( + SignaturePolicyId signaturePolicyId) + { + this.signaturePolicyId = signaturePolicyId; + this.isSignaturePolicyImplied = false; + } + + public SignaturePolicyId getSignaturePolicyId() + { + return signaturePolicyId; + } + + public boolean isSignaturePolicyImplied() + { + return isSignaturePolicyImplied; + } + + /** + *
+ * SignaturePolicyIdentifier ::= CHOICE{ + * SignaturePolicyId SignaturePolicyId, + * SignaturePolicyImplied SignaturePolicyImplied } + * + * SignaturePolicyImplied ::= NULL + *+ */ + public ASN1Primitive toASN1Primitive() + { + if (isSignaturePolicyImplied) + { + return DERNull.INSTANCE; + } + else + { + return signaturePolicyId.toASN1Primitive(); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignerAttribute.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignerAttribute.java new file mode 100644 index 000000000..664db344a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignerAttribute.java @@ -0,0 +1,123 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Attribute; +import org.spongycastle.asn1.x509.AttributeCertificate; + + +public class SignerAttribute + extends ASN1Object +{ + private Object[] values; + + public static SignerAttribute getInstance( + Object o) + { + if (o instanceof SignerAttribute) + { + return (SignerAttribute) o; + } + else if (o != null) + { + return new SignerAttribute(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private SignerAttribute( + ASN1Sequence seq) + { + int index = 0; + values = new Object[seq.size()]; + + for (Enumeration e = seq.getObjects(); e.hasMoreElements();) + { + ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(e.nextElement()); + + if (taggedObject.getTagNo() == 0) + { + ASN1Sequence attrs = ASN1Sequence.getInstance(taggedObject, true); + Attribute[] attributes = new Attribute[attrs.size()]; + + for (int i = 0; i != attributes.length; i++) + { + attributes[i] = Attribute.getInstance(attrs.getObjectAt(i)); + } + values[index] = attributes; + } + else if (taggedObject.getTagNo() == 1) + { + values[index] = AttributeCertificate.getInstance(ASN1Sequence.getInstance(taggedObject, true)); + } + else + { + throw new IllegalArgumentException("illegal tag: " + taggedObject.getTagNo()); + } + index++; + } + } + + public SignerAttribute( + Attribute[] claimedAttributes) + { + this.values = new Object[1]; + this.values[0] = claimedAttributes; + } + + public SignerAttribute( + AttributeCertificate certifiedAttributes) + { + this.values = new Object[1]; + this.values[0] = certifiedAttributes; + } + + /** + * Return the sequence of choices - the array elements will either be of + * type Attribute[] or AttributeCertificate depending on what tag was used. + * + * @return array of choices. + */ + public Object[] getValues() + { + return values; + } + + /** + * + *
+ * SignerAttribute ::= SEQUENCE OF CHOICE { + * claimedAttributes [0] ClaimedAttributes, + * certifiedAttributes [1] CertifiedAttributes } + * + * ClaimedAttributes ::= SEQUENCE OF Attribute + * CertifiedAttributes ::= AttributeCertificate -- as defined in RFC 3281: see clause 4.1. + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != values.length; i++) + { + if (values[i] instanceof Attribute[]) + { + v.add(new DERTaggedObject(0, new DERSequence((Attribute[])values[i]))); + } + else + { + v.add(new DERTaggedObject(1, (AttributeCertificate)values[i])); + } + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignerLocation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignerLocation.java new file mode 100644 index 000000000..ebf693e0d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/esf/SignerLocation.java @@ -0,0 +1,162 @@ +package org.spongycastle.asn1.esf; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Signer-Location attribute (RFC3126). + * + *
+ * SignerLocation ::= SEQUENCE { + * countryName [0] DirectoryString OPTIONAL, + * localityName [1] DirectoryString OPTIONAL, + * postalAddress [2] PostalAddress OPTIONAL } + * + * PostalAddress ::= SEQUENCE SIZE(1..6) OF DirectoryString + *+ */ +public class SignerLocation + extends ASN1Object +{ + private DERUTF8String countryName; + private DERUTF8String localityName; + private ASN1Sequence postalAddress; + + private SignerLocation( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + DERTaggedObject o = (DERTaggedObject)e.nextElement(); + + switch (o.getTagNo()) + { + case 0: + DirectoryString countryNameDirectoryString = DirectoryString.getInstance(o, true); + this.countryName = new DERUTF8String(countryNameDirectoryString.getString()); + break; + case 1: + DirectoryString localityNameDirectoryString = DirectoryString.getInstance(o, true); + this.localityName = new DERUTF8String(localityNameDirectoryString.getString()); + break; + case 2: + if (o.isExplicit()) + { + this.postalAddress = ASN1Sequence.getInstance(o, true); + } + else // handle erroneous implicitly tagged sequences + { + this.postalAddress = ASN1Sequence.getInstance(o, false); + } + if (postalAddress != null && postalAddress.size() > 6) + { + throw new IllegalArgumentException("postal address must contain less than 6 strings"); + } + break; + default: + throw new IllegalArgumentException("illegal tag"); + } + } + } + + public SignerLocation( + DERUTF8String countryName, + DERUTF8String localityName, + ASN1Sequence postalAddress) + { + if (postalAddress != null && postalAddress.size() > 6) + { + throw new IllegalArgumentException("postal address must contain less than 6 strings"); + } + + if (countryName != null) + { + this.countryName = DERUTF8String.getInstance(countryName.toASN1Primitive()); + } + + if (localityName != null) + { + this.localityName = DERUTF8String.getInstance(localityName.toASN1Primitive()); + } + + if (postalAddress != null) + { + this.postalAddress = ASN1Sequence.getInstance(postalAddress.toASN1Primitive()); + } + } + + public static SignerLocation getInstance( + Object obj) + { + if (obj == null || obj instanceof SignerLocation) + { + return (SignerLocation)obj; + } + + return new SignerLocation(ASN1Sequence.getInstance(obj)); + } + + public DERUTF8String getCountryName() + { + return countryName; + } + + public DERUTF8String getLocalityName() + { + return localityName; + } + + public ASN1Sequence getPostalAddress() + { + return postalAddress; + } + + /** + *
+ * SignerLocation ::= SEQUENCE { + * countryName [0] DirectoryString OPTIONAL, + * localityName [1] DirectoryString OPTIONAL, + * postalAddress [2] PostalAddress OPTIONAL } + * + * PostalAddress ::= SEQUENCE SIZE(1..6) OF DirectoryString + * + * DirectoryString ::= CHOICE { + * teletexString TeletexString (SIZE (1..MAX)), + * printableString PrintableString (SIZE (1..MAX)), + * universalString UniversalString (SIZE (1..MAX)), + * utf8String UTF8String (SIZE (1.. MAX)), + * bmpString BMPString (SIZE (1..MAX)) } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (countryName != null) + { + v.add(new DERTaggedObject(true, 0, countryName)); + } + + if (localityName != null) + { + v.add(new DERTaggedObject(true, 1, localityName)); + } + + if (postalAddress != null) + { + v.add(new DERTaggedObject(true, 2, postalAddress)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ContentHints.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ContentHints.java new file mode 100644 index 000000000..d544c937b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ContentHints.java @@ -0,0 +1,114 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERUTF8String; + +public class ContentHints + extends ASN1Object +{ + private DERUTF8String contentDescription; + private ASN1ObjectIdentifier contentType; + + public static ContentHints getInstance(Object o) + { + if (o instanceof ContentHints) + { + return (ContentHints)o; + } + else if (o != null) + { + return new ContentHints(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * constructor + */ + private ContentHints(ASN1Sequence seq) + { + ASN1Encodable field = seq.getObjectAt(0); + if (field.toASN1Primitive() instanceof DERUTF8String) + { + contentDescription = DERUTF8String.getInstance(field); + contentType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(1)); + } + else + { + contentType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + } + } + + /** + * @deprecated use ASN1ObjectIdentifier + */ + public ContentHints( + DERObjectIdentifier contentType) + { + this(new ASN1ObjectIdentifier(contentType.getId())); + } + + /** + * @deprecated use ASN1ObjectIdentifier + */ + public ContentHints( + DERObjectIdentifier contentType, + DERUTF8String contentDescription) + { + this(new ASN1ObjectIdentifier(contentType.getId()), contentDescription); + } + + public ContentHints( + ASN1ObjectIdentifier contentType) + { + this.contentType = contentType; + this.contentDescription = null; + } + + public ContentHints( + ASN1ObjectIdentifier contentType, + DERUTF8String contentDescription) + { + this.contentType = contentType; + this.contentDescription = contentDescription; + } + + public ASN1ObjectIdentifier getContentType() + { + return contentType; + } + + public DERUTF8String getContentDescription() + { + return contentDescription; + } + + /** + *
+ * ContentHints ::= SEQUENCE { + * contentDescription UTF8String (SIZE (1..MAX)) OPTIONAL, + * contentType ContentType } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (contentDescription != null) + { + v.add(contentDescription); + } + + v.add(contentType); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ContentIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ContentIdentifier.java new file mode 100644 index 000000000..7082a43a1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ContentIdentifier.java @@ -0,0 +1,63 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; + +public class ContentIdentifier + extends ASN1Object +{ + ASN1OctetString value; + + public static ContentIdentifier getInstance(Object o) + { + if (o instanceof ContentIdentifier) + { + return (ContentIdentifier) o; + } + else if (o != null) + { + return new ContentIdentifier(ASN1OctetString.getInstance(o)); + } + + return null; + } + + /** + * Create from OCTET STRING whose octets represent the identifier. + */ + private ContentIdentifier( + ASN1OctetString value) + { + this.value = value; + } + + /** + * Create from byte array representing the identifier. + */ + public ContentIdentifier( + byte[] value) + { + this(new DEROctetString(value)); + } + + public ASN1OctetString getValue() + { + return value; + } + + /** + * The definition of ContentIdentifier is + *
+ * ContentIdentifier ::= OCTET STRING + *+ * id-aa-contentIdentifier OBJECT IDENTIFIER ::= { iso(1) + * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) + * smime(16) id-aa(2) 7 } + */ + public ASN1Primitive toASN1Primitive() + { + return value; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ESSCertID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ESSCertID.java new file mode 100644 index 000000000..e63b69639 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ESSCertID.java @@ -0,0 +1,95 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.IssuerSerial; + +public class ESSCertID + extends ASN1Object +{ + private ASN1OctetString certHash; + + private IssuerSerial issuerSerial; + + public static ESSCertID getInstance(Object o) + { + if (o instanceof ESSCertID) + { + return (ESSCertID)o; + } + else if (o != null) + { + return new ESSCertID(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * constructor + */ + private ESSCertID(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + certHash = ASN1OctetString.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(1)); + } + } + + public ESSCertID( + byte[] hash) + { + certHash = new DEROctetString(hash); + } + + public ESSCertID( + byte[] hash, + IssuerSerial issuerSerial) + { + this.certHash = new DEROctetString(hash); + this.issuerSerial = issuerSerial; + } + + public byte[] getCertHash() + { + return certHash.getOctets(); + } + + public IssuerSerial getIssuerSerial() + { + return issuerSerial; + } + + /** + *
+ * ESSCertID ::= SEQUENCE { + * certHash Hash, + * issuerSerial IssuerSerial OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certHash); + + if (issuerSerial != null) + { + v.add(issuerSerial); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ESSCertIDv2.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ESSCertIDv2.java new file mode 100644 index 000000000..dbafd904e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/ESSCertIDv2.java @@ -0,0 +1,155 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.IssuerSerial; + +public class ESSCertIDv2 + extends ASN1Object +{ + private AlgorithmIdentifier hashAlgorithm; + private byte[] certHash; + private IssuerSerial issuerSerial; + private static final AlgorithmIdentifier DEFAULT_ALG_ID = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256); + + public static ESSCertIDv2 getInstance( + Object o) + { + if (o instanceof ESSCertIDv2) + { + return (ESSCertIDv2) o; + } + else if (o != null) + { + return new ESSCertIDv2(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private ESSCertIDv2( + ASN1Sequence seq) + { + if (seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + int count = 0; + + if (seq.getObjectAt(0) instanceof ASN1OctetString) + { + // Default value + this.hashAlgorithm = DEFAULT_ALG_ID; + } + else + { + this.hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(count++).toASN1Primitive()); + } + + this.certHash = ASN1OctetString.getInstance(seq.getObjectAt(count++).toASN1Primitive()).getOctets(); + + if (seq.size() > count) + { + this.issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(count)); + } + } + + public ESSCertIDv2( + byte[] certHash) + { + this(null, certHash, null); + } + + public ESSCertIDv2( + AlgorithmIdentifier algId, + byte[] certHash) + { + this(algId, certHash, null); + } + + public ESSCertIDv2( + byte[] certHash, + IssuerSerial issuerSerial) + { + this(null, certHash, issuerSerial); + } + + public ESSCertIDv2( + AlgorithmIdentifier algId, + byte[] certHash, + IssuerSerial issuerSerial) + { + if (algId == null) + { + // Default value + this.hashAlgorithm = DEFAULT_ALG_ID; + } + else + { + this.hashAlgorithm = algId; + } + + this.certHash = certHash; + this.issuerSerial = issuerSerial; + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return this.hashAlgorithm; + } + + public byte[] getCertHash() + { + return certHash; + } + + public IssuerSerial getIssuerSerial() + { + return issuerSerial; + } + + /** + *
+ * ESSCertIDv2 ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier + * DEFAULT {algorithm id-sha256}, + * certHash Hash, + * issuerSerial IssuerSerial OPTIONAL + * } + * + * Hash ::= OCTET STRING + * + * IssuerSerial ::= SEQUENCE { + * issuer GeneralNames, + * serialNumber CertificateSerialNumber + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (!hashAlgorithm.equals(DEFAULT_ALG_ID)) + { + v.add(hashAlgorithm); + } + + v.add(new DEROctetString(certHash).toASN1Primitive()); + + if (issuerSerial != null) + { + v.add(issuerSerial); + } + + return new DERSequence(v); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/OtherCertID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/OtherCertID.java new file mode 100644 index 000000000..530491f76 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/OtherCertID.java @@ -0,0 +1,137 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DigestInfo; +import org.spongycastle.asn1.x509.IssuerSerial; + +public class OtherCertID + extends ASN1Object +{ + private ASN1Encodable otherCertHash; + private IssuerSerial issuerSerial; + + public static OtherCertID getInstance(Object o) + { + if (o instanceof OtherCertID) + { + return (OtherCertID) o; + } + else if (o != null) + { + return new OtherCertID(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * constructor + */ + private OtherCertID(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + if (seq.getObjectAt(0).toASN1Primitive() instanceof ASN1OctetString) + { + otherCertHash = ASN1OctetString.getInstance(seq.getObjectAt(0)); + } + else + { + otherCertHash = DigestInfo.getInstance(seq.getObjectAt(0)); + + } + + if (seq.size() > 1) + { + issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(1)); + } + } + + public OtherCertID( + AlgorithmIdentifier algId, + byte[] digest) + { + this.otherCertHash = new DigestInfo(algId, digest); + } + + public OtherCertID( + AlgorithmIdentifier algId, + byte[] digest, + IssuerSerial issuerSerial) + { + this.otherCertHash = new DigestInfo(algId, digest); + this.issuerSerial = issuerSerial; + } + + public AlgorithmIdentifier getAlgorithmHash() + { + if (otherCertHash.toASN1Primitive() instanceof ASN1OctetString) + { + // SHA-1 + return new AlgorithmIdentifier("1.3.14.3.2.26"); + } + else + { + return DigestInfo.getInstance(otherCertHash).getAlgorithmId(); + } + } + + public byte[] getCertHash() + { + if (otherCertHash.toASN1Primitive() instanceof ASN1OctetString) + { + // SHA-1 + return ((ASN1OctetString)otherCertHash.toASN1Primitive()).getOctets(); + } + else + { + return DigestInfo.getInstance(otherCertHash).getDigest(); + } + } + + public IssuerSerial getIssuerSerial() + { + return issuerSerial; + } + + /** + *
+ * OtherCertID ::= SEQUENCE { + * otherCertHash OtherHash, + * issuerSerial IssuerSerial OPTIONAL } + * + * OtherHash ::= CHOICE { + * sha1Hash OCTET STRING, + * otherHash OtherHashAlgAndValue } + * + * OtherHashAlgAndValue ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * hashValue OCTET STRING } + * + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(otherCertHash); + + if (issuerSerial != null) + { + v.add(issuerSerial); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/OtherSigningCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/OtherSigningCertificate.java new file mode 100644 index 000000000..2198d1a02 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/OtherSigningCertificate.java @@ -0,0 +1,109 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.PolicyInformation; + +public class OtherSigningCertificate + extends ASN1Object +{ + ASN1Sequence certs; + ASN1Sequence policies; + + public static OtherSigningCertificate getInstance(Object o) + { + if (o instanceof OtherSigningCertificate) + { + return (OtherSigningCertificate) o; + } + else if (o != null) + { + return new OtherSigningCertificate(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * constructeurs + */ + private OtherSigningCertificate(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + } + + public OtherSigningCertificate( + OtherCertID otherCertID) + { + certs = new DERSequence(otherCertID); + } + + public OtherCertID[] getCerts() + { + OtherCertID[] cs = new OtherCertID[certs.size()]; + + for (int i = 0; i != certs.size(); i++) + { + cs[i] = OtherCertID.getInstance(certs.getObjectAt(i)); + } + + return cs; + } + + public PolicyInformation[] getPolicies() + { + if (policies == null) + { + return null; + } + + PolicyInformation[] ps = new PolicyInformation[policies.size()]; + + for (int i = 0; i != policies.size(); i++) + { + ps[i] = PolicyInformation.getInstance(policies.getObjectAt(i)); + } + + return ps; + } + + /** + * The definition of OtherSigningCertificate is + *
+ * OtherSigningCertificate ::= SEQUENCE { + * certs SEQUENCE OF OtherCertID, + * policies SEQUENCE OF PolicyInformation OPTIONAL + * } + *+ * id-aa-ets-otherSigCert OBJECT IDENTIFIER ::= { iso(1) + * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) + * smime(16) id-aa(2) 19 } + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certs); + + if (policies != null) + { + v.add(policies); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/SigningCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/SigningCertificate.java new file mode 100644 index 000000000..87a9881bd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/SigningCertificate.java @@ -0,0 +1,109 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.PolicyInformation; + + +public class SigningCertificate + extends ASN1Object +{ + ASN1Sequence certs; + ASN1Sequence policies; + + public static SigningCertificate getInstance(Object o) + { + if (o instanceof SigningCertificate) + { + return (SigningCertificate) o; + } + else if (o != null) + { + return new SigningCertificate(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * constructeurs + */ + private SigningCertificate(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + } + + public SigningCertificate( + ESSCertID essCertID) + { + certs = new DERSequence(essCertID); + } + + public ESSCertID[] getCerts() + { + ESSCertID[] cs = new ESSCertID[certs.size()]; + + for (int i = 0; i != certs.size(); i++) + { + cs[i] = ESSCertID.getInstance(certs.getObjectAt(i)); + } + + return cs; + } + + public PolicyInformation[] getPolicies() + { + if (policies == null) + { + return null; + } + + PolicyInformation[] ps = new PolicyInformation[policies.size()]; + + for (int i = 0; i != policies.size(); i++) + { + ps[i] = PolicyInformation.getInstance(policies.getObjectAt(i)); + } + + return ps; + } + + /** + * The definition of SigningCertificate is + *
+ * SigningCertificate ::= SEQUENCE { + * certs SEQUENCE OF ESSCertID, + * policies SEQUENCE OF PolicyInformation OPTIONAL + * } + *+ * id-aa-signingCertificate OBJECT IDENTIFIER ::= { iso(1) + * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) + * smime(16) id-aa(2) 12 } + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certs); + + if (policies != null) + { + v.add(policies); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/SigningCertificateV2.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/SigningCertificateV2.java new file mode 100644 index 000000000..3af8c7318 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ess/SigningCertificateV2.java @@ -0,0 +1,136 @@ +package org.spongycastle.asn1.ess; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.PolicyInformation; + +public class SigningCertificateV2 + extends ASN1Object +{ + ASN1Sequence certs; + ASN1Sequence policies; + + public static SigningCertificateV2 getInstance( + Object o) + { + if (o == null || o instanceof SigningCertificateV2) + { + return (SigningCertificateV2) o; + } + else if (o instanceof ASN1Sequence) + { + return new SigningCertificateV2((ASN1Sequence) o); + } + + return null; + } + + private SigningCertificateV2( + ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + } + + public SigningCertificateV2( + ESSCertIDv2 cert) + { + this.certs = new DERSequence(cert); + } + + public SigningCertificateV2( + ESSCertIDv2[] certs) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i=0; i < certs.length; i++) + { + v.add(certs[i]); + } + this.certs = new DERSequence(v); + } + + public SigningCertificateV2( + ESSCertIDv2[] certs, + PolicyInformation[] policies) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i=0; i < certs.length; i++) + { + v.add(certs[i]); + } + this.certs = new DERSequence(v); + + if (policies != null) + { + v = new ASN1EncodableVector(); + for (int i=0; i < policies.length; i++) + { + v.add(policies[i]); + } + this.policies = new DERSequence(v); + } + } + + public ESSCertIDv2[] getCerts() + { + ESSCertIDv2[] certIds = new ESSCertIDv2[certs.size()]; + for (int i = 0; i != certs.size(); i++) + { + certIds[i] = ESSCertIDv2.getInstance(certs.getObjectAt(i)); + } + return certIds; + } + + public PolicyInformation[] getPolicies() + { + if (policies == null) + { + return null; + } + + PolicyInformation[] policyInformations = new PolicyInformation[policies.size()]; + for (int i = 0; i != policies.size(); i++) + { + policyInformations[i] = PolicyInformation.getInstance(policies.getObjectAt(i)); + } + return policyInformations; + } + + /** + * The definition of SigningCertificateV2 is + *
+ * SigningCertificateV2 ::= SEQUENCE { + * certs SEQUENCE OF ESSCertIDv2, + * policies SEQUENCE OF PolicyInformation OPTIONAL + * } + *+ * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::= { iso(1) + * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) + * smime(16) id-aa(2) 47 } + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certs); + + if (policies != null) + { + v.add(policies); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/gnu/GNUObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/gnu/GNUObjectIdentifiers.java new file mode 100644 index 000000000..fd3cf2407 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/gnu/GNUObjectIdentifiers.java @@ -0,0 +1,58 @@ +package org.spongycastle.asn1.gnu; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * GNU project OID collection
+ * { iso(1) identifier-organization(3) dod(6) internet(1) private(4) } == IETF defined things + */ +public interface GNUObjectIdentifiers +{ + /** 1.3.6.1.4.1.11591.1 -- used by GNU Radius */ + public static final ASN1ObjectIdentifier GNU = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius + /** 1.3.6.1.4.1.11591.2 -- used by GNU PG */ + public static final ASN1ObjectIdentifier GnuPG = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ăgypten) + /** 1.3.6.1.4.1.11591.2.1 -- notation */ + public static final ASN1ObjectIdentifier notation = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation + /** 1.3.6.1.4.1.11591.2.1.1 -- pkaAddress */ + public static final ASN1ObjectIdentifier pkaAddress = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress + /** 1.3.6.1.4.1.11591.3 -- GNU Radar */ + public static final ASN1ObjectIdentifier GnuRadar = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar + /** 1.3.6.1.4.1.11591.12 -- digestAlgorithm */ + public static final ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm + /** 1.3.6.1.4.1.11591.12.2 -- TIGER/192 */ + public static final ASN1ObjectIdentifier Tiger_192 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192 + /** 1.3.6.1.4.1.11591.13 -- encryptionAlgorithm */ + public static final ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm + /** 1.3.6.1.4.1.11591.13.2 -- Serpent */ + public static final ASN1ObjectIdentifier Serpent = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent + /** 1.3.6.1.4.1.11591.13.2.1 -- Serpent-128-ECB */ + public static final ASN1ObjectIdentifier Serpent_128_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB + /** 1.3.6.1.4.1.11591.13.2.2 -- Serpent-128-CBC */ + public static final ASN1ObjectIdentifier Serpent_128_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC + /** 1.3.6.1.4.1.11591.13.2.3 -- Serpent-128-OFB */ + public static final ASN1ObjectIdentifier Serpent_128_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB + /** 1.3.6.1.4.1.11591.13.2.4 -- Serpent-128-CFB */ + public static final ASN1ObjectIdentifier Serpent_128_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB + /** 1.3.6.1.4.1.11591.13.2.21 -- Serpent-192-ECB */ + public static final ASN1ObjectIdentifier Serpent_192_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB + /** 1.3.6.1.4.1.11591.13.2.22 -- Serpent-192-CCB */ + public static final ASN1ObjectIdentifier Serpent_192_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC + /** 1.3.6.1.4.1.11591.13.2.23 -- Serpent-192-OFB */ + public static final ASN1ObjectIdentifier Serpent_192_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB + /** 1.3.6.1.4.1.11591.13.2.24 -- Serpent-192-CFB */ + public static final ASN1ObjectIdentifier Serpent_192_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB + /** 1.3.6.1.4.1.11591.13.2.41 -- Serpent-256-ECB */ + public static final ASN1ObjectIdentifier Serpent_256_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB + /** 1.3.6.1.4.1.11591.13.2.42 -- Serpent-256-CBC */ + public static final ASN1ObjectIdentifier Serpent_256_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC + /** 1.3.6.1.4.1.11591.13.2.43 -- Serpent-256-OFB */ + public static final ASN1ObjectIdentifier Serpent_256_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB + /** 1.3.6.1.4.1.11591.13.2.44 -- Serpent-256-CFB */ + public static final ASN1ObjectIdentifier Serpent_256_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB + + /** 1.3.6.1.4.1.11591.14 -- CRC algorithms */ + public static final ASN1ObjectIdentifier CRC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms + /** 1.3.6.1.4.1.11591.14,1 -- CRC32 */ + public static final ASN1ObjectIdentifier CRC32 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32 +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/iana/IANAObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/iana/IANAObjectIdentifiers.java new file mode 100644 index 000000000..731fdfb4c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/iana/IANAObjectIdentifiers.java @@ -0,0 +1,60 @@ +package org.spongycastle.asn1.iana; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * IANA: + * { iso(1) identifier-organization(3) dod(6) internet(1) } == IETF defined things + */ +public interface IANAObjectIdentifiers +{ + + /** { iso(1) identifier-organization(3) dod(6) internet(1) } == IETF defined things */ + static final ASN1ObjectIdentifier internet = new ASN1ObjectIdentifier("1.3.6.1"); + /** 1.3.6.1.1: Internet directory: X.500 */ + static final ASN1ObjectIdentifier directory = internet.branch("1"); + /** 1.3.6.1.2: Internet management */ + static final ASN1ObjectIdentifier mgmt = internet.branch("2"); + /** 1.3.6.1.3: */ + static final ASN1ObjectIdentifier experimental = internet.branch("3"); + /** 1.3.6.1.4: */ + static final ASN1ObjectIdentifier _private = internet.branch("4"); + /** 1.3.6.1.5: Security services */ + static final ASN1ObjectIdentifier security = internet.branch("5"); + /** 1.3.6.1.6: SNMPv2 -- never really used */ + static final ASN1ObjectIdentifier SNMPv2 = internet.branch("6"); + /** 1.3.6.1.7: mail -- never really used */ + static final ASN1ObjectIdentifier mail = internet.branch("7"); + + + // id-SHA1 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) ipsec(8) isakmpOakley(1)} + // + + + /** IANA security mechanisms; 1.3.6.1.5.5 */ + static final ASN1ObjectIdentifier security_mechanisms = security.branch("5"); + /** IANA security nametypes; 1.3.6.1.5.6 */ + static final ASN1ObjectIdentifier security_nametypes = security.branch("6"); + + /** PKIX base OID: 1.3.6.1.5.6.6 */ + static final ASN1ObjectIdentifier pkix = security_mechanisms.branch("6"); + + + /** IPSEC base OID: 1.3.6.1.5.5.8 */ + static final ASN1ObjectIdentifier ipsec = security_mechanisms.branch("8"); + /** IPSEC ISAKMP-Oakley OID: 1.3.6.1.5.5.8.1 */ + static final ASN1ObjectIdentifier isakmpOakley = ipsec.branch("1"); + + /** IPSEC ISAKMP-Oakley hmacMD5 OID: 1.3.6.1.5.5.8.1.1 */ + static final ASN1ObjectIdentifier hmacMD5 = isakmpOakley.branch("1"); + /** IPSEC ISAKMP-Oakley hmacSHA1 OID: 1.3.6.1.5.5.8.1.2 */ + static final ASN1ObjectIdentifier hmacSHA1 = isakmpOakley.branch("2"); + + /** IPSEC ISAKMP-Oakley hmacTIGER OID: 1.3.6.1.5.5.8.1.3 */ + static final ASN1ObjectIdentifier hmacTIGER = isakmpOakley.branch("3"); + + /** IPSEC ISAKMP-Oakley hmacRIPEMD160 OID: 1.3.6.1.5.5.8.1.4 */ + static final ASN1ObjectIdentifier hmacRIPEMD160 = isakmpOakley.branch("4"); + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/CscaMasterList.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/CscaMasterList.java new file mode 100644 index 000000000..cb0fa3b03 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/CscaMasterList.java @@ -0,0 +1,114 @@ +package org.spongycastle.asn1.icao; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; +import org.spongycastle.asn1.x509.Certificate; + +/** + * The CscaMasterList object. This object can be wrapped in a + * CMSSignedData to be published in LDAP. + *
+ *+ * CscaMasterList ::= SEQUENCE { + * version CscaMasterListVersion, + * certList SET OF Certificate } + * + * CscaMasterListVersion :: INTEGER {v0(0)} + *+ */ + +public class CscaMasterList + extends ASN1Object +{ + private ASN1Integer version = new ASN1Integer(0); + private Certificate[] certList; + + public static CscaMasterList getInstance( + Object obj) + { + if (obj instanceof CscaMasterList) + { + return (CscaMasterList)obj; + } + else if (obj != null) + { + return new CscaMasterList(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CscaMasterList( + ASN1Sequence seq) + { + if (seq == null || seq.size() == 0) + { + throw new IllegalArgumentException( + "null or empty sequence passed."); + } + if (seq.size() != 2) + { + throw new IllegalArgumentException( + "Incorrect sequence size: " + seq.size()); + } + + version = ASN1Integer.getInstance(seq.getObjectAt(0)); + ASN1Set certSet = ASN1Set.getInstance(seq.getObjectAt(1)); + certList = new Certificate[certSet.size()]; + for (int i = 0; i < certList.length; i++) + { + certList[i] + = Certificate.getInstance(certSet.getObjectAt(i)); + } + } + + public CscaMasterList( + Certificate[] certStructs) + { + certList = copyCertList(certStructs); + } + + public int getVersion() + { + return version.getValue().intValue(); + } + + public Certificate[] getCertStructs() + { + return copyCertList(certList); + } + + private Certificate[] copyCertList(Certificate[] orig) + { + Certificate[] certs = new Certificate[orig.length]; + + for (int i = 0; i != certs.length; i++) + { + certs[i] = orig[i]; + } + + return certs; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + + seq.add(version); + + ASN1EncodableVector certSet = new ASN1EncodableVector(); + for (int i = 0; i < certList.length; i++) + { + certSet.add(certList[i]); + } + seq.add(new DERSet(certSet)); + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/DataGroupHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/DataGroupHash.java new file mode 100644 index 000000000..51a1ce2e2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/DataGroupHash.java @@ -0,0 +1,97 @@ +package org.spongycastle.asn1.icao; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * The DataGroupHash object. + *
+ * DataGroupHash ::= SEQUENCE { + * dataGroupNumber DataGroupNumber, + * dataGroupHashValue OCTET STRING } + * + * DataGroupNumber ::= INTEGER { + * dataGroup1 (1), + * dataGroup1 (2), + * dataGroup1 (3), + * dataGroup1 (4), + * dataGroup1 (5), + * dataGroup1 (6), + * dataGroup1 (7), + * dataGroup1 (8), + * dataGroup1 (9), + * dataGroup1 (10), + * dataGroup1 (11), + * dataGroup1 (12), + * dataGroup1 (13), + * dataGroup1 (14), + * dataGroup1 (15), + * dataGroup1 (16) } + * + *+ */ +public class DataGroupHash + extends ASN1Object +{ + ASN1Integer dataGroupNumber; + ASN1OctetString dataGroupHashValue; + + public static DataGroupHash getInstance( + Object obj) + { + if (obj instanceof DataGroupHash) + { + return (DataGroupHash)obj; + } + else if (obj != null) + { + return new DataGroupHash(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private DataGroupHash(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + // dataGroupNumber + dataGroupNumber = ASN1Integer.getInstance(e.nextElement()); + // dataGroupHashValue + dataGroupHashValue = ASN1OctetString.getInstance(e.nextElement()); + } + + public DataGroupHash( + int dataGroupNumber, + ASN1OctetString dataGroupHashValue) + { + this.dataGroupNumber = new ASN1Integer(dataGroupNumber); + this.dataGroupHashValue = dataGroupHashValue; + } + + public int getDataGroupNumber() + { + return dataGroupNumber.getValue().intValue(); + } + + public ASN1OctetString getDataGroupHashValue() + { + return dataGroupHashValue; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + seq.add(dataGroupNumber); + seq.add(dataGroupHashValue); + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/ICAOObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/ICAOObjectIdentifiers.java new file mode 100644 index 000000000..ffa293076 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/ICAOObjectIdentifiers.java @@ -0,0 +1,49 @@ +package org.spongycastle.asn1.icao; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * + * { ISOITU(2) intorgs(23) icao(136) } + */ +public interface ICAOObjectIdentifiers +{ + // + // base id + // + /** 2.23.136 */ + static final ASN1ObjectIdentifier id_icao = new ASN1ObjectIdentifier("2.23.136"); + + /** 2.23.136.1 */ + static final ASN1ObjectIdentifier id_icao_mrtd = id_icao.branch("1"); + /** 2.23.136.1.1 */ + static final ASN1ObjectIdentifier id_icao_mrtd_security = id_icao_mrtd.branch("1"); + + /** LDS security object, see ICAO Doc 9303-Volume 2-Section IV-A3.2
+ * 2.23.136.1.1.1 */ + static final ASN1ObjectIdentifier id_icao_ldsSecurityObject = id_icao_mrtd_security.branch("1"); + + /** CSCA master list, see TR CSCA Countersigning and Master List issuance
+ * 2.23.136.1.1.2 + */ + static final ASN1ObjectIdentifier id_icao_cscaMasterList = id_icao_mrtd_security.branch("2"); + /** 2.23.136.1.1.3 */ + static final ASN1ObjectIdentifier id_icao_cscaMasterListSigningKey = id_icao_mrtd_security.branch("3"); + + /** document type list, see draft TR LDS and PKI Maintenance, par. 3.2.1
+ * 2.23.136.1.1.4 + */ + static final ASN1ObjectIdentifier id_icao_documentTypeList = id_icao_mrtd_security.branch("4"); + + /** Active Authentication protocol, see draft TR LDS and PKI Maintenance, par. 5.2.2
+ * 2.23.136.1.1.5 + */ + static final ASN1ObjectIdentifier id_icao_aaProtocolObject = id_icao_mrtd_security.branch("5"); + + /** CSCA name change and key reoll-over, see draft TR LDS and PKI Maintenance, par. 3.2.1
+ * 2.23.136.1.1.6 + */ + static final ASN1ObjectIdentifier id_icao_extensions = id_icao_mrtd_security.branch("6"); + /** 2.23.136.1.1.6.1 */ + static final ASN1ObjectIdentifier id_icao_extensions_namechangekeyrollover = id_icao_extensions.branch("1"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/LDSSecurityObject.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/LDSSecurityObject.java new file mode 100644 index 000000000..f09f2923b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/LDSSecurityObject.java @@ -0,0 +1,159 @@ +package org.spongycastle.asn1.icao; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * The LDSSecurityObject object (V1.8). + *
+ * LDSSecurityObject ::= SEQUENCE { + * version LDSSecurityObjectVersion, + * hashAlgorithm DigestAlgorithmIdentifier, + * dataGroupHashValues SEQUENCE SIZE (2..ub-DataGroups) OF DataHashGroup, + * ldsVersionInfo LDSVersionInfo OPTIONAL + * -- if present, version MUST be v1 } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier, + * + * LDSSecurityObjectVersion :: INTEGER {V0(0)} + *+ */ + +public class LDSSecurityObject + extends ASN1Object + implements ICAOObjectIdentifiers +{ + public static final int ub_DataGroups = 16; + + private ASN1Integer version = new ASN1Integer(0); + private AlgorithmIdentifier digestAlgorithmIdentifier; + private DataGroupHash[] datagroupHash; + private LDSVersionInfo versionInfo; + + public static LDSSecurityObject getInstance( + Object obj) + { + if (obj instanceof LDSSecurityObject) + { + return (LDSSecurityObject)obj; + } + else if (obj != null) + { + return new LDSSecurityObject(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private LDSSecurityObject( + ASN1Sequence seq) + { + if (seq == null || seq.size() == 0) + { + throw new IllegalArgumentException("null or empty sequence passed."); + } + + Enumeration e = seq.getObjects(); + + // version + version = ASN1Integer.getInstance(e.nextElement()); + // digestAlgorithmIdentifier + digestAlgorithmIdentifier = AlgorithmIdentifier.getInstance(e.nextElement()); + + ASN1Sequence datagroupHashSeq = ASN1Sequence.getInstance(e.nextElement()); + + if (version.getValue().intValue() == 1) + { + versionInfo = LDSVersionInfo.getInstance(e.nextElement()); + } + + checkDatagroupHashSeqSize(datagroupHashSeq.size()); + + datagroupHash = new DataGroupHash[datagroupHashSeq.size()]; + for (int i= 0; i< datagroupHashSeq.size();i++) + { + datagroupHash[i] = DataGroupHash.getInstance(datagroupHashSeq.getObjectAt(i)); + } + } + + public LDSSecurityObject( + AlgorithmIdentifier digestAlgorithmIdentifier, + DataGroupHash[] datagroupHash) + { + this.version = new ASN1Integer(0); + this.digestAlgorithmIdentifier = digestAlgorithmIdentifier; + this.datagroupHash = datagroupHash; + + checkDatagroupHashSeqSize(datagroupHash.length); + } + + public LDSSecurityObject( + AlgorithmIdentifier digestAlgorithmIdentifier, + DataGroupHash[] datagroupHash, + LDSVersionInfo versionInfo) + { + this.version = new ASN1Integer(1); + this.digestAlgorithmIdentifier = digestAlgorithmIdentifier; + this.datagroupHash = datagroupHash; + this.versionInfo = versionInfo; + + checkDatagroupHashSeqSize(datagroupHash.length); + } + + private void checkDatagroupHashSeqSize(int size) + { + if ((size < 2) || (size > ub_DataGroups)) + { + throw new IllegalArgumentException("wrong size in DataGroupHashValues : not in (2.."+ ub_DataGroups +")"); + } + } + + public int getVersion() + { + return version.getValue().intValue(); + } + + public AlgorithmIdentifier getDigestAlgorithmIdentifier() + { + return digestAlgorithmIdentifier; + } + + public DataGroupHash[] getDatagroupHash() + { + return datagroupHash; + } + + public LDSVersionInfo getVersionInfo() + { + return versionInfo; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + + seq.add(version); + seq.add(digestAlgorithmIdentifier); + + ASN1EncodableVector seqname = new ASN1EncodableVector(); + for (int i = 0; i < datagroupHash.length; i++) + { + seqname.add(datagroupHash[i]); + } + seq.add(new DERSequence(seqname)); + + if (versionInfo != null) + { + seq.add(versionInfo); + } + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/LDSVersionInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/LDSVersionInfo.java new file mode 100644 index 000000000..e4cafc29b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/icao/LDSVersionInfo.java @@ -0,0 +1,75 @@ +package org.spongycastle.asn1.icao; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; + +public class LDSVersionInfo + extends ASN1Object +{ + private DERPrintableString ldsVersion; + private DERPrintableString unicodeVersion; + + public LDSVersionInfo(String ldsVersion, String unicodeVersion) + { + this.ldsVersion = new DERPrintableString(ldsVersion); + this.unicodeVersion = new DERPrintableString(unicodeVersion); + } + + private LDSVersionInfo(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("sequence wrong size for LDSVersionInfo"); + } + + this.ldsVersion = DERPrintableString.getInstance(seq.getObjectAt(0)); + this.unicodeVersion = DERPrintableString.getInstance(seq.getObjectAt(1)); + } + + public static LDSVersionInfo getInstance(Object obj) + { + if (obj instanceof LDSVersionInfo) + { + return (LDSVersionInfo)obj; + } + else if (obj != null) + { + return new LDSVersionInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public String getLdsVersion() + { + return ldsVersion.getString(); + } + + public String getUnicodeVersion() + { + return unicodeVersion.getString(); + } + + /** + *
+ * LDSVersionInfo ::= SEQUENCE { + * ldsVersion PRINTABLE STRING + * unicodeVersion PRINTABLE STRING + * } + *+ * @return + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(ldsVersion); + v.add(unicodeVersion); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java new file mode 100644 index 000000000..3add7dc9a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java @@ -0,0 +1,210 @@ +package org.spongycastle.asn1.isismtt; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * ISISMT -- Industrial Signature Interoperability Specification + */ +public interface ISISMTTObjectIdentifiers +{ + + /** 1.3.36.8 */ + static final ASN1ObjectIdentifier id_isismtt = new ASN1ObjectIdentifier("1.3.36.8"); + + /** 1.3.36.8.1 */ + static final ASN1ObjectIdentifier id_isismtt_cp = id_isismtt.branch("1"); + + /** + * The id-isismtt-cp-accredited OID indicates that the certificate is a + * qualified certificate according to Directive 1999/93/EC of the European + * Parliament and of the Council of 13 December 1999 on a Community + * Framework for Electronic Signatures, which additionally conforms the + * special requirements of the SigG and has been issued by an accredited CA. + *
+ * 1.3.36.8.1.1 + */ + + static final ASN1ObjectIdentifier id_isismtt_cp_accredited = id_isismtt_cp.branch("1"); + + /** 1.3.36.8.3 */ + static final ASN1ObjectIdentifier id_isismtt_at = id_isismtt.branch("3"); + + /** + * Certificate extensionDate of certificate generation + *
+ * DateOfCertGenSyntax ::= GeneralizedTime + *+ * OID: 1.3.36.8.3.1 + */ + static final ASN1ObjectIdentifier id_isismtt_at_dateOfCertGen = id_isismtt_at.branch("1"); + + /** + * Attribute to indicate that the certificate holder may sign in the name of + * a third person. May also be used as extension in a certificate. + *
+ * OID: 1.3.36.8.3.2 + */ + static final ASN1ObjectIdentifier id_isismtt_at_procuration = id_isismtt_at.branch("2"); + + /** + * Attribute to indicate admissions to certain professions. May be used as + * attribute in attribute certificate or as extension in a certificate + *
+ * OID: 1.3.36.8.3.3 + */ + static final ASN1ObjectIdentifier id_isismtt_at_admission = id_isismtt_at.branch("3"); + + /** + * Monetary limit for transactions. The QcEuMonetaryLimit QC statement MUST + * be used in new certificates in place of the extension/attribute + * MonetaryLimit since January 1, 2004. For the sake of backward + * compatibility with certificates already in use, SigG conforming + * components MUST support MonetaryLimit (as well as QcEuLimitValue). + *
+ * OID: 1.3.36.8.3.4 + */ + static final ASN1ObjectIdentifier id_isismtt_at_monetaryLimit = id_isismtt_at.branch("4"); + + /** + * A declaration of majority. May be used as attribute in attribute + * certificate or as extension in a certificate + *
+ * OID: 1.3.36.8.3.5 + */ + static final ASN1ObjectIdentifier id_isismtt_at_declarationOfMajority = id_isismtt_at.branch("5"); + + /** + * Serial number of the smart card containing the corresponding private key + *
+ * ICCSNSyntax ::= OCTET STRING (SIZE(8..20)) + *+ *
+ * OID: 1.3.36.8.3.6 + */ + static final ASN1ObjectIdentifier id_isismtt_at_iCCSN = id_isismtt_at.branch("6"); + + /** + * Reference for a file of a smartcard that stores the public key of this + * certificate and that is used as "security anchor". + *
+ * PKReferenceSyntax ::= OCTET STRING (SIZE(20)) + *+ *
+ * OID: 1.3.36.8.3.7 + */ + static final ASN1ObjectIdentifier id_isismtt_at_PKReference = id_isismtt_at.branch("7"); + + /** + * Some other restriction regarding the usage of this certificate. May be + * used as attribute in attribute certificate or as extension in a + * certificate. + *
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + *+ *
+ * OID: 1.3.36.8.3.8 + * + * @see org.spongycastle.asn1.isismtt.x509.Restriction + */ + static final ASN1ObjectIdentifier id_isismtt_at_restriction = id_isismtt_at.branch("8"); + + /** + * (Single)Request extension: Clients may include this extension in a + * (single) Request to request the responder to send the certificate in the + * response message along with the status information. Besides the LDAP + * service, this extension provides another mechanism for the distribution + * of certificates, which MAY optionally be provided by certificate + * repositories. + *
+ * RetrieveIfAllowed ::= BOOLEAN + *+ *
+ * OID: 1.3.36.8.3.9 + */ + static final ASN1ObjectIdentifier id_isismtt_at_retrieveIfAllowed = id_isismtt_at.branch("9"); + + /** + * SingleOCSPResponse extension: The certificate requested by the client by + * inserting the RetrieveIfAllowed extension in the request, will be + * returned in this extension. + *
+ * OID: 1.3.36.8.3.10 + * + * @see org.spongycastle.asn1.isismtt.ocsp.RequestedCertificate + */ + static final ASN1ObjectIdentifier id_isismtt_at_requestedCertificate = id_isismtt_at.branch("10"); + + /** + * Base ObjectIdentifier for naming authorities + *
+ * OID: 1.3.36.8.3.11 + */ + static final ASN1ObjectIdentifier id_isismtt_at_namingAuthorities = id_isismtt_at.branch("11"); + + /** + * SingleOCSPResponse extension: Date, when certificate has been published + * in the directory and status information has become available. Currently, + * accrediting authorities enforce that SigG-conforming OCSP servers include + * this extension in the responses. + * + *
+ * CertInDirSince ::= GeneralizedTime + *+ *
+ * OID: 1.3.36.8.3.12 + */ + static final ASN1ObjectIdentifier id_isismtt_at_certInDirSince = id_isismtt_at.branch("12"); + + /** + * Hash of a certificate in OCSP. + *
+ * OID: 1.3.36.8.3.13 + * + * @see org.spongycastle.asn1.isismtt.ocsp.CertHash + */ + static final ASN1ObjectIdentifier id_isismtt_at_certHash = id_isismtt_at.branch("13"); + + /** + *
+ * NameAtBirth ::= DirectoryString(SIZE(1..64) + *+ * + * Used in + * {@link org.spongycastle.asn1.x509.SubjectDirectoryAttributes SubjectDirectoryAttributes} + *
+ * OID: 1.3.36.8.3.14 + */ + static final ASN1ObjectIdentifier id_isismtt_at_nameAtBirth = id_isismtt_at.branch("14"); + + /** + * Some other information of non-restrictive nature regarding the usage of + * this certificate. May be used as attribute in atribute certificate or as + * extension in a certificate. + * + *
+ * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) + *+ *
+ * OID: 1.3.36.8.3.15 + * + * @see org.spongycastle.asn1.isismtt.x509.AdditionalInformationSyntax + */ + static final ASN1ObjectIdentifier id_isismtt_at_additionalInformation = id_isismtt_at.branch("15"); + + /** + * Indicates that an attribute certificate exists, which limits the + * usability of this public key certificate. Whenever verifying a signature + * with the help of this certificate, the content of the corresponding + * attribute certificate should be concerned. This extension MUST be + * included in a PKC, if a corresponding attribute certificate (having the + * PKC as base certificate) contains some attribute that restricts the + * usability of the PKC too. Attribute certificates with restricting content + * MUST always be included in the signed document. + *
+ * LiabilityLimitationFlagSyntax ::= BOOLEAN + *+ *
+ * OID: 0.2.262.1.10.12.0 + */ + static final ASN1ObjectIdentifier id_isismtt_at_liabilityLimitationFlag = new ASN1ObjectIdentifier("0.2.262.1.10.12.0"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ocsp/CertHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ocsp/CertHash.java new file mode 100644 index 000000000..85dc7f329 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ocsp/CertHash.java @@ -0,0 +1,124 @@ +package org.spongycastle.asn1.isismtt.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * ISIS-MTT PROFILE: The responder may include this extension in a response to + * send the hash of the requested certificate to the responder. This hash is + * cryptographically bound to the certificate and serves as evidence that the + * certificate is known to the responder (i.e. it has been issued and is present + * in the directory). Hence, this extension is a means to provide a positive + * statement of availability as described in T8.[8]. As explained in T13.[1], + * clients may rely on this information to be able to validate signatures after + * the expiry of the corresponding certificate. Hence, clients MUST support this + * extension. If a positive statement of availability is to be delivered, this + * extension syntax and OID MUST be used. + *
+ * + *+ * CertHash ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * certificateHash OCTET STRING + * } + *+ */ +public class CertHash + extends ASN1Object +{ + + private AlgorithmIdentifier hashAlgorithm; + private byte[] certificateHash; + + public static CertHash getInstance(Object obj) + { + if (obj == null || obj instanceof CertHash) + { + return (CertHash)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new CertHash((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type CertHash: + * + *
+ * CertHash ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * certificateHash OCTET STRING + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private CertHash(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + certificateHash = DEROctetString.getInstance(seq.getObjectAt(1)).getOctets(); + } + + /** + * Constructor from a given details. + * + * @param hashAlgorithm The hash algorithm identifier. + * @param certificateHash The hash of the whole DER encoding of the certificate. + */ + public CertHash(AlgorithmIdentifier hashAlgorithm, byte[] certificateHash) + { + this.hashAlgorithm = hashAlgorithm; + this.certificateHash = new byte[certificateHash.length]; + System.arraycopy(certificateHash, 0, this.certificateHash, 0, + certificateHash.length); + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public byte[] getCertificateHash() + { + return certificateHash; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * CertHash ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * certificateHash OCTET STRING + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + vec.add(hashAlgorithm); + vec.add(new DEROctetString(certificateHash)); + return new DERSequence(vec); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ocsp/RequestedCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ocsp/RequestedCertificate.java new file mode 100644 index 000000000..395c37439 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/ocsp/RequestedCertificate.java @@ -0,0 +1,183 @@ +package org.spongycastle.asn1.isismtt.ocsp; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Certificate; + +/** + * ISIS-MTT-Optional: The certificate requested by the client by inserting the + * RetrieveIfAllowed extension in the request, will be returned in this + * extension. + * + * ISIS-MTT-SigG: The signature act allows publishing certificates only then, + * when the certificate owner gives his explicit permission. Accordingly, there + * may be ïżœnondownloadableïżœ certificates, about which the responder must provide + * status information, but MUST NOT include them in the response. Clients may + * get therefore the following three kind of answers on a single request + * including the RetrieveIfAllowed extension: + *
+ * RequestedCertificate ::= CHOICE { + * Certificate Certificate, + * publicKeyCertificate [0] EXPLICIT OCTET STRING, + * attributeCertificate [1] EXPLICIT OCTET STRING + * } + *+ */ +public class RequestedCertificate + extends ASN1Object + implements ASN1Choice +{ + public static final int certificate = -1; + public static final int publicKeyCertificate = 0; + public static final int attributeCertificate = 1; + + private Certificate cert; + private byte[] publicKeyCert; + private byte[] attributeCert; + + public static RequestedCertificate getInstance(Object obj) + { + if (obj == null || obj instanceof RequestedCertificate) + { + return (RequestedCertificate)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new RequestedCertificate(Certificate.getInstance(obj)); + } + if (obj instanceof ASN1TaggedObject) + { + return new RequestedCertificate((ASN1TaggedObject)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + public static RequestedCertificate getInstance(ASN1TaggedObject obj, boolean explicit) + { + if (!explicit) + { + throw new IllegalArgumentException("choice item must be explicitly tagged"); + } + + return getInstance(obj.getObject()); + } + + private RequestedCertificate(ASN1TaggedObject tagged) + { + if (tagged.getTagNo() == publicKeyCertificate) + { + publicKeyCert = ASN1OctetString.getInstance(tagged, true).getOctets(); + } + else if (tagged.getTagNo() == attributeCertificate) + { + attributeCert = ASN1OctetString.getInstance(tagged, true).getOctets(); + } + else + { + throw new IllegalArgumentException("unknown tag number: " + tagged.getTagNo()); + } + } + + /** + * Constructor from a given details. + * + * Only one parameter can be given. All other must be
null
.
+ *
+ * @param certificate Given as Certificate
+ */
+ public RequestedCertificate(Certificate certificate)
+ {
+ this.cert = certificate;
+ }
+
+ public RequestedCertificate(int type, byte[] certificateOctets)
+ {
+ this(new DERTaggedObject(type, new DEROctetString(certificateOctets)));
+ }
+
+ public int getType()
+ {
+ if (cert != null)
+ {
+ return certificate;
+ }
+ if (publicKeyCert != null)
+ {
+ return publicKeyCertificate;
+ }
+ return attributeCertificate;
+ }
+
+ public byte[] getCertificateBytes()
+ {
+ if (cert != null)
+ {
+ try
+ {
+ return cert.getEncoded();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("can't decode certificate: " + e);
+ }
+ }
+ if (publicKeyCert != null)
+ {
+ return publicKeyCert;
+ }
+ return attributeCert;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * + * RequestedCertificate ::= CHOICE { + * Certificate Certificate, + * publicKeyCertificate [0] EXPLICIT OCTET STRING, + * attributeCertificate [1] EXPLICIT OCTET STRING + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + if (publicKeyCert != null) + { + return new DERTaggedObject(0, new DEROctetString(publicKeyCert)); + } + if (attributeCert != null) + { + return new DERTaggedObject(1, new DEROctetString(attributeCert)); + } + return cert.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java new file mode 100644 index 000000000..8436b9d2f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java @@ -0,0 +1,70 @@ +package org.spongycastle.asn1.isismtt.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Some other information of non-restrictive nature regarding the usage of this + * certificate. + * + *
+ * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) + *+ */ +public class AdditionalInformationSyntax + extends ASN1Object +{ + private DirectoryString information; + + public static AdditionalInformationSyntax getInstance(Object obj) + { + if (obj instanceof AdditionalInformationSyntax) + { + return (AdditionalInformationSyntax)obj; + } + + if (obj != null) + { + return new AdditionalInformationSyntax(DirectoryString.getInstance(obj)); + } + + return null; + } + + private AdditionalInformationSyntax(DirectoryString information) + { + this.information = information; + } + + /** + * Constructor from a given details. + * + * @param information The describtion of the information. + */ + public AdditionalInformationSyntax(String information) + { + this(new DirectoryString(information)); + } + + public DirectoryString getInformation() + { + return information; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + return information.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/AdmissionSyntax.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/AdmissionSyntax.java new file mode 100644 index 000000000..1cc5f4071 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/AdmissionSyntax.java @@ -0,0 +1,280 @@ +package org.spongycastle.asn1.isismtt.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.GeneralName; + +/** + * Attribute to indicate admissions to certain professions. + * + *
+ * AdmissionSyntax ::= SEQUENCE + * { + * admissionAuthority GeneralName OPTIONAL, + * contentsOfAdmissions SEQUENCE OF Admissions + * } + * + * Admissions ::= SEQUENCE + * { + * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL + * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL + * professionInfos SEQUENCE OF ProfessionInfo + * } + * + * NamingAuthority ::= SEQUENCE + * { + * namingAuthorityId OBJECT IDENTIFIER OPTIONAL, + * namingAuthorityUrl IA5String OPTIONAL, + * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL + * } + * + * ProfessionInfo ::= SEQUENCE + * { + * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL, + * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)), + * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL, + * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL, + * addProfessionInfo OCTET STRING OPTIONAL + * } + *+ * + * + * ISIS-MTT PROFILE: The relatively complex structure of AdmissionSyntax + * supports the following concepts and requirements: + *
+ * AdmissionSyntax ::= SEQUENCE + * { + * admissionAuthority GeneralName OPTIONAL, + * contentsOfAdmissions SEQUENCE OF Admissions + * } + * + * Admissions ::= SEQUENCE + * { + * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL + * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL + * professionInfos SEQUENCE OF ProfessionInfo + * } + * + * NamingAuthority ::= SEQUENCE + * { + * namingAuthorityId OBJECT IDENTIFIER OPTIONAL, + * namingAuthorityUrl IA5String OPTIONAL, + * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL + * } + * + * ProfessionInfo ::= SEQUENCE + * { + * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL, + * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)), + * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL, + * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL, + * addProfessionInfo OCTET STRING OPTIONAL + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private AdmissionSyntax(ASN1Sequence seq) + { + switch (seq.size()) + { + case 1: + contentsOfAdmissions = DERSequence.getInstance(seq.getObjectAt(0)); + break; + case 2: + admissionAuthority = GeneralName.getInstance(seq.getObjectAt(0)); + contentsOfAdmissions = DERSequence.getInstance(seq.getObjectAt(1)); + break; + default: + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + } + + /** + * Constructor from given details. + * + * @param admissionAuthority The admission authority. + * @param contentsOfAdmissions The admissions. + */ + public AdmissionSyntax(GeneralName admissionAuthority, ASN1Sequence contentsOfAdmissions) + { + this.admissionAuthority = admissionAuthority; + this.contentsOfAdmissions = contentsOfAdmissions; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * AdmissionSyntax ::= SEQUENCE + * { + * admissionAuthority GeneralName OPTIONAL, + * contentsOfAdmissions SEQUENCE OF Admissions + * } + * + * Admissions ::= SEQUENCE + * { + * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL + * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL + * professionInfos SEQUENCE OF ProfessionInfo + * } + * + * NamingAuthority ::= SEQUENCE + * { + * namingAuthorityId OBJECT IDENTIFIER OPTIONAL, + * namingAuthorityUrl IA5String OPTIONAL, + * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL + * } + * + * ProfessionInfo ::= SEQUENCE + * { + * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL, + * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)), + * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL, + * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL, + * addProfessionInfo OCTET STRING OPTIONAL + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + if (admissionAuthority != null) + { + vec.add(admissionAuthority); + } + vec.add(contentsOfAdmissions); + return new DERSequence(vec); + } + + /** + * @return Returns the admissionAuthority if present, null otherwise. + */ + public GeneralName getAdmissionAuthority() + { + return admissionAuthority; + } + + /** + * @return Returns the contentsOfAdmissions. + */ + public Admissions[] getContentsOfAdmissions() + { + Admissions[] admissions = new Admissions[contentsOfAdmissions.size()]; + int count = 0; + for (Enumeration e = contentsOfAdmissions.getObjects(); e.hasMoreElements();) + { + admissions[count++] = Admissions.getInstance(e.nextElement()); + } + return admissions; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/Admissions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/Admissions.java new file mode 100644 index 000000000..9b140c966 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/Admissions.java @@ -0,0 +1,189 @@ +package org.spongycastle.asn1.isismtt.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.GeneralName; + +/** + * An Admissions structure. + * + *
+ * Admissions ::= SEQUENCE + * { + * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL + * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL + * professionInfos SEQUENCE OF ProfessionInfo + * } + * + *+ * + * @see org.spongycastle.asn1.isismtt.x509.AdmissionSyntax + * @see org.spongycastle.asn1.isismtt.x509.ProfessionInfo + * @see org.spongycastle.asn1.isismtt.x509.NamingAuthority + */ +public class Admissions + extends ASN1Object +{ + + private GeneralName admissionAuthority; + + private NamingAuthority namingAuthority; + + private ASN1Sequence professionInfos; + + public static Admissions getInstance(Object obj) + { + if (obj == null || obj instanceof Admissions) + { + return (Admissions)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new Admissions((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type ProcurationSyntax: + * + *
+ * Admissions ::= SEQUENCE + * { + * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL + * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL + * professionInfos SEQUENCE OF ProfessionInfo + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private Admissions(ASN1Sequence seq) + { + if (seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + Enumeration e = seq.getObjects(); + + ASN1Encodable o = (ASN1Encodable)e.nextElement(); + if (o instanceof ASN1TaggedObject) + { + switch (((ASN1TaggedObject)o).getTagNo()) + { + case 0: + admissionAuthority = GeneralName.getInstance((ASN1TaggedObject)o, true); + break; + case 1: + namingAuthority = NamingAuthority.getInstance((ASN1TaggedObject)o, true); + break; + default: + throw new IllegalArgumentException("Bad tag number: " + ((ASN1TaggedObject)o).getTagNo()); + } + o = (ASN1Encodable)e.nextElement(); + } + if (o instanceof ASN1TaggedObject) + { + switch (((ASN1TaggedObject)o).getTagNo()) + { + case 1: + namingAuthority = NamingAuthority.getInstance((ASN1TaggedObject)o, true); + break; + default: + throw new IllegalArgumentException("Bad tag number: " + ((ASN1TaggedObject)o).getTagNo()); + } + o = (ASN1Encodable)e.nextElement(); + } + professionInfos = ASN1Sequence.getInstance(o); + if (e.hasMoreElements()) + { + throw new IllegalArgumentException("Bad object encountered: " + + e.nextElement().getClass()); + } + } + + /** + * Constructor from a given details. + * + * Parameter
professionInfos
is mandatory.
+ *
+ * @param admissionAuthority The admission authority.
+ * @param namingAuthority The naming authority.
+ * @param professionInfos The profession infos.
+ */
+ public Admissions(GeneralName admissionAuthority,
+ NamingAuthority namingAuthority, ProfessionInfo[] professionInfos)
+ {
+ this.admissionAuthority = admissionAuthority;
+ this.namingAuthority = namingAuthority;
+ this.professionInfos = new DERSequence(professionInfos);
+ }
+
+ public GeneralName getAdmissionAuthority()
+ {
+ return admissionAuthority;
+ }
+
+ public NamingAuthority getNamingAuthority()
+ {
+ return namingAuthority;
+ }
+
+ public ProfessionInfo[] getProfessionInfos()
+ {
+ ProfessionInfo[] infos = new ProfessionInfo[professionInfos.size()];
+ int count = 0;
+ for (Enumeration e = professionInfos.getObjects(); e.hasMoreElements();)
+ {
+ infos[count++] = ProfessionInfo.getInstance(e.nextElement());
+ }
+ return infos;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * + * Admissions ::= SEQUENCE + * { + * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL + * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL + * professionInfos SEQUENCE OF ProfessionInfo + * } + * + *+ * + * @return an ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + + if (admissionAuthority != null) + { + vec.add(new DERTaggedObject(true, 0, admissionAuthority)); + } + if (namingAuthority != null) + { + vec.add(new DERTaggedObject(true, 1, namingAuthority)); + } + vec.add(professionInfos); + + return new DERSequence(vec); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/DeclarationOfMajority.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/DeclarationOfMajority.java new file mode 100644 index 000000000..fa8851d76 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/DeclarationOfMajority.java @@ -0,0 +1,164 @@ +package org.spongycastle.asn1.isismtt.x509; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * A declaration of majority. + * + *
+ * DeclarationOfMajoritySyntax ::= CHOICE + * { + * notYoungerThan [0] IMPLICIT INTEGER, + * fullAgeAtCountry [1] IMPLICIT SEQUENCE + * { + * fullAge BOOLEAN DEFAULT TRUE, + * country PrintableString (SIZE(2)) + * } + * dateOfBirth [2] IMPLICIT GeneralizedTime + * } + *+ * + * fullAgeAtCountry indicates the majority of the owner with respect to the laws + * of a specific country. + */ +public class DeclarationOfMajority + extends ASN1Object + implements ASN1Choice +{ + public static final int notYoungerThan = 0; + public static final int fullAgeAtCountry = 1; + public static final int dateOfBirth = 2; + + private ASN1TaggedObject declaration; + + public DeclarationOfMajority(int notYoungerThan) + { + declaration = new DERTaggedObject(false, 0, new ASN1Integer(notYoungerThan)); + } + + public DeclarationOfMajority(boolean fullAge, String country) + { + if (country.length() > 2) + { + throw new IllegalArgumentException("country can only be 2 characters"); + } + + if (fullAge) + { + declaration = new DERTaggedObject(false, 1, new DERSequence(new DERPrintableString(country, true))); + } + else + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(ASN1Boolean.FALSE); + v.add(new DERPrintableString(country, true)); + + declaration = new DERTaggedObject(false, 1, new DERSequence(v)); + } + } + + public DeclarationOfMajority(ASN1GeneralizedTime dateOfBirth) + { + declaration = new DERTaggedObject(false, 2, dateOfBirth); + } + + public static DeclarationOfMajority getInstance(Object obj) + { + if (obj == null || obj instanceof DeclarationOfMajority) + { + return (DeclarationOfMajority)obj; + } + + if (obj instanceof ASN1TaggedObject) + { + return new DeclarationOfMajority((ASN1TaggedObject)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + private DeclarationOfMajority(ASN1TaggedObject o) + { + if (o.getTagNo() > 2) + { + throw new IllegalArgumentException("Bad tag number: " + o.getTagNo()); + } + declaration = o; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * DeclarationOfMajoritySyntax ::= CHOICE + * { + * notYoungerThan [0] IMPLICIT INTEGER, + * fullAgeAtCountry [1] IMPLICIT SEQUENCE + * { + * fullAge BOOLEAN DEFAULT TRUE, + * country PrintableString (SIZE(2)) + * } + * dateOfBirth [2] IMPLICIT GeneralizedTime + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + return declaration; + } + + public int getType() + { + return declaration.getTagNo(); + } + + /** + * @return notYoungerThan if that's what we are, -1 otherwise + */ + public int notYoungerThan() + { + if (declaration.getTagNo() != 0) + { + return -1; + } + + return ASN1Integer.getInstance(declaration, false).getValue().intValue(); + } + + public ASN1Sequence fullAgeAtCountry() + { + if (declaration.getTagNo() != 1) + { + return null; + } + + return ASN1Sequence.getInstance(declaration, false); + } + + public ASN1GeneralizedTime getDateOfBirth() + { + if (declaration.getTagNo() != 2) + { + return null; + } + + return ASN1GeneralizedTime.getInstance(declaration, false); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/MonetaryLimit.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/MonetaryLimit.java new file mode 100644 index 000000000..f7f646d20 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/MonetaryLimit.java @@ -0,0 +1,131 @@ +package org.spongycastle.asn1.isismtt.x509; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; + +/** + * Monetary limit for transactions. The QcEuMonetaryLimit QC statement MUST be + * used in new certificates in place of the extension/attribute MonetaryLimit + * since January 1, 2004. For the sake of backward compatibility with + * certificates already in use, components SHOULD support MonetaryLimit (as well + * as QcEuLimitValue). + * + * Indicates a monetary limit within which the certificate holder is authorized + * to act. (This value DOES NOT express a limit on the liability of the + * certification authority). + * + *
+ * MonetaryLimitSyntax ::= SEQUENCE + * { + * currency PrintableString (SIZE(3)), + * amount INTEGER, + * exponent INTEGER + * } + *+ * + * currency must be the ISO code. + * + * value = amountïżœ10*exponent + */ +public class MonetaryLimit + extends ASN1Object +{ + DERPrintableString currency; + ASN1Integer amount; + ASN1Integer exponent; + + public static MonetaryLimit getInstance(Object obj) + { + if (obj == null || obj instanceof MonetaryLimit) + { + return (MonetaryLimit)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new MonetaryLimit(ASN1Sequence.getInstance(obj)); + } + + throw new IllegalArgumentException("unknown object in getInstance"); + } + + private MonetaryLimit(ASN1Sequence seq) + { + if (seq.size() != 3) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + Enumeration e = seq.getObjects(); + currency = DERPrintableString.getInstance(e.nextElement()); + amount = ASN1Integer.getInstance(e.nextElement()); + exponent = ASN1Integer.getInstance(e.nextElement()); + } + + /** + * Constructor from a given details. + * + * + * value = amountïżœ10^exponent + * + * @param currency The currency. Must be the ISO code. + * @param amount The amount + * @param exponent The exponent + */ + public MonetaryLimit(String currency, int amount, int exponent) + { + this.currency = new DERPrintableString(currency, true); + this.amount = new ASN1Integer(amount); + this.exponent = new ASN1Integer(exponent); + } + + public String getCurrency() + { + return currency.getString(); + } + + public BigInteger getAmount() + { + return amount.getValue(); + } + + public BigInteger getExponent() + { + return exponent.getValue(); + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * MonetaryLimitSyntax ::= SEQUENCE + * { + * currency PrintableString (SIZE(3)), + * amount INTEGER, + * exponent INTEGER + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + seq.add(currency); + seq.add(amount); + seq.add(exponent); + + return new DERSequence(seq); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/NamingAuthority.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/NamingAuthority.java new file mode 100644 index 000000000..920d2e0d1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/NamingAuthority.java @@ -0,0 +1,244 @@ +package org.spongycastle.asn1.isismtt.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.isismtt.ISISMTTObjectIdentifiers; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Names of authorities which are responsible for the administration of title + * registers. + * + *
+ * NamingAuthority ::= SEQUENCE + * { + * namingAuthorityId OBJECT IDENTIFIER OPTIONAL, + * namingAuthorityUrl IA5String OPTIONAL, + * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL + * } + *+ * @see org.spongycastle.asn1.isismtt.x509.AdmissionSyntax + * + */ +public class NamingAuthority + extends ASN1Object +{ + + /** + * Profession OIDs should always be defined under the OID branch of the + * responsible naming authority. At the time of this writing, the work group + * ïżœRecht, Wirtschaft, Steuernïżœ (ïżœLaw, Economy, Taxesïżœ) is registered as the + * first naming authority under the OID id-isismtt-at-namingAuthorities. + */ + public static final ASN1ObjectIdentifier id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern = + new ASN1ObjectIdentifier(ISISMTTObjectIdentifiers.id_isismtt_at_namingAuthorities + ".1"); + + private ASN1ObjectIdentifier namingAuthorityId; + private String namingAuthorityUrl; + private DirectoryString namingAuthorityText; + + public static NamingAuthority getInstance(Object obj) + { + if (obj == null || obj instanceof NamingAuthority) + { + return (NamingAuthority)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new NamingAuthority((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + public static NamingAuthority getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Constructor from ASN1Sequence. + * + * + *
+ * NamingAuthority ::= SEQUENCE + * { + * namingAuthorityId OBJECT IDENTIFIER OPTIONAL, + * namingAuthorityUrl IA5String OPTIONAL, + * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private NamingAuthority(ASN1Sequence seq) + { + + if (seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + if (e.hasMoreElements()) + { + ASN1Encodable o = (ASN1Encodable)e.nextElement(); + if (o instanceof ASN1ObjectIdentifier) + { + namingAuthorityId = (ASN1ObjectIdentifier)o; + } + else if (o instanceof DERIA5String) + { + namingAuthorityUrl = DERIA5String.getInstance(o).getString(); + } + else if (o instanceof ASN1String) + { + namingAuthorityText = DirectoryString.getInstance(o); + } + else + { + throw new IllegalArgumentException("Bad object encountered: " + + o.getClass()); + } + } + if (e.hasMoreElements()) + { + ASN1Encodable o = (ASN1Encodable)e.nextElement(); + if (o instanceof DERIA5String) + { + namingAuthorityUrl = DERIA5String.getInstance(o).getString(); + } + else if (o instanceof ASN1String) + { + namingAuthorityText = DirectoryString.getInstance(o); + } + else + { + throw new IllegalArgumentException("Bad object encountered: " + + o.getClass()); + } + } + if (e.hasMoreElements()) + { + ASN1Encodable o = (ASN1Encodable)e.nextElement(); + if (o instanceof ASN1String) + { + namingAuthorityText = DirectoryString.getInstance(o); + } + else + { + throw new IllegalArgumentException("Bad object encountered: " + + o.getClass()); + } + + } + } + + /** + * @return Returns the namingAuthorityId. + */ + public ASN1ObjectIdentifier getNamingAuthorityId() + { + return namingAuthorityId; + } + + /** + * @return Returns the namingAuthorityText. + */ + public DirectoryString getNamingAuthorityText() + { + return namingAuthorityText; + } + + /** + * @return Returns the namingAuthorityUrl. + */ + public String getNamingAuthorityUrl() + { + return namingAuthorityUrl; + } + + /** + * Constructor from given details. + * + * All parameters can be combined. + * + * @param namingAuthorityId ObjectIdentifier for naming authority. + * @param namingAuthorityUrl URL for naming authority. + * @param namingAuthorityText Textual representation of naming authority. + * @deprecated use ASN1ObjectIdentifier method + */ + public NamingAuthority(DERObjectIdentifier namingAuthorityId, + String namingAuthorityUrl, DirectoryString namingAuthorityText) + { + this.namingAuthorityId = new ASN1ObjectIdentifier(namingAuthorityId.getId()); + this.namingAuthorityUrl = namingAuthorityUrl; + this.namingAuthorityText = namingAuthorityText; + } + + /** + * Constructor from given details. + * + * All parameters can be combined. + * + * @param namingAuthorityId ObjectIdentifier for naming authority. + * @param namingAuthorityUrl URL for naming authority. + * @param namingAuthorityText Textual representation of naming authority. + */ + public NamingAuthority(ASN1ObjectIdentifier namingAuthorityId, + String namingAuthorityUrl, DirectoryString namingAuthorityText) + { + this.namingAuthorityId = namingAuthorityId; + this.namingAuthorityUrl = namingAuthorityUrl; + this.namingAuthorityText = namingAuthorityText; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * NamingAuthority ::= SEQUENCE + * { + * namingAuthorityId OBJECT IDENTIFIER OPTIONAL, + * namingAuthorityUrl IA5String OPTIONAL, + * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + if (namingAuthorityId != null) + { + vec.add(namingAuthorityId); + } + if (namingAuthorityUrl != null) + { + vec.add(new DERIA5String(namingAuthorityUrl, true)); + } + if (namingAuthorityText != null) + { + vec.add(namingAuthorityText); + } + return new DERSequence(vec); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/ProcurationSyntax.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/ProcurationSyntax.java new file mode 100644 index 000000000..5838fee30 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/ProcurationSyntax.java @@ -0,0 +1,240 @@ +package org.spongycastle.asn1.isismtt.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.DirectoryString; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.IssuerSerial; + +/** + * Attribute to indicate that the certificate holder may sign in the name of a + * third person. + *
+ * ISIS-MTT PROFILE: The corresponding ProcurationSyntax contains either the + * name of the person who is represented (subcomponent thirdPerson) or a + * reference to his/her base certificate (in the component signingFor, + * subcomponent certRef), furthermore the optional components country and + * typeSubstitution to indicate the country whose laws apply, and respectively + * the type of procuration (e.g. manager, procuration, custody). + *
+ * ISIS-MTT PROFILE: The GeneralName MUST be of type directoryName and MAY only + * contain: - RFC3039 attributes, except pseudonym (countryName, commonName, + * surname, givenName, serialNumber, organizationName, organizationalUnitName, + * stateOrProvincename, localityName, postalAddress) and - SubjectDirectoryName + * attributes (title, dateOfBirth, placeOfBirth, gender, countryOfCitizenship, + * countryOfResidence and NameAtBirth). + * + *
+ * ProcurationSyntax ::= SEQUENCE { + * country [1] EXPLICIT PrintableString(SIZE(2)) OPTIONAL, + * typeOfSubstitution [2] EXPLICIT DirectoryString (SIZE(1..128)) OPTIONAL, + * signingFor [3] EXPLICIT SigningFor + * } + * + * SigningFor ::= CHOICE + * { + * thirdPerson GeneralName, + * certRef IssuerSerial + * } + *+ * + */ +public class ProcurationSyntax + extends ASN1Object +{ + private String country; + private DirectoryString typeOfSubstitution; + + private GeneralName thirdPerson; + private IssuerSerial certRef; + + public static ProcurationSyntax getInstance(Object obj) + { + if (obj == null || obj instanceof ProcurationSyntax) + { + return (ProcurationSyntax)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new ProcurationSyntax((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type ProcurationSyntax: + * + *
+ * ProcurationSyntax ::= SEQUENCE { + * country [1] EXPLICIT PrintableString(SIZE(2)) OPTIONAL, + * typeOfSubstitution [2] EXPLICIT DirectoryString (SIZE(1..128)) OPTIONAL, + * signingFor [3] EXPLICIT SigningFor + * } + * + * SigningFor ::= CHOICE + * { + * thirdPerson GeneralName, + * certRef IssuerSerial + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private ProcurationSyntax(ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement()); + switch (o.getTagNo()) + { + case 1: + country = DERPrintableString.getInstance(o, true).getString(); + break; + case 2: + typeOfSubstitution = DirectoryString.getInstance(o, true); + break; + case 3: + ASN1Encodable signingFor = o.getObject(); + if (signingFor instanceof ASN1TaggedObject) + { + thirdPerson = GeneralName.getInstance(signingFor); + } + else + { + certRef = IssuerSerial.getInstance(signingFor); + } + break; + default: + throw new IllegalArgumentException("Bad tag number: " + o.getTagNo()); + } + } + } + + /** + * Constructor from a given details. + * + * + * Either
generalName
or certRef
MUST be
+ * null
.
+ *
+ * @param country The country code whose laws apply.
+ * @param typeOfSubstitution The type of procuration.
+ * @param certRef Reference to certificate of the person who is represented.
+ */
+ public ProcurationSyntax(
+ String country,
+ DirectoryString typeOfSubstitution,
+ IssuerSerial certRef)
+ {
+ this.country = country;
+ this.typeOfSubstitution = typeOfSubstitution;
+ this.thirdPerson = null;
+ this.certRef = certRef;
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ *
+ * Either generalName
or certRef
MUST be
+ * null
.
+ *
+ * @param country The country code whose laws apply.
+ * @param typeOfSubstitution The type of procuration.
+ * @param thirdPerson The GeneralName of the person who is represented.
+ */
+ public ProcurationSyntax(
+ String country,
+ DirectoryString typeOfSubstitution,
+ GeneralName thirdPerson)
+ {
+ this.country = country;
+ this.typeOfSubstitution = typeOfSubstitution;
+ this.thirdPerson = thirdPerson;
+ this.certRef = null;
+ }
+
+ public String getCountry()
+ {
+ return country;
+ }
+
+ public DirectoryString getTypeOfSubstitution()
+ {
+ return typeOfSubstitution;
+ }
+
+ public GeneralName getThirdPerson()
+ {
+ return thirdPerson;
+ }
+
+ public IssuerSerial getCertRef()
+ {
+ return certRef;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * + * ProcurationSyntax ::= SEQUENCE { + * country [1] EXPLICIT PrintableString(SIZE(2)) OPTIONAL, + * typeOfSubstitution [2] EXPLICIT DirectoryString (SIZE(1..128)) OPTIONAL, + * signingFor [3] EXPLICIT SigningFor + * } + * + * SigningFor ::= CHOICE + * { + * thirdPerson GeneralName, + * certRef IssuerSerial + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + if (country != null) + { + vec.add(new DERTaggedObject(true, 1, new DERPrintableString(country, true))); + } + if (typeOfSubstitution != null) + { + vec.add(new DERTaggedObject(true, 2, typeOfSubstitution)); + } + if (thirdPerson != null) + { + vec.add(new DERTaggedObject(true, 3, thirdPerson)); + } + else + { + vec.add(new DERTaggedObject(true, 3, certRef)); + } + + return new DERSequence(vec); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/ProfessionInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/ProfessionInfo.java new file mode 100644 index 000000000..cf91505ed --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/ProfessionInfo.java @@ -0,0 +1,408 @@ +package org.spongycastle.asn1.isismtt.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Professions, specializations, disciplines, fields of activity, etc. + * + *
+ * ProfessionInfo ::= SEQUENCE + * { + * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL, + * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)), + * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL, + * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL, + * addProfessionInfo OCTET STRING OPTIONAL + * } + *+ * + * @see org.spongycastle.asn1.isismtt.x509.AdmissionSyntax + */ +public class ProfessionInfo + extends ASN1Object +{ + + /** + * Rechtsanwïżœltin + */ + public static final ASN1ObjectIdentifier Rechtsanwltin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".1"); + + /** + * Rechtsanwalt + */ + public static final ASN1ObjectIdentifier Rechtsanwalt = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".2"); + + /** + * Rechtsbeistand + */ + public static final ASN1ObjectIdentifier Rechtsbeistand = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".3"); + + /** + * Steuerberaterin + */ + public static final ASN1ObjectIdentifier Steuerberaterin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".4"); + + /** + * Steuerberater + */ + public static final ASN1ObjectIdentifier Steuerberater = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".5"); + + /** + * Steuerbevollmïżœchtigte + */ + public static final ASN1ObjectIdentifier Steuerbevollmchtigte = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".6"); + + /** + * Steuerbevollmïżœchtigter + */ + public static final ASN1ObjectIdentifier Steuerbevollmchtigter = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".7"); + + /** + * Notarin + */ + public static final ASN1ObjectIdentifier Notarin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".8"); + + /** + * Notar + */ + public static final ASN1ObjectIdentifier Notar = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".9"); + + /** + * Notarvertreterin + */ + public static final ASN1ObjectIdentifier Notarvertreterin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".10"); + + /** + * Notarvertreter + */ + public static final ASN1ObjectIdentifier Notarvertreter = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".11"); + + /** + * Notariatsverwalterin + */ + public static final ASN1ObjectIdentifier Notariatsverwalterin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".12"); + + /** + * Notariatsverwalter + */ + public static final ASN1ObjectIdentifier Notariatsverwalter = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".13"); + + /** + * Wirtschaftsprïżœferin + */ + public static final ASN1ObjectIdentifier Wirtschaftsprferin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".14"); + + /** + * Wirtschaftsprïżœfer + */ + public static final ASN1ObjectIdentifier Wirtschaftsprfer = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".15"); + + /** + * Vereidigte Buchprïżœferin + */ + public static final ASN1ObjectIdentifier VereidigteBuchprferin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".16"); + + /** + * Vereidigter Buchprïżœfer + */ + public static final ASN1ObjectIdentifier VereidigterBuchprfer = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".17"); + + /** + * Patentanwïżœltin + */ + public static final ASN1ObjectIdentifier Patentanwltin = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".18"); + + /** + * Patentanwalt + */ + public static final ASN1ObjectIdentifier Patentanwalt = new ASN1ObjectIdentifier( + NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".19"); + + private NamingAuthority namingAuthority; + + private ASN1Sequence professionItems; + + private ASN1Sequence professionOIDs; + + private String registrationNumber; + + private ASN1OctetString addProfessionInfo; + + public static ProfessionInfo getInstance(Object obj) + { + if (obj == null || obj instanceof ProfessionInfo) + { + return (ProfessionInfo)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new ProfessionInfo((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * + *
+ * ProfessionInfo ::= SEQUENCE + * { + * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL, + * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)), + * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL, + * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL, + * addProfessionInfo OCTET STRING OPTIONAL + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private ProfessionInfo(ASN1Sequence seq) + { + if (seq.size() > 5) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + ASN1Encodable o = (ASN1Encodable)e.nextElement(); + + if (o instanceof ASN1TaggedObject) + { + if (((ASN1TaggedObject)o).getTagNo() != 0) + { + throw new IllegalArgumentException("Bad tag number: " + + ((ASN1TaggedObject)o).getTagNo()); + } + namingAuthority = NamingAuthority.getInstance((ASN1TaggedObject)o, true); + o = (ASN1Encodable)e.nextElement(); + } + + professionItems = ASN1Sequence.getInstance(o); + + if (e.hasMoreElements()) + { + o = (ASN1Encodable)e.nextElement(); + if (o instanceof ASN1Sequence) + { + professionOIDs = ASN1Sequence.getInstance(o); + } + else if (o instanceof DERPrintableString) + { + registrationNumber = DERPrintableString.getInstance(o).getString(); + } + else if (o instanceof ASN1OctetString) + { + addProfessionInfo = ASN1OctetString.getInstance(o); + } + else + { + throw new IllegalArgumentException("Bad object encountered: " + + o.getClass()); + } + } + if (e.hasMoreElements()) + { + o = (ASN1Encodable)e.nextElement(); + if (o instanceof DERPrintableString) + { + registrationNumber = DERPrintableString.getInstance(o).getString(); + } + else if (o instanceof DEROctetString) + { + addProfessionInfo = (DEROctetString)o; + } + else + { + throw new IllegalArgumentException("Bad object encountered: " + + o.getClass()); + } + } + if (e.hasMoreElements()) + { + o = (ASN1Encodable)e.nextElement(); + if (o instanceof DEROctetString) + { + addProfessionInfo = (DEROctetString)o; + } + else + { + throw new IllegalArgumentException("Bad object encountered: " + + o.getClass()); + } + } + + } + + /** + * Constructor from given details. + * + *
professionItems
is mandatory, all other parameters are
+ * optional.
+ *
+ * @param namingAuthority The naming authority.
+ * @param professionItems Directory strings of the profession.
+ * @param professionOIDs DERObjectIdentfier objects for the
+ * profession.
+ * @param registrationNumber Registration number.
+ * @param addProfessionInfo Additional infos in encoded form.
+ */
+ public ProfessionInfo(NamingAuthority namingAuthority,
+ DirectoryString[] professionItems, ASN1ObjectIdentifier[] professionOIDs,
+ String registrationNumber, ASN1OctetString addProfessionInfo)
+ {
+ this.namingAuthority = namingAuthority;
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i != professionItems.length; i++)
+ {
+ v.add(professionItems[i]);
+ }
+ this.professionItems = new DERSequence(v);
+ if (professionOIDs != null)
+ {
+ v = new ASN1EncodableVector();
+ for (int i = 0; i != professionOIDs.length; i++)
+ {
+ v.add(professionOIDs[i]);
+ }
+ this.professionOIDs = new DERSequence(v);
+ }
+ this.registrationNumber = registrationNumber;
+ this.addProfessionInfo = addProfessionInfo;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * + * ProfessionInfo ::= SEQUENCE + * { + * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL, + * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)), + * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL, + * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL, + * addProfessionInfo OCTET STRING OPTIONAL + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + if (namingAuthority != null) + { + vec.add(new DERTaggedObject(true, 0, namingAuthority)); + } + vec.add(professionItems); + if (professionOIDs != null) + { + vec.add(professionOIDs); + } + if (registrationNumber != null) + { + vec.add(new DERPrintableString(registrationNumber, true)); + } + if (addProfessionInfo != null) + { + vec.add(addProfessionInfo); + } + return new DERSequence(vec); + } + + /** + * @return Returns the addProfessionInfo. + */ + public ASN1OctetString getAddProfessionInfo() + { + return addProfessionInfo; + } + + /** + * @return Returns the namingAuthority. + */ + public NamingAuthority getNamingAuthority() + { + return namingAuthority; + } + + /** + * @return Returns the professionItems. + */ + public DirectoryString[] getProfessionItems() + { + DirectoryString[] items = new DirectoryString[professionItems.size()]; + int count = 0; + for (Enumeration e = professionItems.getObjects(); e.hasMoreElements();) + { + items[count++] = DirectoryString.getInstance(e.nextElement()); + } + return items; + } + + /** + * @return Returns the professionOIDs. + */ + public ASN1ObjectIdentifier[] getProfessionOIDs() + { + if (professionOIDs == null) + { + return new ASN1ObjectIdentifier[0]; + } + ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[professionOIDs.size()]; + int count = 0; + for (Enumeration e = professionOIDs.getObjects(); e.hasMoreElements();) + { + oids[count++] = ASN1ObjectIdentifier.getInstance(e.nextElement()); + } + return oids; + } + + /** + * @return Returns the registrationNumber. + */ + public String getRegistrationNumber() + { + return registrationNumber; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/Restriction.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/Restriction.java new file mode 100644 index 000000000..4f296ecf5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/isismtt/x509/Restriction.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.isismtt.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Some other restriction regarding the usage of this certificate. + * + *
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + *+ */ +public class Restriction + extends ASN1Object +{ + private DirectoryString restriction; + + public static Restriction getInstance(Object obj) + { + if (obj instanceof Restriction) + { + return (Restriction)obj; + } + + if (obj != null) + { + return new Restriction(DirectoryString.getInstance(obj)); + } + + return null; + } + + /** + * Constructor from DirectoryString. + * + * The DirectoryString is of type RestrictionSyntax: + * + *
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + *+ * + * @param restriction A DirectoryString. + */ + private Restriction(DirectoryString restriction) + { + this.restriction = restriction; + } + + /** + * Constructor from a given details. + * + * @param restriction The describtion of the restriction. + */ + public Restriction(String restriction) + { + this.restriction = new DirectoryString(restriction); + } + + public DirectoryString getRestriction() + { + return restriction; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + * + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + return restriction.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/kisa/KISAObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/kisa/KISAObjectIdentifiers.java new file mode 100644 index 000000000..986b2a0a1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/kisa/KISAObjectIdentifiers.java @@ -0,0 +1,31 @@ +package org.spongycastle.asn1.kisa; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * Korea Information Security Agency (KISA) + * ({iso(1) member-body(2) kr(410) kisa(200004)}) + *
+ * See RFC 4010 + * Use of the SEED Encryption Algorithm + * in Cryptographic Message Syntax (CMS), + * and RFC 4269 + * The SEED Encryption Algorithm + */ +public interface KISAObjectIdentifiers +{ + /** RFC 4010, 4269: id-seedCBC; OID 1.2.410.200004.1.4 */ + static final ASN1ObjectIdentifier id_seedCBC = new ASN1ObjectIdentifier("1.2.410.200004.1.4"); + + /** RFC 4269: id-seedMAC; OID 1.2.410.200004.1.7 */ + static final ASN1ObjectIdentifier id_seedMAC = new ASN1ObjectIdentifier("1.2.410.200004.1.7"); + + /** RFC 4269: pbeWithSHA1AndSEED-CBC; OID 1.2.410.200004.1.15 */ + static final ASN1ObjectIdentifier pbeWithSHA1AndSEED_CBC = new ASN1ObjectIdentifier("1.2.410.200004.1.15"); + + /** RFC 4010: id-npki-app-cmsSeed-wrap; OID 1.2.410.200004.7.1.1.1 */ + static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1"); + + /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */ + static final ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java new file mode 100644 index 000000000..44b5653e4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java @@ -0,0 +1,28 @@ +package org.spongycastle.asn1.microsoft; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * Microsoft + *
+ * Object identifier base: + *
+ * iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311) + *+ * 1.3.6.1.4.1.311 + */ +public interface MicrosoftObjectIdentifiers +{ + /** Base OID: 1.3.6.1.4.1.311 */ + static final ASN1ObjectIdentifier microsoft = new ASN1ObjectIdentifier("1.3.6.1.4.1.311"); + /** OID: 1.3.6.1.4.1.311.20.2 */ + static final ASN1ObjectIdentifier microsoftCertTemplateV1 = microsoft.branch("20.2"); + /** OID: 1.3.6.1.4.1.311.21.1 */ + static final ASN1ObjectIdentifier microsoftCaVersion = microsoft.branch("21.1"); + /** OID: 1.3.6.1.4.1.311.21.2 */ + static final ASN1ObjectIdentifier microsoftPrevCaCertHash = microsoft.branch("21.2"); + /** OID: 1.3.6.1.4.1.311.21.7 */ + static final ASN1ObjectIdentifier microsoftCertTemplateV2 = microsoft.branch("21.7"); + /** OID: 1.3.6.1.4.1.311.21.10 */ + static final ASN1ObjectIdentifier microsoftAppPolicies = microsoft.branch("21.10"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/CAST5CBCParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/CAST5CBCParameters.java new file mode 100644 index 000000000..b611ccd6b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/CAST5CBCParameters.java @@ -0,0 +1,78 @@ +package org.spongycastle.asn1.misc; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class CAST5CBCParameters + extends ASN1Object +{ + ASN1Integer keyLength; + ASN1OctetString iv; + + public static CAST5CBCParameters getInstance( + Object o) + { + if (o instanceof CAST5CBCParameters) + { + return (CAST5CBCParameters)o; + } + else if (o != null) + { + return new CAST5CBCParameters(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CAST5CBCParameters( + byte[] iv, + int keyLength) + { + this.iv = new DEROctetString(iv); + this.keyLength = new ASN1Integer(keyLength); + } + + public CAST5CBCParameters( + ASN1Sequence seq) + { + iv = (ASN1OctetString)seq.getObjectAt(0); + keyLength = (ASN1Integer)seq.getObjectAt(1); + } + + public byte[] getIV() + { + return iv.getOctets(); + } + + public int getKeyLength() + { + return keyLength.getValue().intValue(); + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * cast5CBCParameters ::= SEQUENCE { + * iv OCTET STRING DEFAULT 0, + * -- Initialization vector + * keyLength INTEGER + * -- Key length, in bits + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(iv); + v.add(keyLength); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/IDEACBCPar.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/IDEACBCPar.java new file mode 100644 index 000000000..cb6ab3c0b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/IDEACBCPar.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.misc; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class IDEACBCPar + extends ASN1Object +{ + ASN1OctetString iv; + + public static IDEACBCPar getInstance( + Object o) + { + if (o instanceof IDEACBCPar) + { + return (IDEACBCPar)o; + } + else if (o != null) + { + return new IDEACBCPar(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public IDEACBCPar( + byte[] iv) + { + this.iv = new DEROctetString(iv); + } + + public IDEACBCPar( + ASN1Sequence seq) + { + if (seq.size() == 1) + { + iv = (ASN1OctetString)seq.getObjectAt(0); + } + else + { + iv = null; + } + } + + public byte[] getIV() + { + if (iv != null) + { + return iv.getOctets(); + } + else + { + return null; + } + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * IDEA-CBCPar ::= SEQUENCE { + * iv OCTET STRING OPTIONAL -- exactly 8 octets + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (iv != null) + { + v.add(iv); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/MiscObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/MiscObjectIdentifiers.java new file mode 100644 index 000000000..318da9a3d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/MiscObjectIdentifiers.java @@ -0,0 +1,59 @@ +package org.spongycastle.asn1.misc; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public interface MiscObjectIdentifiers +{ + // + // Netscape + // iso/itu(2) joint-assign(16) us(840) uscompany(1) netscape(113730) cert-extensions(1) } + // + /** Netscape cert extensions OID base: 2.16.840.1.113730.1 */ + static final ASN1ObjectIdentifier netscape = new ASN1ObjectIdentifier("2.16.840.1.113730.1"); + /** Netscape cert CertType OID: 2.16.840.1.113730.1.1 */ + static final ASN1ObjectIdentifier netscapeCertType = netscape.branch("1"); + /** Netscape cert BaseURL OID: 2.16.840.1.113730.1.2 */ + static final ASN1ObjectIdentifier netscapeBaseURL = netscape.branch("2"); + /** Netscape cert RevocationURL OID: 2.16.840.1.113730.1.3 */ + static final ASN1ObjectIdentifier netscapeRevocationURL = netscape.branch("3"); + /** Netscape cert CARevocationURL OID: 2.16.840.1.113730.1.4 */ + static final ASN1ObjectIdentifier netscapeCARevocationURL = netscape.branch("4"); + /** Netscape cert RenewalURL OID: 2.16.840.1.113730.1.7 */ + static final ASN1ObjectIdentifier netscapeRenewalURL = netscape.branch("7"); + /** Netscape cert CApolicyURL OID: 2.16.840.1.113730.1.8 */ + static final ASN1ObjectIdentifier netscapeCApolicyURL = netscape.branch("8"); + /** Netscape cert SSLServerName OID: 2.16.840.1.113730.1.12 */ + static final ASN1ObjectIdentifier netscapeSSLServerName = netscape.branch("12"); + /** Netscape cert CertComment OID: 2.16.840.1.113730.1.13 */ + static final ASN1ObjectIdentifier netscapeCertComment = netscape.branch("13"); + + // + // Verisign + // iso/itu(2) joint-assign(16) us(840) uscompany(1) verisign(113733) cert-extensions(1) } + // + /** Verisign OID base: 2.16.840.1.113733.1 */ + static final ASN1ObjectIdentifier verisign = new ASN1ObjectIdentifier("2.16.840.1.113733.1"); + + /** Verisign CZAG (Country,Zip,Age,Gender) Extension OID: 2.16.840.1.113733.1.6.3 */ + static final ASN1ObjectIdentifier verisignCzagExtension = verisign.branch("6.3"); + /** Verisign D&B D-U-N-S number Extension OID: 2.16.840.1.113733.1.6.15 */ + static final ASN1ObjectIdentifier verisignDnbDunsNumber = verisign.branch("6.15"); + + // + // Novell + // iso/itu(2) country(16) us(840) organization(1) novell(113719) + // + /** Novell OID base: 2.16.840.1.113719 */ + static final ASN1ObjectIdentifier novell = new ASN1ObjectIdentifier("2.16.840.1.113719"); + /** Novell SecurityAttribs OID: 2.16.840.1.113719.1.9.4.1 */ + static final ASN1ObjectIdentifier novellSecurityAttribs = novell.branch("1.9.4.1"); + + // + // Entrust + // iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7) + // + /** NortelNetworks Entrust OID base: 1.2.840.113533.7 */ + static final ASN1ObjectIdentifier entrust = new ASN1ObjectIdentifier("1.2.840.113533.7"); + /** NortelNetworks Entrust VersionExtension OID: 1.2.840.113533.7.65.0 */ + static final ASN1ObjectIdentifier entrustVersionExtension = entrust.branch("65.0"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/NetscapeCertType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/NetscapeCertType.java new file mode 100644 index 000000000..03fdf2317 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/NetscapeCertType.java @@ -0,0 +1,54 @@ +package org.spongycastle.asn1.misc; + +import org.spongycastle.asn1.DERBitString; + +/** + * The NetscapeCertType object. + *
+ * NetscapeCertType ::= BIT STRING { + * SSLClient (0), + * SSLServer (1), + * S/MIME (2), + * Object Signing (3), + * Reserved (4), + * SSL CA (5), + * S/MIME CA (6), + * Object Signing CA (7) } + *+ */ +public class NetscapeCertType + extends DERBitString +{ + public static final int sslClient = (1 << 7); + public static final int sslServer = (1 << 6); + public static final int smime = (1 << 5); + public static final int objectSigning = (1 << 4); + public static final int reserved = (1 << 3); + public static final int sslCA = (1 << 2); + public static final int smimeCA = (1 << 1); + public static final int objectSigningCA = (1 << 0); + + /** + * Basic constructor. + * + * @param usage - the bitwise OR of the Key Usage flags giving the + * allowed uses for the key. + * e.g. (X509NetscapeCertType.sslCA | X509NetscapeCertType.smimeCA) + */ + public NetscapeCertType( + int usage) + { + super(getBytes(usage), getPadBits(usage)); + } + + public NetscapeCertType( + DERBitString usage) + { + super(usage.getBytes(), usage.getPadBits()); + } + + public String toString() + { + return "NetscapeCertType: 0x" + Integer.toHexString(data[0] & 0xff); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/NetscapeRevocationURL.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/NetscapeRevocationURL.java new file mode 100644 index 000000000..2ae83774c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/NetscapeRevocationURL.java @@ -0,0 +1,18 @@ +package org.spongycastle.asn1.misc; + +import org.spongycastle.asn1.DERIA5String; + +public class NetscapeRevocationURL + extends DERIA5String +{ + public NetscapeRevocationURL( + DERIA5String str) + { + super(str.getString()); + } + + public String toString() + { + return "NetscapeRevocationURL: " + this.getString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/VerisignCzagExtension.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/VerisignCzagExtension.java new file mode 100644 index 000000000..810b16088 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/misc/VerisignCzagExtension.java @@ -0,0 +1,18 @@ +package org.spongycastle.asn1.misc; + +import org.spongycastle.asn1.DERIA5String; + +public class VerisignCzagExtension + extends DERIA5String +{ + public VerisignCzagExtension( + DERIA5String str) + { + super(str.getString()); + } + + public String toString() + { + return "VerisignCzagExtension: " + this.getString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/mozilla/PublicKeyAndChallenge.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/mozilla/PublicKeyAndChallenge.java new file mode 100644 index 000000000..93902fa85 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/mozilla/PublicKeyAndChallenge.java @@ -0,0 +1,63 @@ +package org.spongycastle.asn1.mozilla; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; + +/** + * This is designed to parse + * the PublicKeyAndChallenge created by the KEYGEN tag included by + * Mozilla based browsers. + *
+ * PublicKeyAndChallenge ::= SEQUENCE { + * spki SubjectPublicKeyInfo, + * challenge IA5STRING + * } + * + *+ */ +public class PublicKeyAndChallenge + extends ASN1Object +{ + private ASN1Sequence pkacSeq; + private SubjectPublicKeyInfo spki; + private DERIA5String challenge; + + public static PublicKeyAndChallenge getInstance(Object obj) + { + if (obj instanceof PublicKeyAndChallenge) + { + return (PublicKeyAndChallenge)obj; + } + else if (obj != null) + { + return new PublicKeyAndChallenge(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private PublicKeyAndChallenge(ASN1Sequence seq) + { + pkacSeq = seq; + spki = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(0)); + challenge = DERIA5String.getInstance(seq.getObjectAt(1)); + } + + public ASN1Primitive toASN1Primitive() + { + return pkacSeq; + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return spki; + } + + public DERIA5String getChallenge() + { + return challenge; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/nist/NISTNamedCurves.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/nist/NISTNamedCurves.java new file mode 100644 index 000000000..9ddc6a3ad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/nist/NISTNamedCurves.java @@ -0,0 +1,99 @@ +package org.spongycastle.asn1.nist; + +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.sec.SECObjectIdentifiers; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.util.Strings; + +/** + * Utility class for fetching curves using their NIST names as published in FIPS-PUB 186-3 + */ +public class NISTNamedCurves +{ + static final Hashtable objIds = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static void defineCurve(String name, ASN1ObjectIdentifier oid) + { + objIds.put(name, oid); + names.put(oid, name); + } + + static + { + defineCurve("B-571", SECObjectIdentifiers.sect571r1); + defineCurve("B-409", SECObjectIdentifiers.sect409r1); + defineCurve("B-283", SECObjectIdentifiers.sect283r1); + defineCurve("B-233", SECObjectIdentifiers.sect233r1); + defineCurve("B-163", SECObjectIdentifiers.sect163r2); + defineCurve("K-571", SECObjectIdentifiers.sect571k1); + defineCurve("K-409", SECObjectIdentifiers.sect409k1); + defineCurve("K-283", SECObjectIdentifiers.sect283k1); + defineCurve("K-233", SECObjectIdentifiers.sect233k1); + defineCurve("K-163", SECObjectIdentifiers.sect163k1); + defineCurve("P-521", SECObjectIdentifiers.secp521r1); + defineCurve("P-384", SECObjectIdentifiers.secp384r1); + defineCurve("P-256", SECObjectIdentifiers.secp256r1); + defineCurve("P-224", SECObjectIdentifiers.secp224r1); + defineCurve("P-192", SECObjectIdentifiers.secp192r1); + } + + public static X9ECParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name)); + + if (oid != null) + { + return getByOID(oid); + } + + return null; + } + + /** + * return the X9ECParameters object for the named curve represented by + * the passed in object identifier. Null if the curve isn't present. + * + * @param oid an object identifier representing a named curve, if present. + */ + public static X9ECParameters getByOID( + ASN1ObjectIdentifier oid) + { + return SECNamedCurves.getByOID(oid); + } + + /** + * return the object identifier signified by the passed in name. Null + * if there is no object identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static ASN1ObjectIdentifier getOID( + String name) + { + return (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name)); + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName( + ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/nist/NISTObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/nist/NISTObjectIdentifiers.java new file mode 100644 index 000000000..a488008d7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/nist/NISTObjectIdentifiers.java @@ -0,0 +1,96 @@ +package org.spongycastle.asn1.nist; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * + * NIST: + * iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3) + */ +public interface NISTObjectIdentifiers +{ + // + // nistalgorithms(4) + // + /** 2.16.840.1.101.3.4 -- algorithms */ + static final ASN1ObjectIdentifier nistAlgorithm = new ASN1ObjectIdentifier("2.16.840.1.101.3.4"); + + /** 2.16.840.1.101.3.4.2 */ + static final ASN1ObjectIdentifier hashAlgs = nistAlgorithm.branch("2"); + + /** 2.16.840.1.101.3.4.2.1 */ + static final ASN1ObjectIdentifier id_sha256 = hashAlgs.branch("1"); + /** 2.16.840.1.101.3.4.2.2 */ + static final ASN1ObjectIdentifier id_sha384 = hashAlgs.branch("2"); + /** 2.16.840.1.101.3.4.2.3 */ + static final ASN1ObjectIdentifier id_sha512 = hashAlgs.branch("3"); + /** 2.16.840.1.101.3.4.2.4 */ + static final ASN1ObjectIdentifier id_sha224 = hashAlgs.branch("4"); + /** 2.16.840.1.101.3.4.2.5 */ + static final ASN1ObjectIdentifier id_sha512_224 = hashAlgs.branch("5"); + /** 2.16.840.1.101.3.4.2.6 */ + static final ASN1ObjectIdentifier id_sha512_256 = hashAlgs.branch("6"); + + /** 2.16.840.1.101.3.4.1 */ + static final ASN1ObjectIdentifier aes = nistAlgorithm.branch("1"); + + /** 2.16.840.1.101.3.4.1.1 */ + static final ASN1ObjectIdentifier id_aes128_ECB = aes.branch("1"); + /** 2.16.840.1.101.3.4.1.2 */ + static final ASN1ObjectIdentifier id_aes128_CBC = aes.branch("2"); + /** 2.16.840.1.101.3.4.1.3 */ + static final ASN1ObjectIdentifier id_aes128_OFB = aes.branch("3"); + /** 2.16.840.1.101.3.4.1.4 */ + static final ASN1ObjectIdentifier id_aes128_CFB = aes.branch("4"); + /** 2.16.840.1.101.3.4.1.5 */ + static final ASN1ObjectIdentifier id_aes128_wrap = aes.branch("5"); + /** 2.16.840.1.101.3.4.1.6 */ + static final ASN1ObjectIdentifier id_aes128_GCM = aes.branch("6"); + /** 2.16.840.1.101.3.4.1.7 */ + static final ASN1ObjectIdentifier id_aes128_CCM = aes.branch("7"); + + /** 2.16.840.1.101.3.4.1.21 */ + static final ASN1ObjectIdentifier id_aes192_ECB = aes.branch("21"); + /** 2.16.840.1.101.3.4.1.22 */ + static final ASN1ObjectIdentifier id_aes192_CBC = aes.branch("22"); + /** 2.16.840.1.101.3.4.1.23 */ + static final ASN1ObjectIdentifier id_aes192_OFB = aes.branch("23"); + /** 2.16.840.1.101.3.4.1.24 */ + static final ASN1ObjectIdentifier id_aes192_CFB = aes.branch("24"); + /** 2.16.840.1.101.3.4.1.25 */ + static final ASN1ObjectIdentifier id_aes192_wrap = aes.branch("25"); + /** 2.16.840.1.101.3.4.1.26 */ + static final ASN1ObjectIdentifier id_aes192_GCM = aes.branch("26"); + /** 2.16.840.1.101.3.4.1.27 */ + static final ASN1ObjectIdentifier id_aes192_CCM = aes.branch("27"); + + /** 2.16.840.1.101.3.4.1.41 */ + static final ASN1ObjectIdentifier id_aes256_ECB = aes.branch("41"); + /** 2.16.840.1.101.3.4.1.42 */ + static final ASN1ObjectIdentifier id_aes256_CBC = aes.branch("42"); + /** 2.16.840.1.101.3.4.1.43 */ + static final ASN1ObjectIdentifier id_aes256_OFB = aes.branch("43"); + /** 2.16.840.1.101.3.4.1.44 */ + static final ASN1ObjectIdentifier id_aes256_CFB = aes.branch("44"); + /** 2.16.840.1.101.3.4.1.45 */ + static final ASN1ObjectIdentifier id_aes256_wrap = aes.branch("45"); + /** 2.16.840.1.101.3.4.1.46 */ + static final ASN1ObjectIdentifier id_aes256_GCM = aes.branch("46"); + /** 2.16.840.1.101.3.4.1.47 */ + static final ASN1ObjectIdentifier id_aes256_CCM = aes.branch("47"); + + // + // signatures + // + /** 2.16.840.1.101.3.4.3 */ + static final ASN1ObjectIdentifier id_dsa_with_sha2 = nistAlgorithm.branch("3"); + + /** 2.16.840.1.101.3.4.3.1 */ + static final ASN1ObjectIdentifier dsa_with_sha224 = id_dsa_with_sha2.branch("1"); + /** 2.16.840.1.101.3.4.3.2 */ + static final ASN1ObjectIdentifier dsa_with_sha256 = id_dsa_with_sha2.branch("2"); + /** 2.16.840.1.101.3.4.3.3 */ + static final ASN1ObjectIdentifier dsa_with_sha384 = id_dsa_with_sha2.branch("3"); + /** 2.16.840.1.101.3.4.3.4 */ + static final ASN1ObjectIdentifier dsa_with_sha512 = id_dsa_with_sha2.branch("4"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ntt/NTTObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ntt/NTTObjectIdentifiers.java new file mode 100644 index 000000000..3eced4233 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ntt/NTTObjectIdentifiers.java @@ -0,0 +1,25 @@ +package org.spongycastle.asn1.ntt; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * From RFC 3657 + * Use of the Camellia Encryption Algorithm + * in Cryptographic Message Syntax (CMS) + */ +public interface NTTObjectIdentifiers +{ + /** id-camellia128-cbc; OID 1.2.392.200011.61.1.1.1.2 */ + static final ASN1ObjectIdentifier id_camellia128_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.2"); + /** id-camellia192-cbc; OID 1.2.392.200011.61.1.1.1.3 */ + static final ASN1ObjectIdentifier id_camellia192_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.3"); + /** id-camellia256-cbc; OID 1.2.392.200011.61.1.1.1.4 */ + static final ASN1ObjectIdentifier id_camellia256_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.4"); + + /** id-camellia128-wrap; OID 1.2.392.200011.61.1.1.3.2 */ + static final ASN1ObjectIdentifier id_camellia128_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.2"); + /** id-camellia192-wrap; OID 1.2.392.200011.61.1.1.3.3 */ + static final ASN1ObjectIdentifier id_camellia192_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.3"); + /** id-camellia256-wrap; OID 1.2.392.200011.61.1.1.3.4 */ + static final ASN1ObjectIdentifier id_camellia256_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.4"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/BasicOCSPResponse.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/BasicOCSPResponse.java new file mode 100644 index 000000000..9fa235e9f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/BasicOCSPResponse.java @@ -0,0 +1,112 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class BasicOCSPResponse + extends ASN1Object +{ + private ResponseData tbsResponseData; + private AlgorithmIdentifier signatureAlgorithm; + private DERBitString signature; + private ASN1Sequence certs; + + public BasicOCSPResponse( + ResponseData tbsResponseData, + AlgorithmIdentifier signatureAlgorithm, + DERBitString signature, + ASN1Sequence certs) + { + this.tbsResponseData = tbsResponseData; + this.signatureAlgorithm = signatureAlgorithm; + this.signature = signature; + this.certs = certs; + } + + private BasicOCSPResponse( + ASN1Sequence seq) + { + this.tbsResponseData = ResponseData.getInstance(seq.getObjectAt(0)); + this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + this.signature = (DERBitString)seq.getObjectAt(2); + + if (seq.size() > 3) + { + this.certs = ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(3), true); + } + } + + public static BasicOCSPResponse getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static BasicOCSPResponse getInstance( + Object obj) + { + if (obj instanceof BasicOCSPResponse) + { + return (BasicOCSPResponse)obj; + } + else if (obj != null) + { + return new BasicOCSPResponse(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ResponseData getTbsResponseData() + { + return tbsResponseData; + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return signatureAlgorithm; + } + + public DERBitString getSignature() + { + return signature; + } + + public ASN1Sequence getCerts() + { + return certs; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(tbsResponseData); + v.add(signatureAlgorithm); + v.add(signature); + if (certs != null) + { + v.add(new DERTaggedObject(true, 0, certs)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CertID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CertID.java new file mode 100644 index 000000000..35a13b07c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CertID.java @@ -0,0 +1,105 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class CertID + extends ASN1Object +{ + AlgorithmIdentifier hashAlgorithm; + ASN1OctetString issuerNameHash; + ASN1OctetString issuerKeyHash; + ASN1Integer serialNumber; + + public CertID( + AlgorithmIdentifier hashAlgorithm, + ASN1OctetString issuerNameHash, + ASN1OctetString issuerKeyHash, + ASN1Integer serialNumber) + { + this.hashAlgorithm = hashAlgorithm; + this.issuerNameHash = issuerNameHash; + this.issuerKeyHash = issuerKeyHash; + this.serialNumber = serialNumber; + } + + private CertID( + ASN1Sequence seq) + { + hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + issuerNameHash = (ASN1OctetString)seq.getObjectAt(1); + issuerKeyHash = (ASN1OctetString)seq.getObjectAt(2); + serialNumber = (ASN1Integer)seq.getObjectAt(3); + } + + public static CertID getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static CertID getInstance( + Object obj) + { + if (obj instanceof CertID) + { + return (CertID)obj; + } + else if (obj != null) + { + return new CertID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public ASN1OctetString getIssuerNameHash() + { + return issuerNameHash; + } + + public ASN1OctetString getIssuerKeyHash() + { + return issuerKeyHash; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(hashAlgorithm); + v.add(issuerNameHash); + v.add(issuerKeyHash); + v.add(serialNumber); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CertStatus.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CertStatus.java new file mode 100644 index 000000000..d0f508c4d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CertStatus.java @@ -0,0 +1,105 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DERTaggedObject; + +public class CertStatus + extends ASN1Object + implements ASN1Choice +{ + private int tagNo; + private ASN1Encodable value; + + /** + * create a CertStatus object with a tag of zero. + */ + public CertStatus() + { + tagNo = 0; + value = DERNull.INSTANCE; + } + + public CertStatus( + RevokedInfo info) + { + tagNo = 1; + value = info; + } + + public CertStatus( + int tagNo, + ASN1Encodable value) + { + this.tagNo = tagNo; + this.value = value; + } + + public CertStatus( + ASN1TaggedObject choice) + { + this.tagNo = choice.getTagNo(); + + switch (choice.getTagNo()) + { + case 0: + value = DERNull.INSTANCE; + break; + case 1: + value = RevokedInfo.getInstance(choice, false); + break; + case 2: + value = DERNull.INSTANCE; + } + } + + public static CertStatus getInstance( + Object obj) + { + if (obj == null || obj instanceof CertStatus) + { + return (CertStatus)obj; + } + else if (obj instanceof ASN1TaggedObject) + { + return new CertStatus((ASN1TaggedObject)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public static CertStatus getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + public int getTagNo() + { + return tagNo; + } + + public ASN1Encodable getStatus() + { + return value; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return new DERTaggedObject(false, tagNo, value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CrlID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CrlID.java new file mode 100644 index 000000000..5dead09e3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/CrlID.java @@ -0,0 +1,111 @@ +package org.spongycastle.asn1.ocsp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class CrlID + extends ASN1Object +{ + private DERIA5String crlUrl; + private ASN1Integer crlNum; + private ASN1GeneralizedTime crlTime; + + private CrlID( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1TaggedObject o = (ASN1TaggedObject)e.nextElement(); + + switch (o.getTagNo()) + { + case 0: + crlUrl = DERIA5String.getInstance(o, true); + break; + case 1: + crlNum = ASN1Integer.getInstance(o, true); + break; + case 2: + crlTime = DERGeneralizedTime.getInstance(o, true); + break; + default: + throw new IllegalArgumentException( + "unknown tag number: " + o.getTagNo()); + } + } + } + + public static CrlID getInstance( + Object obj) + { + if (obj instanceof CrlID) + { + return (CrlID)obj; + } + else if (obj != null) + { + return new CrlID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public DERIA5String getCrlUrl() + { + return crlUrl; + } + + public ASN1Integer getCrlNum() + { + return crlNum; + } + + public ASN1GeneralizedTime getCrlTime() + { + return crlTime; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * CrlID ::= SEQUENCE { + * crlUrl [0] EXPLICIT IA5String OPTIONAL, + * crlNum [1] EXPLICIT INTEGER OPTIONAL, + * crlTime [2] EXPLICIT GeneralizedTime OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (crlUrl != null) + { + v.add(new DERTaggedObject(true, 0, crlUrl)); + } + + if (crlNum != null) + { + v.add(new DERTaggedObject(true, 1, crlNum)); + } + + if (crlTime != null) + { + v.add(new DERTaggedObject(true, 2, crlTime)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPObjectIdentifiers.java new file mode 100644 index 000000000..6e306c687 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPObjectIdentifiers.java @@ -0,0 +1,29 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * OIDs for RFC 2560 + * Online Certificate Status Protocol - OCSP. + */ +public interface OCSPObjectIdentifiers +{ + /** OID: 1.3.6.1.5.5.7.48.1 */ + static final ASN1ObjectIdentifier id_pkix_ocsp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1"); + /** OID: 1.3.6.1.5.5.7.48.1.1 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_basic = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.1"); + + /** OID: 1.3.6.1.5.5.7.48.1.2 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_nonce = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.2"); + /** OID: 1.3.6.1.5.5.7.48.1.3 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_crl = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.3"); + + /** OID: 1.3.6.1.5.5.7.48.1.4 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_response = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.4"); + /** OID: 1.3.6.1.5.5.7.48.1.5 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_nocheck = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.5"); + /** OID: 1.3.6.1.5.5.7.48.1.6 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_archive_cutoff = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.6"); + /** OID: 1.3.6.1.5.5.7.48.1.7 */ + static final ASN1ObjectIdentifier id_pkix_ocsp_service_locator = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.7"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPRequest.java new file mode 100644 index 000000000..ba8d8c46e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPRequest.java @@ -0,0 +1,90 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class OCSPRequest + extends ASN1Object +{ + TBSRequest tbsRequest; + Signature optionalSignature; + + public OCSPRequest( + TBSRequest tbsRequest, + Signature optionalSignature) + { + this.tbsRequest = tbsRequest; + this.optionalSignature = optionalSignature; + } + + private OCSPRequest( + ASN1Sequence seq) + { + tbsRequest = TBSRequest.getInstance(seq.getObjectAt(0)); + + if (seq.size() == 2) + { + optionalSignature = Signature.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true); + } + } + + public static OCSPRequest getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static OCSPRequest getInstance( + Object obj) + { + if (obj instanceof OCSPRequest) + { + return (OCSPRequest)obj; + } + else if (obj != null) + { + return new OCSPRequest(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public TBSRequest getTbsRequest() + { + return tbsRequest; + } + + public Signature getOptionalSignature() + { + return optionalSignature; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * OCSPRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(tbsRequest); + + if (optionalSignature != null) + { + v.add(new DERTaggedObject(true, 0, optionalSignature)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPResponse.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPResponse.java new file mode 100644 index 000000000..c3bf5813a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPResponse.java @@ -0,0 +1,90 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class OCSPResponse + extends ASN1Object +{ + OCSPResponseStatus responseStatus; + ResponseBytes responseBytes; + + public OCSPResponse( + OCSPResponseStatus responseStatus, + ResponseBytes responseBytes) + { + this.responseStatus = responseStatus; + this.responseBytes = responseBytes; + } + + private OCSPResponse( + ASN1Sequence seq) + { + responseStatus = OCSPResponseStatus.getInstance(seq.getObjectAt(0)); + + if (seq.size() == 2) + { + responseBytes = ResponseBytes.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true); + } + } + + public static OCSPResponse getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static OCSPResponse getInstance( + Object obj) + { + if (obj instanceof OCSPResponse) + { + return (OCSPResponse)obj; + } + else if (obj != null) + { + return new OCSPResponse(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public OCSPResponseStatus getResponseStatus() + { + return responseStatus; + } + + public ResponseBytes getResponseBytes() + { + return responseBytes; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(responseStatus); + + if (responseBytes != null) + { + v.add(new DERTaggedObject(true, 0, responseBytes)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPResponseStatus.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPResponseStatus.java new file mode 100644 index 000000000..b9729ce34 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/OCSPResponseStatus.java @@ -0,0 +1,71 @@ +package org.spongycastle.asn1.ocsp; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Enumerated; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; + +public class OCSPResponseStatus + extends ASN1Object +{ + public static final int SUCCESSFUL = 0; + public static final int MALFORMED_REQUEST = 1; + public static final int INTERNAL_ERROR = 2; + public static final int TRY_LATER = 3; + public static final int SIG_REQUIRED = 5; + public static final int UNAUTHORIZED = 6; + + private ASN1Enumerated value; + + /** + * The OCSPResponseStatus enumeration. + *
+ * OCSPResponseStatus ::= ENUMERATED { + * successful (0), --Response has valid confirmations + * malformedRequest (1), --Illegal confirmation request + * internalError (2), --Internal error in issuer + * tryLater (3), --Try again later + * --(4) is not used + * sigRequired (5), --Must sign the request + * unauthorized (6) --Request unauthorized + * } + *+ */ + public OCSPResponseStatus( + int value) + { + this(new ASN1Enumerated(value)); + } + + private OCSPResponseStatus( + ASN1Enumerated value) + { + this.value = value; + } + + public static OCSPResponseStatus getInstance( + Object obj) + { + if (obj instanceof OCSPResponseStatus) + { + return (OCSPResponseStatus)obj; + } + else if (obj != null) + { + return new OCSPResponseStatus(ASN1Enumerated.getInstance(obj)); + } + + return null; + } + + public BigInteger getValue() + { + return value.getValue(); + } + + public ASN1Primitive toASN1Primitive() + { + return value; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/Request.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/Request.java new file mode 100644 index 000000000..62e7fb229 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/Request.java @@ -0,0 +1,91 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; + +public class Request + extends ASN1Object +{ + CertID reqCert; + Extensions singleRequestExtensions; + + public Request( + CertID reqCert, + Extensions singleRequestExtensions) + { + this.reqCert = reqCert; + this.singleRequestExtensions = singleRequestExtensions; + } + + private Request( + ASN1Sequence seq) + { + reqCert = CertID.getInstance(seq.getObjectAt(0)); + + if (seq.size() == 2) + { + singleRequestExtensions = Extensions.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true); + } + } + + public static Request getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static Request getInstance( + Object obj) + { + if (obj instanceof Request) + { + return (Request)obj; + } + else if (obj != null) + { + return new Request(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public CertID getReqCert() + { + return reqCert; + } + + public Extensions getSingleRequestExtensions() + { + return singleRequestExtensions; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(reqCert); + + if (singleRequestExtensions != null) + { + v.add(new DERTaggedObject(true, 0, singleRequestExtensions)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponderID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponderID.java new file mode 100644 index 000000000..d53eb7a32 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponderID.java @@ -0,0 +1,104 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; + +public class ResponderID + extends ASN1Object + implements ASN1Choice +{ + private ASN1Encodable value; + + public ResponderID( + ASN1OctetString value) + { + this.value = value; + } + + public ResponderID( + X500Name value) + { + this.value = value; + } + + public static ResponderID getInstance( + Object obj) + { + if (obj instanceof ResponderID) + { + return (ResponderID)obj; + } + else if (obj instanceof DEROctetString) + { + return new ResponderID((DEROctetString)obj); + } + else if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject o = (ASN1TaggedObject)obj; + + if (o.getTagNo() == 1) + { + return new ResponderID(X500Name.getInstance(o, true)); + } + else + { + return new ResponderID(ASN1OctetString.getInstance(o, true)); + } + } + + return new ResponderID(X500Name.getInstance(obj)); + } + + public static ResponderID getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + public byte[] getKeyHash() + { + if (this.value instanceof ASN1OctetString) + { + ASN1OctetString octetString = (ASN1OctetString)this.value; + return octetString.getOctets(); + } + + return null; + } + + public X500Name getName() + { + if (this.value instanceof ASN1OctetString) + { + return null; + } + + return X500Name.getInstance(value); + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + *+ */ + public ASN1Primitive toASN1Primitive() + { + if (value instanceof ASN1OctetString) + { + return new DERTaggedObject(true, 2, value); + } + + return new DERTaggedObject(true, 1, value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponseBytes.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponseBytes.java new file mode 100644 index 000000000..790ebee82 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponseBytes.java @@ -0,0 +1,82 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class ResponseBytes + extends ASN1Object +{ + ASN1ObjectIdentifier responseType; + ASN1OctetString response; + + public ResponseBytes( + ASN1ObjectIdentifier responseType, + ASN1OctetString response) + { + this.responseType = responseType; + this.response = response; + } + + public ResponseBytes( + ASN1Sequence seq) + { + responseType = (ASN1ObjectIdentifier)seq.getObjectAt(0); + response = (ASN1OctetString)seq.getObjectAt(1); + } + + public static ResponseBytes getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static ResponseBytes getInstance( + Object obj) + { + if (obj == null || obj instanceof ResponseBytes) + { + return (ResponseBytes)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new ResponseBytes((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public ASN1ObjectIdentifier getResponseType() + { + return responseType; + } + + public ASN1OctetString getResponse() + { + return response; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(responseType); + v.add(response); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponseData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponseData.java new file mode 100644 index 000000000..522e1d7ea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ResponseData.java @@ -0,0 +1,182 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.X509Extensions; + +public class ResponseData + extends ASN1Object +{ + private static final ASN1Integer V1 = new ASN1Integer(0); + + private boolean versionPresent; + + private ASN1Integer version; + private ResponderID responderID; + private ASN1GeneralizedTime producedAt; + private ASN1Sequence responses; + private Extensions responseExtensions; + + public ResponseData( + ASN1Integer version, + ResponderID responderID, + ASN1GeneralizedTime producedAt, + ASN1Sequence responses, + Extensions responseExtensions) + { + this.version = version; + this.responderID = responderID; + this.producedAt = producedAt; + this.responses = responses; + this.responseExtensions = responseExtensions; + } + + /** + * @deprecated use method taking Extensions + * @param responderID + * @param producedAt + * @param responses + * @param responseExtensions + */ + public ResponseData( + ResponderID responderID, + DERGeneralizedTime producedAt, + ASN1Sequence responses, + X509Extensions responseExtensions) + { + this(V1, responderID, ASN1GeneralizedTime.getInstance(producedAt), responses, Extensions.getInstance(responseExtensions)); + } + + public ResponseData( + ResponderID responderID, + ASN1GeneralizedTime producedAt, + ASN1Sequence responses, + Extensions responseExtensions) + { + this(V1, responderID, producedAt, responses, responseExtensions); + } + + private ResponseData( + ASN1Sequence seq) + { + int index = 0; + + if (seq.getObjectAt(0) instanceof ASN1TaggedObject) + { + ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0); + + if (o.getTagNo() == 0) + { + this.versionPresent = true; + this.version = ASN1Integer.getInstance( + (ASN1TaggedObject)seq.getObjectAt(0), true); + index++; + } + else + { + this.version = V1; + } + } + else + { + this.version = V1; + } + + this.responderID = ResponderID.getInstance(seq.getObjectAt(index++)); + this.producedAt = ASN1GeneralizedTime.getInstance(seq.getObjectAt(index++)); + this.responses = (ASN1Sequence)seq.getObjectAt(index++); + + if (seq.size() > index) + { + this.responseExtensions = Extensions.getInstance( + (ASN1TaggedObject)seq.getObjectAt(index), true); + } + } + + public static ResponseData getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static ResponseData getInstance( + Object obj) + { + if (obj instanceof ResponseData) + { + return (ResponseData)obj; + } + else if (obj != null) + { + return new ResponseData(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1Integer getVersion() + { + return version; + } + + public ResponderID getResponderID() + { + return responderID; + } + + public ASN1GeneralizedTime getProducedAt() + { + return producedAt; + } + + public ASN1Sequence getResponses() + { + return responses; + } + + public Extensions getResponseExtensions() + { + return responseExtensions; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (versionPresent || !version.equals(V1)) + { + v.add(new DERTaggedObject(true, 0, version)); + } + + v.add(responderID); + v.add(producedAt); + v.add(responses); + if (responseExtensions != null) + { + v.add(new DERTaggedObject(true, 1, responseExtensions)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/RevokedInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/RevokedInfo.java new file mode 100644 index 000000000..bf9eca77a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/RevokedInfo.java @@ -0,0 +1,92 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Enumerated; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.CRLReason; + +public class RevokedInfo + extends ASN1Object +{ + private ASN1GeneralizedTime revocationTime; + private CRLReason revocationReason; + + public RevokedInfo( + ASN1GeneralizedTime revocationTime, + CRLReason revocationReason) + { + this.revocationTime = revocationTime; + this.revocationReason = revocationReason; + } + + private RevokedInfo( + ASN1Sequence seq) + { + this.revocationTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + this.revocationReason = CRLReason.getInstance(ASN1Enumerated.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)); + } + } + + public static RevokedInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RevokedInfo getInstance( + Object obj) + { + if (obj instanceof RevokedInfo) + { + return (RevokedInfo)obj; + } + else if (obj != null) + { + return new RevokedInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1GeneralizedTime getRevocationTime() + { + return revocationTime; + } + + public CRLReason getRevocationReason() + { + return revocationReason; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(revocationTime); + if (revocationReason != null) + { + v.add(new DERTaggedObject(true, 0, revocationReason)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ServiceLocator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ServiceLocator.java new file mode 100644 index 000000000..32aa9b7ee --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/ServiceLocator.java @@ -0,0 +1,36 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.X500Name; + +public class ServiceLocator + extends ASN1Object +{ + X500Name issuer; + ASN1Primitive locator; + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * ServiceLocator ::= SEQUENCE { + * issuer Name, + * locator AuthorityInfoAccessSyntax OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(issuer); + + if (locator != null) + { + v.add(locator); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/Signature.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/Signature.java new file mode 100644 index 000000000..776f7066a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/Signature.java @@ -0,0 +1,111 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class Signature + extends ASN1Object +{ + AlgorithmIdentifier signatureAlgorithm; + DERBitString signature; + ASN1Sequence certs; + + public Signature( + AlgorithmIdentifier signatureAlgorithm, + DERBitString signature) + { + this.signatureAlgorithm = signatureAlgorithm; + this.signature = signature; + } + + public Signature( + AlgorithmIdentifier signatureAlgorithm, + DERBitString signature, + ASN1Sequence certs) + { + this.signatureAlgorithm = signatureAlgorithm; + this.signature = signature; + this.certs = certs; + } + + private Signature( + ASN1Sequence seq) + { + signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + signature = (DERBitString)seq.getObjectAt(1); + + if (seq.size() == 3) + { + certs = ASN1Sequence.getInstance( + (ASN1TaggedObject)seq.getObjectAt(2), true); + } + } + + public static Signature getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static Signature getInstance( + Object obj) + { + if (obj instanceof Signature) + { + return (Signature)obj; + } + else if (obj != null) + { + return new Signature(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return signatureAlgorithm; + } + + public DERBitString getSignature() + { + return signature; + } + + public ASN1Sequence getCerts() + { + return certs; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL} + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(signatureAlgorithm); + v.add(signature); + + if (certs != null) + { + v.add(new DERTaggedObject(true, 0, certs)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/SingleResponse.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/SingleResponse.java new file mode 100644 index 000000000..f3b29c99e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/SingleResponse.java @@ -0,0 +1,181 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.X509Extensions; + +public class SingleResponse + extends ASN1Object +{ + private CertID certID; + private CertStatus certStatus; + private ASN1GeneralizedTime thisUpdate; + private ASN1GeneralizedTime nextUpdate; + private Extensions singleExtensions; + + /** + * @deprecated use method taking ASN1GeneralizedTime and Extensions + * @param certID + * @param certStatus + * @param thisUpdate + * @param nextUpdate + * @param singleExtensions + */ + public SingleResponse( + CertID certID, + CertStatus certStatus, + DERGeneralizedTime thisUpdate, + DERGeneralizedTime nextUpdate, + X509Extensions singleExtensions) + { + this(certID, certStatus, thisUpdate, nextUpdate, Extensions.getInstance(singleExtensions)); + } + + /** + * @deprecated use method taking ASN1GeneralizedTime and Extensions + * @param certID + * @param certStatus + * @param thisUpdate + * @param nextUpdate + * @param singleExtensions + */ + public SingleResponse( + CertID certID, + CertStatus certStatus, + DERGeneralizedTime thisUpdate, + DERGeneralizedTime nextUpdate, + Extensions singleExtensions) + { + this(certID, certStatus, ASN1GeneralizedTime.getInstance(thisUpdate), ASN1GeneralizedTime.getInstance(nextUpdate), Extensions.getInstance(singleExtensions)); + } + + public SingleResponse( + CertID certID, + CertStatus certStatus, + ASN1GeneralizedTime thisUpdate, + ASN1GeneralizedTime nextUpdate, + Extensions singleExtensions) + { + this.certID = certID; + this.certStatus = certStatus; + this.thisUpdate = thisUpdate; + this.nextUpdate = nextUpdate; + this.singleExtensions = singleExtensions; + } + + private SingleResponse( + ASN1Sequence seq) + { + this.certID = CertID.getInstance(seq.getObjectAt(0)); + this.certStatus = CertStatus.getInstance(seq.getObjectAt(1)); + this.thisUpdate = ASN1GeneralizedTime.getInstance(seq.getObjectAt(2)); + + if (seq.size() > 4) + { + this.nextUpdate = ASN1GeneralizedTime.getInstance( + (ASN1TaggedObject)seq.getObjectAt(3), true); + this.singleExtensions = Extensions.getInstance( + (ASN1TaggedObject)seq.getObjectAt(4), true); + } + else if (seq.size() > 3) + { + ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(3); + + if (o.getTagNo() == 0) + { + this.nextUpdate = ASN1GeneralizedTime.getInstance(o, true); + } + else + { + this.singleExtensions = Extensions.getInstance(o, true); + } + } + } + + public static SingleResponse getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static SingleResponse getInstance( + Object obj) + { + if (obj instanceof SingleResponse) + { + return (SingleResponse)obj; + } + else if (obj != null) + { + return new SingleResponse(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public CertID getCertID() + { + return certID; + } + + public CertStatus getCertStatus() + { + return certStatus; + } + + public ASN1GeneralizedTime getThisUpdate() + { + return thisUpdate; + } + + public ASN1GeneralizedTime getNextUpdate() + { + return nextUpdate; + } + + public Extensions getSingleExtensions() + { + return singleExtensions; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certID); + v.add(certStatus); + v.add(thisUpdate); + + if (nextUpdate != null) + { + v.add(new DERTaggedObject(true, 0, nextUpdate)); + } + + if (singleExtensions != null) + { + v.add(new DERTaggedObject(true, 1, singleExtensions)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/TBSRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/TBSRequest.java new file mode 100644 index 000000000..6c4220bb2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ocsp/TBSRequest.java @@ -0,0 +1,172 @@ +package org.spongycastle.asn1.ocsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.X509Extensions; + +public class TBSRequest + extends ASN1Object +{ + private static final ASN1Integer V1 = new ASN1Integer(0); + + ASN1Integer version; + GeneralName requestorName; + ASN1Sequence requestList; + Extensions requestExtensions; + + boolean versionSet; + + /** + * @deprecated use method taking Extensions + * @param requestorName + * @param requestList + * @param requestExtensions + */ + public TBSRequest( + GeneralName requestorName, + ASN1Sequence requestList, + X509Extensions requestExtensions) + { + this.version = V1; + this.requestorName = requestorName; + this.requestList = requestList; + this.requestExtensions = Extensions.getInstance(requestExtensions); + } + + public TBSRequest( + GeneralName requestorName, + ASN1Sequence requestList, + Extensions requestExtensions) + { + this.version = V1; + this.requestorName = requestorName; + this.requestList = requestList; + this.requestExtensions = requestExtensions; + } + + private TBSRequest( + ASN1Sequence seq) + { + int index = 0; + + if (seq.getObjectAt(0) instanceof ASN1TaggedObject) + { + ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0); + + if (o.getTagNo() == 0) + { + versionSet = true; + version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true); + index++; + } + else + { + version = V1; + } + } + else + { + version = V1; + } + + if (seq.getObjectAt(index) instanceof ASN1TaggedObject) + { + requestorName = GeneralName.getInstance((ASN1TaggedObject)seq.getObjectAt(index++), true); + } + + requestList = (ASN1Sequence)seq.getObjectAt(index++); + + if (seq.size() == (index + 1)) + { + requestExtensions = Extensions.getInstance((ASN1TaggedObject)seq.getObjectAt(index), true); + } + } + + public static TBSRequest getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static TBSRequest getInstance( + Object obj) + { + if (obj instanceof TBSRequest) + { + return (TBSRequest)obj; + } + else if (obj != null) + { + return new TBSRequest(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1Integer getVersion() + { + return version; + } + + public GeneralName getRequestorName() + { + return requestorName; + } + + public ASN1Sequence getRequestList() + { + return requestList; + } + + public Extensions getRequestExtensions() + { + return requestExtensions; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + // + // if default don't include - unless explicitly provided. Not strictly correct + // but required for some requests + // + if (!version.equals(V1) || versionSet) + { + v.add(new DERTaggedObject(true, 0, version)); + } + + if (requestorName != null) + { + v.add(new DERTaggedObject(true, 1, requestorName)); + } + + v.add(requestList); + + if (requestExtensions != null) + { + v.add(new DERTaggedObject(true, 2, requestExtensions)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/oiw/ElGamalParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/oiw/ElGamalParameter.java new file mode 100644 index 000000000..28ed26326 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/oiw/ElGamalParameter.java @@ -0,0 +1,54 @@ +package org.spongycastle.asn1.oiw; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class ElGamalParameter + extends ASN1Object +{ + ASN1Integer p, g; + + public ElGamalParameter( + BigInteger p, + BigInteger g) + { + this.p = new ASN1Integer(p); + this.g = new ASN1Integer(g); + } + + public ElGamalParameter( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + p = (ASN1Integer)e.nextElement(); + g = (ASN1Integer)e.nextElement(); + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getG() + { + return g.getPositiveValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(p); + v.add(g); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/oiw/OIWObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/oiw/OIWObjectIdentifiers.java new file mode 100644 index 000000000..18bf44b28 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/oiw/OIWObjectIdentifiers.java @@ -0,0 +1,50 @@ +package org.spongycastle.asn1.oiw; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * OIW organization's OIDs: + *
+ * id-SHA1 OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } + */ +public interface OIWObjectIdentifiers +{ + /** OID: 1.3.14.3.2.2 */ + static final ASN1ObjectIdentifier md4WithRSA = new ASN1ObjectIdentifier("1.3.14.3.2.2"); + /** OID: 1.3.14.3.2.3 */ + static final ASN1ObjectIdentifier md5WithRSA = new ASN1ObjectIdentifier("1.3.14.3.2.3"); + /** OID: 1.3.14.3.2.4 */ + static final ASN1ObjectIdentifier md4WithRSAEncryption = new ASN1ObjectIdentifier("1.3.14.3.2.4"); + + /** OID: 1.3.14.3.2.6 */ + static final ASN1ObjectIdentifier desECB = new ASN1ObjectIdentifier("1.3.14.3.2.6"); + /** OID: 1.3.14.3.2.7 */ + static final ASN1ObjectIdentifier desCBC = new ASN1ObjectIdentifier("1.3.14.3.2.7"); + /** OID: 1.3.14.3.2.8 */ + static final ASN1ObjectIdentifier desOFB = new ASN1ObjectIdentifier("1.3.14.3.2.8"); + /** OID: 1.3.14.3.2.9 */ + static final ASN1ObjectIdentifier desCFB = new ASN1ObjectIdentifier("1.3.14.3.2.9"); + + /** OID: 1.3.14.3.2.17 */ + static final ASN1ObjectIdentifier desEDE = new ASN1ObjectIdentifier("1.3.14.3.2.17"); + + /** OID: 1.3.14.3.2.26 */ + static final ASN1ObjectIdentifier idSHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.26"); + + /** OID: 1.3.14.3.2.27 */ + static final ASN1ObjectIdentifier dsaWithSHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.27"); + + /** OID: 1.3.14.3.2.29 */ + static final ASN1ObjectIdentifier sha1WithRSA = new ASN1ObjectIdentifier("1.3.14.3.2.29"); + + /** + *
+ * ElGamal Algorithm OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 } + *+ * OID: 1.3.14.7.2.1.1 + */ + static final ASN1ObjectIdentifier elGamalAlgorithm = new ASN1ObjectIdentifier("1.3.14.7.2.1.1"); + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/Attribute.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/Attribute.java new file mode 100644 index 000000000..ea010e74c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/Attribute.java @@ -0,0 +1,88 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERSequence; + +public class Attribute + extends ASN1Object +{ + private ASN1ObjectIdentifier attrType; + private ASN1Set attrValues; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static Attribute getInstance( + Object o) + { + if (o == null || o instanceof Attribute) + { + return (Attribute)o; + } + + if (o instanceof ASN1Sequence) + { + return new Attribute((ASN1Sequence)o); + } + + throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName()); + } + + public Attribute( + ASN1Sequence seq) + { + attrType = (ASN1ObjectIdentifier)seq.getObjectAt(0); + attrValues = (ASN1Set)seq.getObjectAt(1); + } + + public Attribute( + ASN1ObjectIdentifier attrType, + ASN1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public ASN1ObjectIdentifier getAttrType() + { + return attrType; + } + + public ASN1Set getAttrValues() + { + return attrValues; + } + + public ASN1Encodable[] getAttributeValues() + { + return attrValues.toArray(); + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Attribute ::= SEQUENCE { + * attrType OBJECT IDENTIFIER, + * attrValues SET OF AttributeValue + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(attrType); + v.add(attrValues); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/AuthenticatedSafe.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/AuthenticatedSafe.java new file mode 100644 index 000000000..c4a824aea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/AuthenticatedSafe.java @@ -0,0 +1,74 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.DLSequence; + +public class AuthenticatedSafe + extends ASN1Object +{ + private ContentInfo[] info; + private boolean isBer = true; + + private AuthenticatedSafe( + ASN1Sequence seq) + { + info = new ContentInfo[seq.size()]; + + for (int i = 0; i != info.length; i++) + { + info[i] = ContentInfo.getInstance(seq.getObjectAt(i)); + } + + isBer = seq instanceof BERSequence; + } + + public static AuthenticatedSafe getInstance( + Object o) + { + if (o instanceof AuthenticatedSafe) + { + return (AuthenticatedSafe)o; + } + + if (o != null) + { + return new AuthenticatedSafe(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public AuthenticatedSafe( + ContentInfo[] info) + { + this.info = info; + } + + public ContentInfo[] getContentInfo() + { + return info; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != info.length; i++) + { + v.add(info[i]); + } + + if (isBer) + { + return new BERSequence(v); + } + else + { + return new DLSequence(v); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CRLBag.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CRLBag.java new file mode 100644 index 000000000..9a5268375 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CRLBag.java @@ -0,0 +1,82 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class CRLBag + extends ASN1Object +{ + private ASN1ObjectIdentifier crlId; + private ASN1Encodable crlValue; + + private CRLBag( + ASN1Sequence seq) + { + this.crlId = (ASN1ObjectIdentifier)seq.getObjectAt(0); + this.crlValue = ((DERTaggedObject)seq.getObjectAt(1)).getObject(); + } + + public static CRLBag getInstance(Object o) + { + if (o instanceof CRLBag) + { + return (CRLBag)o; + } + else if (o != null) + { + return new CRLBag(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CRLBag( + ASN1ObjectIdentifier crlId, + ASN1Encodable crlValue) + { + this.crlId = crlId; + this.crlValue = crlValue; + } + + public ASN1ObjectIdentifier getcrlId() + { + return crlId; + } + + public ASN1Encodable getCRLValue() + { + return crlValue; + } + + /** + *
+ CRLBag ::= SEQUENCE { + crlId BAG-TYPE.&id ({CRLTypes}), + crlValue [0] EXPLICIT BAG-TYPE.&Type ({CRLTypes}{@crlId}) + } + + x509CRL BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1} + -- DER-encoded X.509 CRL stored in OCTET STRING + + CRLTypes BAG-TYPE ::= { + x509CRL, + ... -- For future extensions + } ++ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(crlId); + v.add(new DERTaggedObject(0, crlValue)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertBag.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertBag.java new file mode 100644 index 000000000..40dd02ed9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertBag.java @@ -0,0 +1,66 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class CertBag + extends ASN1Object +{ + private ASN1ObjectIdentifier certId; + private ASN1Encodable certValue; + + private CertBag( + ASN1Sequence seq) + { + this.certId = (ASN1ObjectIdentifier)seq.getObjectAt(0); + this.certValue = ((DERTaggedObject)seq.getObjectAt(1)).getObject(); + } + + public static CertBag getInstance(Object o) + { + if (o instanceof CertBag) + { + return (CertBag)o; + } + else if (o != null) + { + return new CertBag(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public CertBag( + ASN1ObjectIdentifier certId, + ASN1Encodable certValue) + { + this.certId = certId; + this.certValue = certValue; + } + + public ASN1ObjectIdentifier getCertId() + { + return certId; + } + + public ASN1Encodable getCertValue() + { + return certValue; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certId); + v.add(new DERTaggedObject(0, certValue)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertificationRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertificationRequest.java new file mode 100644 index 000000000..68d1d265c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertificationRequest.java @@ -0,0 +1,91 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * PKCS10 Certification request object. + *
+ * CertificationRequest ::= SEQUENCE { + * certificationRequestInfo CertificationRequestInfo, + * signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, + * signature BIT STRING + * } + *+ */ +public class CertificationRequest + extends ASN1Object +{ + protected CertificationRequestInfo reqInfo = null; + protected AlgorithmIdentifier sigAlgId = null; + protected DERBitString sigBits = null; + + public static CertificationRequest getInstance(Object o) + { + if (o instanceof CertificationRequest) + { + return (CertificationRequest)o; + } + + if (o != null) + { + return new CertificationRequest(ASN1Sequence.getInstance(o)); + } + + return null; + } + + protected CertificationRequest() + { + } + + public CertificationRequest( + CertificationRequestInfo requestInfo, + AlgorithmIdentifier algorithm, + DERBitString signature) + { + this.reqInfo = requestInfo; + this.sigAlgId = algorithm; + this.sigBits = signature; + } + + public CertificationRequest( + ASN1Sequence seq) + { + reqInfo = CertificationRequestInfo.getInstance(seq.getObjectAt(0)); + sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + sigBits = (DERBitString)seq.getObjectAt(2); + } + + public CertificationRequestInfo getCertificationRequestInfo() + { + return reqInfo; + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return sigAlgId; + } + + public DERBitString getSignature() + { + return sigBits; + } + + public ASN1Primitive toASN1Primitive() + { + // Construct the CertificateRequest + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(reqInfo); + v.add(sigAlgId); + v.add(sigBits); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertificationRequestInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertificationRequestInfo.java new file mode 100644 index 000000000..e63811030 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/CertificationRequestInfo.java @@ -0,0 +1,164 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509Name; + +/** + * PKCS10 CertificationRequestInfo object. + *
+ * CertificationRequestInfo ::= SEQUENCE { + * version INTEGER { v1(0) } (v1,...), + * subject Name, + * subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, + * attributes [0] Attributes{{ CRIAttributes }} + * } + * + * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }} + * + * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE { + * type ATTRIBUTE.&id({IOSet}), + * values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type}) + * } + *+ */ +public class CertificationRequestInfo + extends ASN1Object +{ + ASN1Integer version = new ASN1Integer(0); + X500Name subject; + SubjectPublicKeyInfo subjectPKInfo; + ASN1Set attributes = null; + + public static CertificationRequestInfo getInstance( + Object obj) + { + if (obj instanceof CertificationRequestInfo) + { + return (CertificationRequestInfo)obj; + } + else if (obj != null) + { + return new CertificationRequestInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Basic constructor. + *
+ * Note: Early on a lot of CAs would only accept messages with attributes missing. As the ASN.1 def shows + * the attributes field is not optional so should always at least contain an empty set. If a fully compliant + * request is required, pass in an empty set, the class will otherwise interpret a null as it should + * encode the request with the field missing. + *
+ * + * @param subject subject to be associated with the public key + * @param pkInfo public key to be associated with subject + * @param attributes any attributes to be associated with the request. + */ + public CertificationRequestInfo( + X500Name subject, + SubjectPublicKeyInfo pkInfo, + ASN1Set attributes) + { + this.subject = subject; + this.subjectPKInfo = pkInfo; + this.attributes = attributes; + + if ((subject == null) || (version == null) || (subjectPKInfo == null)) + { + throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator."); + } + } + + /** + * @deprecated use X500Name method. + */ + public CertificationRequestInfo( + X509Name subject, + SubjectPublicKeyInfo pkInfo, + ASN1Set attributes) + { + this.subject = X500Name.getInstance(subject.toASN1Primitive()); + this.subjectPKInfo = pkInfo; + this.attributes = attributes; + + if ((subject == null) || (version == null) || (subjectPKInfo == null)) + { + throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator."); + } + } + + /** + * @deprecated use getInstance(). + */ + public CertificationRequestInfo( + ASN1Sequence seq) + { + version = (ASN1Integer)seq.getObjectAt(0); + + subject = X500Name.getInstance(seq.getObjectAt(1)); + subjectPKInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(2)); + + // + // some CertificationRequestInfo objects seem to treat this field + // as optional. + // + if (seq.size() > 3) + { + DERTaggedObject tagobj = (DERTaggedObject)seq.getObjectAt(3); + attributes = ASN1Set.getInstance(tagobj, false); + } + + if ((subject == null) || (version == null) || (subjectPKInfo == null)) + { + throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator."); + } + } + + public ASN1Integer getVersion() + { + return version; + } + + public X500Name getSubject() + { + return subject; + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return subjectPKInfo; + } + + public ASN1Set getAttributes() + { + return attributes; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(subject); + v.add(subjectPKInfo); + + if (attributes != null) + { + v.add(new DERTaggedObject(false, 0, attributes)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/ContentInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/ContentInfo.java new file mode 100644 index 000000000..a325f0fdc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/ContentInfo.java @@ -0,0 +1,102 @@ +package org.spongycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.BERTaggedObject; +import org.spongycastle.asn1.DLSequence; + +public class ContentInfo + extends ASN1Object + implements PKCSObjectIdentifiers +{ + private ASN1ObjectIdentifier contentType; + private ASN1Encodable content; + private boolean isBer = true; + + public static ContentInfo getInstance( + Object obj) + { + if (obj instanceof ContentInfo) + { + return (ContentInfo)obj; + } + + if (obj != null) + { + return new ContentInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private ContentInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + contentType = (ASN1ObjectIdentifier)e.nextElement(); + + if (e.hasMoreElements()) + { + content = ((ASN1TaggedObject)e.nextElement()).getObject(); + } + + isBer = seq instanceof BERSequence; + } + + public ContentInfo( + ASN1ObjectIdentifier contentType, + ASN1Encodable content) + { + this.contentType = contentType; + this.content = content; + } + + public ASN1ObjectIdentifier getContentType() + { + return contentType; + } + + public ASN1Encodable getContent() + { + return content; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *+ * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content + * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(contentType); + + if (content != null) + { + v.add(new BERTaggedObject(true, 0, content)); + } + + if (isBer) + { + return new BERSequence(v); + } + else + { + return new DLSequence(v); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/DHParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/DHParameter.java new file mode 100644 index 000000000..a427ca212 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/DHParameter.java @@ -0,0 +1,104 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class DHParameter + extends ASN1Object +{ + ASN1Integer p, g, l; + + public DHParameter( + BigInteger p, + BigInteger g, + int l) + { + this.p = new ASN1Integer(p); + this.g = new ASN1Integer(g); + + if (l != 0) + { + this.l = new ASN1Integer(l); + } + else + { + this.l = null; + } + } + + public static DHParameter getInstance( + Object obj) + { + if (obj instanceof DHParameter) + { + return (DHParameter)obj; + } + + if (obj != null) + { + return new DHParameter(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private DHParameter( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + p = ASN1Integer.getInstance(e.nextElement()); + g = ASN1Integer.getInstance(e.nextElement()); + + if (e.hasMoreElements()) + { + l = (ASN1Integer)e.nextElement(); + } + else + { + l = null; + } + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getG() + { + return g.getPositiveValue(); + } + + public BigInteger getL() + { + if (l == null) + { + return null; + } + + return l.getPositiveValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(p); + v.add(g); + + if (this.getL() != null) + { + v.add(l); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptedData.java new file mode 100644 index 000000000..397758a8f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptedData.java @@ -0,0 +1,115 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.BERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * The EncryptedData object. + *
+ * EncryptedData ::= SEQUENCE { + * version Version, + * encryptedContentInfo EncryptedContentInfo + * } + * + * + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + * + * EncryptedContent ::= OCTET STRING + *+ */ +public class EncryptedData + extends ASN1Object +{ + ASN1Sequence data; + ASN1ObjectIdentifier bagId; + ASN1Primitive bagValue; + + public static EncryptedData getInstance( + Object obj) + { + if (obj instanceof EncryptedData) + { + return (EncryptedData)obj; + } + + if (obj != null) + { + return new EncryptedData(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private EncryptedData( + ASN1Sequence seq) + { + int version = ((ASN1Integer)seq.getObjectAt(0)).getValue().intValue(); + + if (version != 0) + { + throw new IllegalArgumentException("sequence not version 0"); + } + + this.data = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + + public EncryptedData( + ASN1ObjectIdentifier contentType, + AlgorithmIdentifier encryptionAlgorithm, + ASN1Encodable content) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(contentType); + v.add(encryptionAlgorithm.toASN1Primitive()); + v.add(new BERTaggedObject(false, 0, content)); + + data = new BERSequence(v); + } + + public ASN1ObjectIdentifier getContentType() + { + return ASN1ObjectIdentifier.getInstance(data.getObjectAt(0)); + } + + public AlgorithmIdentifier getEncryptionAlgorithm() + { + return AlgorithmIdentifier.getInstance(data.getObjectAt(1)); + } + + public ASN1OctetString getContent() + { + if (data.size() == 3) + { + ASN1TaggedObject o = ASN1TaggedObject.getInstance(data.getObjectAt(2)); + + return ASN1OctetString.getInstance(o, false); + } + + return null; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(0)); + v.add(data); + + return new BERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java new file mode 100644 index 000000000..1e48a6218 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java @@ -0,0 +1,86 @@ +package org.spongycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class EncryptedPrivateKeyInfo + extends ASN1Object +{ + private AlgorithmIdentifier algId; + private ASN1OctetString data; + + private EncryptedPrivateKeyInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + algId = AlgorithmIdentifier.getInstance(e.nextElement()); + data = ASN1OctetString.getInstance(e.nextElement()); + } + + public EncryptedPrivateKeyInfo( + AlgorithmIdentifier algId, + byte[] encoding) + { + this.algId = algId; + this.data = new DEROctetString(encoding); + } + + public static EncryptedPrivateKeyInfo getInstance( + Object obj) + { + if (obj instanceof EncryptedPrivateKeyInfo) + { + return (EncryptedPrivateKeyInfo)obj; + } + else if (obj != null) + { + return new EncryptedPrivateKeyInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public AlgorithmIdentifier getEncryptionAlgorithm() + { + return algId; + } + + public byte[] getEncryptedData() + { + return data.getOctets(); + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}}, + * encryptedData EncryptedData + * } + * + * EncryptedData ::= OCTET STRING + * + * KeyEncryptionAlgorithms ALGORITHM-IDENTIFIER ::= { + * ... -- For local profiles + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(algId); + v.add(data); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptionScheme.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptionScheme.java new file mode 100644 index 000000000..caaa7b326 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/EncryptionScheme.java @@ -0,0 +1,56 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class EncryptionScheme + extends ASN1Object +{ + private AlgorithmIdentifier algId; + + public EncryptionScheme( + ASN1ObjectIdentifier objectId, + ASN1Encodable parameters) + { + this.algId = new AlgorithmIdentifier(objectId, parameters); + } + + private EncryptionScheme( + ASN1Sequence seq) + { + this.algId = AlgorithmIdentifier.getInstance(seq); + } + + public static final EncryptionScheme getInstance(Object obj) + { + if (obj instanceof EncryptionScheme) + { + return (EncryptionScheme)obj; + } + else if (obj != null) + { + return new EncryptionScheme(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1ObjectIdentifier getAlgorithm() + { + return algId.getAlgorithm(); + } + + public ASN1Encodable getParameters() + { + return algId.getParameters(); + } + + public ASN1Primitive toASN1Primitive() + { + return algId.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/IssuerAndSerialNumber.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/IssuerAndSerialNumber.java new file mode 100644 index 000000000..1764694bb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/IssuerAndSerialNumber.java @@ -0,0 +1,85 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.X509Name; + +public class IssuerAndSerialNumber + extends ASN1Object +{ + X500Name name; + ASN1Integer certSerialNumber; + + public static IssuerAndSerialNumber getInstance( + Object obj) + { + if (obj instanceof IssuerAndSerialNumber) + { + return (IssuerAndSerialNumber)obj; + } + else if (obj != null) + { + return new IssuerAndSerialNumber(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private IssuerAndSerialNumber( + ASN1Sequence seq) + { + this.name = X500Name.getInstance(seq.getObjectAt(0)); + this.certSerialNumber = (ASN1Integer)seq.getObjectAt(1); + } + + public IssuerAndSerialNumber( + X509Name name, + BigInteger certSerialNumber) + { + this.name = X500Name.getInstance(name.toASN1Primitive()); + this.certSerialNumber = new ASN1Integer(certSerialNumber); + } + + public IssuerAndSerialNumber( + X509Name name, + ASN1Integer certSerialNumber) + { + this.name = X500Name.getInstance(name.toASN1Primitive()); + this.certSerialNumber = certSerialNumber; + } + + public IssuerAndSerialNumber( + X500Name name, + BigInteger certSerialNumber) + { + this.name = name; + this.certSerialNumber = new ASN1Integer(certSerialNumber); + } + + public X500Name getName() + { + return name; + } + + public ASN1Integer getCertificateSerialNumber() + { + return certSerialNumber; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(name); + v.add(certSerialNumber); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/KeyDerivationFunc.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/KeyDerivationFunc.java new file mode 100644 index 000000000..c6240bd01 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/KeyDerivationFunc.java @@ -0,0 +1,56 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class KeyDerivationFunc + extends ASN1Object +{ + private AlgorithmIdentifier algId; + + public KeyDerivationFunc( + ASN1ObjectIdentifier objectId, + ASN1Encodable parameters) + { + this.algId = new AlgorithmIdentifier(objectId, parameters); + } + + private KeyDerivationFunc( + ASN1Sequence seq) + { + this.algId = AlgorithmIdentifier.getInstance(seq); + } + + public static final KeyDerivationFunc getInstance(Object obj) + { + if (obj instanceof KeyDerivationFunc) + { + return (KeyDerivationFunc)obj; + } + else if (obj != null) + { + return new KeyDerivationFunc(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1ObjectIdentifier getAlgorithm() + { + return algId.getAlgorithm(); + } + + public ASN1Encodable getParameters() + { + return algId.getParameters(); + } + + public ASN1Primitive toASN1Primitive() + { + return algId.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/MacData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/MacData.java new file mode 100644 index 000000000..e1dda07b5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/MacData.java @@ -0,0 +1,106 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.DigestInfo; + +public class MacData + extends ASN1Object +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + + DigestInfo digInfo; + byte[] salt; + BigInteger iterationCount; + + public static MacData getInstance( + Object obj) + { + if (obj instanceof MacData) + { + return (MacData)obj; + } + else if (obj != null) + { + return new MacData(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private MacData( + ASN1Sequence seq) + { + this.digInfo = DigestInfo.getInstance(seq.getObjectAt(0)); + + this.salt = ((ASN1OctetString)seq.getObjectAt(1)).getOctets(); + + if (seq.size() == 3) + { + this.iterationCount = ((ASN1Integer)seq.getObjectAt(2)).getValue(); + } + else + { + this.iterationCount = ONE; + } + } + + public MacData( + DigestInfo digInfo, + byte[] salt, + int iterationCount) + { + this.digInfo = digInfo; + this.salt = salt; + this.iterationCount = BigInteger.valueOf(iterationCount); + } + + public DigestInfo getMac() + { + return digInfo; + } + + public byte[] getSalt() + { + return salt; + } + + public BigInteger getIterationCount() + { + return iterationCount; + } + + /** + *
+ * MacData ::= SEQUENCE { + * mac DigestInfo, + * macSalt OCTET STRING, + * iterations INTEGER DEFAULT 1 + * -- Note: The default is for historic reasons and its use is deprecated. A + * -- higher value, like 1024 is recommended. + *+ * @return the basic ASN1Primitive construction. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(digInfo); + v.add(new DEROctetString(salt)); + + if (!iterationCount.equals(ONE)) + { + v.add(new ASN1Integer(iterationCount)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBEParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBEParameter.java new file mode 100644 index 000000000..c70c06549 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBEParameter.java @@ -0,0 +1,73 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class PBEParameter + extends ASN1Object +{ + ASN1Integer iterations; + ASN1OctetString salt; + + public PBEParameter( + byte[] salt, + int iterations) + { + if (salt.length != 8) + { + throw new IllegalArgumentException("salt length must be 8"); + } + this.salt = new DEROctetString(salt); + this.iterations = new ASN1Integer(iterations); + } + + private PBEParameter( + ASN1Sequence seq) + { + salt = (ASN1OctetString)seq.getObjectAt(0); + iterations = (ASN1Integer)seq.getObjectAt(1); + } + + public static PBEParameter getInstance( + Object obj) + { + if (obj instanceof PBEParameter) + { + return (PBEParameter)obj; + } + else if (obj != null) + { + return new PBEParameter(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public BigInteger getIterationCount() + { + return iterations.getValue(); + } + + public byte[] getSalt() + { + return salt.getOctets(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(salt); + v.add(iterations); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBES2Algorithms.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBES2Algorithms.java new file mode 100644 index 000000000..1a8d9773a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBES2Algorithms.java @@ -0,0 +1,77 @@ +package org.spongycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * @deprecated - use AlgorithmIdentifier and PBES2Parameters + */ +public class PBES2Algorithms + extends AlgorithmIdentifier implements PKCSObjectIdentifiers +{ + private ASN1ObjectIdentifier objectId; + private KeyDerivationFunc func; + private EncryptionScheme scheme; + + public PBES2Algorithms( + ASN1Sequence obj) + { + super(obj); + + Enumeration e = obj.getObjects(); + + objectId = (ASN1ObjectIdentifier)e.nextElement(); + + ASN1Sequence seq = (ASN1Sequence)e.nextElement(); + + e = seq.getObjects(); + + ASN1Sequence funcSeq = (ASN1Sequence)e.nextElement(); + + if (funcSeq.getObjectAt(0).equals(id_PBKDF2)) + { + func = new KeyDerivationFunc(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1))); + } + else + { + func = KeyDerivationFunc.getInstance(funcSeq); + } + + scheme = EncryptionScheme.getInstance(e.nextElement()); + } + + public ASN1ObjectIdentifier getObjectId() + { + return objectId; + } + + public KeyDerivationFunc getKeyDerivationFunc() + { + return func; + } + + public EncryptionScheme getEncryptionScheme() + { + return scheme; + } + + public ASN1Primitive getASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + ASN1EncodableVector subV = new ASN1EncodableVector(); + + v.add(objectId); + + subV.add(func); + subV.add(scheme); + v.add(new DERSequence(subV)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBES2Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBES2Parameters.java new file mode 100644 index 000000000..97c1a8dea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBES2Parameters.java @@ -0,0 +1,77 @@ +package org.spongycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class PBES2Parameters + extends ASN1Object + implements PKCSObjectIdentifiers +{ + private KeyDerivationFunc func; + private EncryptionScheme scheme; + + public static PBES2Parameters getInstance( + Object obj) + { + if (obj instanceof PBES2Parameters) + { + return (PBES2Parameters)obj; + } + if (obj != null) + { + return new PBES2Parameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public PBES2Parameters(KeyDerivationFunc keyDevFunc, EncryptionScheme encScheme) + { + this.func = keyDevFunc; + this.scheme = encScheme; + } + + private PBES2Parameters( + ASN1Sequence obj) + { + Enumeration e = obj.getObjects(); + ASN1Sequence funcSeq = ASN1Sequence.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive()); + + if (funcSeq.getObjectAt(0).equals(id_PBKDF2)) + { + func = new KeyDerivationFunc(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1))); + } + else + { + func = KeyDerivationFunc.getInstance(funcSeq); + } + + scheme = EncryptionScheme.getInstance(e.nextElement()); + } + + public KeyDerivationFunc getKeyDerivationFunc() + { + return func; + } + + public EncryptionScheme getEncryptionScheme() + { + return scheme; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(func); + v.add(scheme); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBKDF2Params.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBKDF2Params.java new file mode 100644 index 000000000..e825888d4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PBKDF2Params.java @@ -0,0 +1,248 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + *
+ * PBKDF2-params ::= SEQUENCE { + * salt CHOICE { + * specified OCTET STRING, + * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + * }, + * iterationCount INTEGER (1..MAX), + * keyLength INTEGER (1..MAX) OPTIONAL, + * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 } + *+ */ +public class PBKDF2Params + extends ASN1Object +{ + private static final AlgorithmIdentifier algid_hmacWithSHA1 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE); + + private ASN1OctetString octStr; + private ASN1Integer iterationCount; + private ASN1Integer keyLength; + private AlgorithmIdentifier prf; + + /** + * Create PBKDF2Params from the passed in object, + * + * @param obj either PBKDF2Params or an ASN2Sequence. + * @return a PBKDF2Params instance. + */ + public static PBKDF2Params getInstance( + Object obj) + { + if (obj instanceof PBKDF2Params) + { + return (PBKDF2Params)obj; + } + + if (obj != null) + { + return new PBKDF2Params(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Create a PBKDF2Params with the specified salt, iteration count, and algid-hmacWithSHA1 for the prf. + * + * @param salt input salt. + * @param iterationCount input iteration count. + */ + public PBKDF2Params( + byte[] salt, + int iterationCount) + { + this.octStr = new DEROctetString(salt); + this.iterationCount = new ASN1Integer(iterationCount); + } + + /** + * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and algid-hmacWithSHA1 for the prf. + * + * @param salt input salt. + * @param iterationCount input iteration count. + * @param keyLength intended key length to be produced. + */ + public PBKDF2Params( + byte[] salt, + int iterationCount, + int keyLength) + { + this(salt, iterationCount); + + this.keyLength = new ASN1Integer(keyLength); + } + + /** + * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and a defined prf. + * + * @param salt input salt. + * @param iterationCount input iteration count. + * @param keyLength intended key length to be produced. + * @param prf the pseudo-random function to use. + */ + public PBKDF2Params( + byte[] salt, + int iterationCount, + int keyLength, + AlgorithmIdentifier prf) + { + this(salt, iterationCount); + + this.keyLength = new ASN1Integer(keyLength); + this.prf = prf; + } + + /** + * Create a PBKDF2Params with the specified salt, iteration count, and a defined prf. + * + * @param salt input salt. + * @param iterationCount input iteration count. + * @param prf the pseudo-random function to use. + */ + public PBKDF2Params( + byte[] salt, + int iterationCount, + AlgorithmIdentifier prf) + { + this(salt, iterationCount); + this.prf = prf; + } + + private PBKDF2Params( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + octStr = (ASN1OctetString)e.nextElement(); + iterationCount = (ASN1Integer)e.nextElement(); + + if (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o instanceof ASN1Integer) + { + keyLength = ASN1Integer.getInstance(o); + if (e.hasMoreElements()) + { + o = e.nextElement(); + } + else + { + o = null; + } + } + else + { + keyLength = null; + } + + if (o != null) + { + prf = AlgorithmIdentifier.getInstance(o); + } + } + } + + /** + * Return the salt to use. + * + * @return the input salt. + */ + public byte[] getSalt() + { + return octStr.getOctets(); + } + + /** + * Return the iteration count to use. + * + * @return the input iteration count. + */ + public BigInteger getIterationCount() + { + return iterationCount.getValue(); + } + + /** + * Return the intended length in octets of the derived key. + * + * @return length in octets for derived key, if specified. + */ + public BigInteger getKeyLength() + { + if (keyLength != null) + { + return keyLength.getValue(); + } + + return null; + } + + /** + * Return true if the PRF is the default (hmacWithSHA1) + * + * @return true if PRF is default, false otherwise. + */ + public boolean isDefaultPrf() + { + return prf == null || prf.equals(algid_hmacWithSHA1); + } + + /** + * Return the algId of the underlying pseudo random function to use. + * + * @return the prf algorithm identifier. + */ + public AlgorithmIdentifier getPrf() + { + if (prf != null) + { + return prf; + } + + return algid_hmacWithSHA1; + } + + /** + * Return an ASN.1 structure suitable for encoding. + * + * @return the object as an ASN.1 encodable structure. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(octStr); + v.add(iterationCount); + + if (keyLength != null) + { + v.add(keyLength); + } + + if (prf != null && !prf.equals(algid_hmacWithSHA1)) + { + v.add(prf); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PKCS12PBEParams.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PKCS12PBEParams.java new file mode 100644 index 000000000..dd6400e2d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PKCS12PBEParams.java @@ -0,0 +1,69 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class PKCS12PBEParams + extends ASN1Object +{ + ASN1Integer iterations; + ASN1OctetString iv; + + public PKCS12PBEParams( + byte[] salt, + int iterations) + { + this.iv = new DEROctetString(salt); + this.iterations = new ASN1Integer(iterations); + } + + private PKCS12PBEParams( + ASN1Sequence seq) + { + iv = (ASN1OctetString)seq.getObjectAt(0); + iterations = ASN1Integer.getInstance(seq.getObjectAt(1)); + } + + public static PKCS12PBEParams getInstance( + Object obj) + { + if (obj instanceof PKCS12PBEParams) + { + return (PKCS12PBEParams)obj; + } + else if (obj != null) + { + return new PKCS12PBEParams(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public BigInteger getIterations() + { + return iterations.getValue(); + } + + public byte[] getIV() + { + return iv.getOctets(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(iv); + v.add(iterations); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PKCSObjectIdentifiers.java new file mode 100644 index 000000000..f9acf0813 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PKCSObjectIdentifiers.java @@ -0,0 +1,390 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * pkcs-1 OBJECT IDENTIFIER ::=
+ * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + * + */ +public interface PKCSObjectIdentifiers +{ + /** PKCS#1: 1.2.840.113549.1.1 */ + static final ASN1ObjectIdentifier pkcs_1 = new ASN1ObjectIdentifier("1.2.840.113549.1.1"); + /** PKCS#1: 1.2.840.113549.1.1.1 */ + static final ASN1ObjectIdentifier rsaEncryption = pkcs_1.branch("1"); + /** PKCS#1: 1.2.840.113549.1.1.2 */ + static final ASN1ObjectIdentifier md2WithRSAEncryption = pkcs_1.branch("2"); + /** PKCS#1: 1.2.840.113549.1.1.3 */ + static final ASN1ObjectIdentifier md4WithRSAEncryption = pkcs_1.branch("3"); + /** PKCS#1: 1.2.840.113549.1.1.4 */ + static final ASN1ObjectIdentifier md5WithRSAEncryption = pkcs_1.branch("4"); + /** PKCS#1: 1.2.840.113549.1.1.5 */ + static final ASN1ObjectIdentifier sha1WithRSAEncryption = pkcs_1.branch("5"); + /** PKCS#1: 1.2.840.113549.1.1.6 */ + static final ASN1ObjectIdentifier srsaOAEPEncryptionSET = pkcs_1.branch("6"); + /** PKCS#1: 1.2.840.113549.1.1.7 */ + static final ASN1ObjectIdentifier id_RSAES_OAEP = pkcs_1.branch("7"); + /** PKCS#1: 1.2.840.113549.1.1.8 */ + static final ASN1ObjectIdentifier id_mgf1 = pkcs_1.branch("8"); + /** PKCS#1: 1.2.840.113549.1.1.9 */ + static final ASN1ObjectIdentifier id_pSpecified = pkcs_1.branch("9"); + /** PKCS#1: 1.2.840.113549.1.1.10 */ + static final ASN1ObjectIdentifier id_RSASSA_PSS = pkcs_1.branch("10"); + /** PKCS#1: 1.2.840.113549.1.1.11 */ + static final ASN1ObjectIdentifier sha256WithRSAEncryption = pkcs_1.branch("11"); + /** PKCS#1: 1.2.840.113549.1.1.12 */ + static final ASN1ObjectIdentifier sha384WithRSAEncryption = pkcs_1.branch("12"); + /** PKCS#1: 1.2.840.113549.1.1.13 */ + static final ASN1ObjectIdentifier sha512WithRSAEncryption = pkcs_1.branch("13"); + /** PKCS#1: 1.2.840.113549.1.1.14 */ + static final ASN1ObjectIdentifier sha224WithRSAEncryption = pkcs_1.branch("14"); + + // + // pkcs-3 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 } + // + /** PKCS#3: 1.2.840.113549.1.3 */ + static final ASN1ObjectIdentifier pkcs_3 = new ASN1ObjectIdentifier("1.2.840.113549.1.3"); + /** PKCS#3: 1.2.840.113549.1.3.1 */ + static final ASN1ObjectIdentifier dhKeyAgreement = pkcs_3.branch("1"); + + // + // pkcs-5 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } + // + /** PKCS#5: 1.2.840.113549.1.5 */ + static final ASN1ObjectIdentifier pkcs_5 = new ASN1ObjectIdentifier("1.2.840.113549.1.5"); + + /** PKCS#5: 1.2.840.113549.1.5.1 */ + static final ASN1ObjectIdentifier pbeWithMD2AndDES_CBC = pkcs_5.branch("1"); + /** PKCS#5: 1.2.840.113549.1.5.4 */ + static final ASN1ObjectIdentifier pbeWithMD2AndRC2_CBC = pkcs_5.branch("4"); + /** PKCS#5: 1.2.840.113549.1.5.3 */ + static final ASN1ObjectIdentifier pbeWithMD5AndDES_CBC = pkcs_5.branch("3"); + /** PKCS#5: 1.2.840.113549.1.5.6 */ + static final ASN1ObjectIdentifier pbeWithMD5AndRC2_CBC = pkcs_5.branch("6"); + /** PKCS#5: 1.2.840.113549.1.5.10 */ + static final ASN1ObjectIdentifier pbeWithSHA1AndDES_CBC = pkcs_5.branch("10"); + /** PKCS#5: 1.2.840.113549.1.5.11 */ + static final ASN1ObjectIdentifier pbeWithSHA1AndRC2_CBC = pkcs_5.branch("11"); + /** PKCS#5: 1.2.840.113549.1.5.13 */ + static final ASN1ObjectIdentifier id_PBES2 = pkcs_5.branch("13"); + /** PKCS#5: 1.2.840.113549.1.5.12 */ + static final ASN1ObjectIdentifier id_PBKDF2 = pkcs_5.branch("12"); + + // + // encryptionAlgorithm OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) 3 } + // + /** 1.2.840.113549.3 */ + static final ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.2.840.113549.3"); + + /** 1.2.840.113549.3.7 */ + static final ASN1ObjectIdentifier des_EDE3_CBC = encryptionAlgorithm.branch("7"); + /** 1.2.840.113549.3.2 */ + static final ASN1ObjectIdentifier RC2_CBC = encryptionAlgorithm.branch("2"); + /** 1.2.840.113549.3.4 */ + static final ASN1ObjectIdentifier rc4 = encryptionAlgorithm.branch("4"); + + // + // object identifiers for digests + // + /** 1.2.840.113549.2 */ + static final ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.2.840.113549.2"); + // + // md2 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2} + // + /** 1.2.840.113549.2.2 */ + static final ASN1ObjectIdentifier md2 = digestAlgorithm.branch("2"); + + // + // md4 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 4} + // + /** 1.2.840.113549.2.4 */ + static final ASN1ObjectIdentifier md4 = digestAlgorithm.branch("4"); + + // + // md5 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5} + // + /** 1.2.840.113549.2.5 */ + static final ASN1ObjectIdentifier md5 = digestAlgorithm.branch("5"); + + /** 1.2.840.113549.2.7 */ + static final ASN1ObjectIdentifier id_hmacWithSHA1 = digestAlgorithm.branch("7"); + /** 1.2.840.113549.2.8 */ + static final ASN1ObjectIdentifier id_hmacWithSHA224 = digestAlgorithm.branch("8"); + /** 1.2.840.113549.2.9 */ + static final ASN1ObjectIdentifier id_hmacWithSHA256 = digestAlgorithm.branch("9"); + /** 1.2.840.113549.2.10 */ + static final ASN1ObjectIdentifier id_hmacWithSHA384 = digestAlgorithm.branch("10"); + /** 1.2.840.113549.2.11 */ + static final ASN1ObjectIdentifier id_hmacWithSHA512 = digestAlgorithm.branch("11"); + + // + // pkcs-7 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 } + // + /** pkcs#7: 1.2.840.113549.1.7 */ + static final ASN1ObjectIdentifier pkcs_7 = new ASN1ObjectIdentifier("1.2.840.113549.1.7"); + /** PKCS#7: 1.2.840.113549.1.7.1 */ + static final ASN1ObjectIdentifier data = new ASN1ObjectIdentifier("1.2.840.113549.1.7.1"); + /** PKCS#7: 1.2.840.113549.1.7.2 */ + static final ASN1ObjectIdentifier signedData = new ASN1ObjectIdentifier("1.2.840.113549.1.7.2"); + /** PKCS#7: 1.2.840.113549.1.7.3 */ + static final ASN1ObjectIdentifier envelopedData = new ASN1ObjectIdentifier("1.2.840.113549.1.7.3"); + /** PKCS#7: 1.2.840.113549.1.7.4 */ + static final ASN1ObjectIdentifier signedAndEnvelopedData = new ASN1ObjectIdentifier("1.2.840.113549.1.7.4"); + /** PKCS#7: 1.2.840.113549.1.7.5 */ + static final ASN1ObjectIdentifier digestedData = new ASN1ObjectIdentifier("1.2.840.113549.1.7.5"); + /** PKCS#7: 1.2.840.113549.1.7.76 */ + static final ASN1ObjectIdentifier encryptedData = new ASN1ObjectIdentifier("1.2.840.113549.1.7.6"); + + // + // pkcs-9 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } + // + /** PKCS#9: 1.2.840.113549.1.9 */ + static final ASN1ObjectIdentifier pkcs_9 = new ASN1ObjectIdentifier("1.2.840.113549.1.9"); + + /** PKCS#9: 1.2.840.113549.1.9.1 */ + static final ASN1ObjectIdentifier pkcs_9_at_emailAddress = pkcs_9.branch("1"); + /** PKCS#9: 1.2.840.113549.1.9.2 */ + static final ASN1ObjectIdentifier pkcs_9_at_unstructuredName = pkcs_9.branch("2"); + /** PKCS#9: 1.2.840.113549.1.9.3 */ + static final ASN1ObjectIdentifier pkcs_9_at_contentType = pkcs_9.branch("3"); + /** PKCS#9: 1.2.840.113549.1.9.4 */ + static final ASN1ObjectIdentifier pkcs_9_at_messageDigest = pkcs_9.branch("4"); + /** PKCS#9: 1.2.840.113549.1.9.5 */ + static final ASN1ObjectIdentifier pkcs_9_at_signingTime = pkcs_9.branch("5"); + /** PKCS#9: 1.2.840.113549.1.9.6 */ + static final ASN1ObjectIdentifier pkcs_9_at_counterSignature = pkcs_9.branch("6"); + /** PKCS#9: 1.2.840.113549.1.9.7 */ + static final ASN1ObjectIdentifier pkcs_9_at_challengePassword = pkcs_9.branch("7"); + /** PKCS#9: 1.2.840.113549.1.9.8 */ + static final ASN1ObjectIdentifier pkcs_9_at_unstructuredAddress = pkcs_9.branch("8"); + /** PKCS#9: 1.2.840.113549.1.9.9 */ + static final ASN1ObjectIdentifier pkcs_9_at_extendedCertificateAttributes = pkcs_9.branch("9"); + + /** PKCS#9: 1.2.840.113549.1.9.13 */ + static final ASN1ObjectIdentifier pkcs_9_at_signingDescription = pkcs_9.branch("13"); + /** PKCS#9: 1.2.840.113549.1.9.14 */ + static final ASN1ObjectIdentifier pkcs_9_at_extensionRequest = pkcs_9.branch("14"); + /** PKCS#9: 1.2.840.113549.1.9.15 */ + static final ASN1ObjectIdentifier pkcs_9_at_smimeCapabilities = pkcs_9.branch("15"); + /** PKCS#9: 1.2.840.113549.1.9.16 */ + static final ASN1ObjectIdentifier id_smime = pkcs_9.branch("16"); + + /** PKCS#9: 1.2.840.113549.1.9.20 */ + static final ASN1ObjectIdentifier pkcs_9_at_friendlyName = pkcs_9.branch("20"); + /** PKCS#9: 1.2.840.113549.1.9.21 */ + static final ASN1ObjectIdentifier pkcs_9_at_localKeyId = pkcs_9.branch("21"); + + /** PKCS#9: 1.2.840.113549.1.9.22.1 + * @deprecated use x509Certificate instead */ + static final ASN1ObjectIdentifier x509certType = pkcs_9.branch("22.1"); + + /** PKCS#9: 1.2.840.113549.1.9.22 */ + static final ASN1ObjectIdentifier certTypes = pkcs_9.branch("22"); + /** PKCS#9: 1.2.840.113549.1.9.22.1 */ + static final ASN1ObjectIdentifier x509Certificate = certTypes.branch("1"); + /** PKCS#9: 1.2.840.113549.1.9.22.2 */ + static final ASN1ObjectIdentifier sdsiCertificate = certTypes.branch("2"); + + /** PKCS#9: 1.2.840.113549.1.9.23 */ + static final ASN1ObjectIdentifier crlTypes = pkcs_9.branch("23"); + /** PKCS#9: 1.2.840.113549.1.9.23.1 */ + static final ASN1ObjectIdentifier x509Crl = crlTypes.branch("1"); + + // + // SMIME capability sub oids. + // + /** PKCS#9: 1.2.840.113549.1.9.15.1 -- smime capability */ + static final ASN1ObjectIdentifier preferSignedData = pkcs_9.branch("15.1"); + /** PKCS#9: 1.2.840.113549.1.9.15.2 -- smime capability */ + static final ASN1ObjectIdentifier canNotDecryptAny = pkcs_9.branch("15.2"); + /** PKCS#9: 1.2.840.113549.1.9.15.3 -- smime capability */ + static final ASN1ObjectIdentifier sMIMECapabilitiesVersions = pkcs_9.branch("15.3"); + + // + // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)} + // + /** PKCS#9: 1.2.840.113549.1.9.16.1 -- smime ct */ + static final ASN1ObjectIdentifier id_ct = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1"); + + /** PKCS#9: 1.2.840.113549.1.9.16.1.2 -- smime ct authData */ + static final ASN1ObjectIdentifier id_ct_authData = id_ct.branch("2"); + /** PKCS#9: 1.2.840.113549.1.9.16.1.4 -- smime ct TSTInfo*/ + static final ASN1ObjectIdentifier id_ct_TSTInfo = id_ct.branch("4"); + /** PKCS#9: 1.2.840.113549.1.9.16.1.9 -- smime ct compressedData */ + static final ASN1ObjectIdentifier id_ct_compressedData = id_ct.branch("9"); + /** PKCS#9: 1.2.840.113549.1.9.16.1.23 -- smime ct authEnvelopedData */ + static final ASN1ObjectIdentifier id_ct_authEnvelopedData = id_ct.branch("23"); + /** PKCS#9: 1.2.840.113549.1.9.16.1.31 -- smime ct timestampedData*/ + static final ASN1ObjectIdentifier id_ct_timestampedData = id_ct.branch("31"); + + + /** S/MIME: Algorithm Identifiers ; 1.2.840.113549.1.9.16.3 */ + static final ASN1ObjectIdentifier id_alg = id_smime.branch("3"); + /** PKCS#9: 1.2.840.113549.1.9.16.3.9 */ + static final ASN1ObjectIdentifier id_alg_PWRI_KEK = id_alg.branch("9"); + + // + // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)} + // + /** PKCS#9: 1.2.840.113549.1.9.16.6 -- smime cti */ + static final ASN1ObjectIdentifier id_cti = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.6"); + + /** PKCS#9: 1.2.840.113549.1.9.16.6.1 -- smime cti proofOfOrigin */ + static final ASN1ObjectIdentifier id_cti_ets_proofOfOrigin = id_cti.branch("1"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2 -- smime cti proofOfReceipt*/ + static final ASN1ObjectIdentifier id_cti_ets_proofOfReceipt = id_cti.branch("2"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.3 -- smime cti proofOfDelivery */ + static final ASN1ObjectIdentifier id_cti_ets_proofOfDelivery = id_cti.branch("3"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.4 -- smime cti proofOfSender */ + static final ASN1ObjectIdentifier id_cti_ets_proofOfSender = id_cti.branch("4"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.5 -- smime cti proofOfApproval */ + static final ASN1ObjectIdentifier id_cti_ets_proofOfApproval = id_cti.branch("5"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.6 -- smime cti proofOfCreation */ + static final ASN1ObjectIdentifier id_cti_ets_proofOfCreation = id_cti.branch("6"); + + // + // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)} + // + /** PKCS#9: 1.2.840.113549.1.9.16.6.2 - smime attributes */ + static final ASN1ObjectIdentifier id_aa = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.2"); + + + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.1 -- smime attribute receiptRequest */ + static final ASN1ObjectIdentifier id_aa_receiptRequest = id_aa.branch("1"); + + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See RFC 2634 */ + static final ASN1ObjectIdentifier id_aa_contentHint = id_aa.branch("4"); // See RFC 2634 + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.5 */ + static final ASN1ObjectIdentifier id_aa_msgSigDigest = id_aa.branch("5"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.10 */ + static final ASN1ObjectIdentifier id_aa_contentReference = id_aa.branch("10"); + /* + * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11} + * + */ + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.11 */ + static final ASN1ObjectIdentifier id_aa_encrypKeyPref = id_aa.branch("11"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.12 */ + static final ASN1ObjectIdentifier id_aa_signingCertificate = id_aa.branch("12"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.47 */ + static final ASN1ObjectIdentifier id_aa_signingCertificateV2 = id_aa.branch("47"); + + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.7 - See RFC 2634 */ + static final ASN1ObjectIdentifier id_aa_contentIdentifier = id_aa.branch("7"); // See RFC 2634 + + /* + * RFC 3126 + */ + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.14 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_signatureTimeStampToken = id_aa.branch("14"); + + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.15 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_sigPolicyId = id_aa.branch("15"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.16 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_commitmentType = id_aa.branch("16"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.17 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_signerLocation = id_aa.branch("17"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.18 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_signerAttr = id_aa.branch("18"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.19 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_otherSigCert = id_aa.branch("19"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.20 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_contentTimestamp = id_aa.branch("20"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.21 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_certificateRefs = id_aa.branch("21"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.22 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_revocationRefs = id_aa.branch("22"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.23 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_certValues = id_aa.branch("23"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.24 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_revocationValues = id_aa.branch("24"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.25 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_escTimeStamp = id_aa.branch("25"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.26 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_certCRLTimestamp = id_aa.branch("26"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.27 - RFC 3126 */ + static final ASN1ObjectIdentifier id_aa_ets_archiveTimestamp = id_aa.branch("27"); + + /** @deprecated use id_aa_ets_sigPolicyId instead */ + static final ASN1ObjectIdentifier id_aa_sigPolicyId = id_aa_ets_sigPolicyId; + /** @deprecated use id_aa_ets_commitmentType instead */ + static final ASN1ObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType; + /** @deprecated use id_aa_ets_signerLocation instead */ + static final ASN1ObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation; + /** @deprecated use id_aa_ets_otherSigCert instead */ + static final ASN1ObjectIdentifier id_aa_otherSigCert = id_aa_ets_otherSigCert; + + /** + * id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + * rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)};
+ * 1.2.840.113549.1.9.16.5 + */ + final String id_spq = "1.2.840.113549.1.9.16.5"; + + /** SMIME SPQ URI: 1.2.840.113549.1.9.16.5.1 */ + static final ASN1ObjectIdentifier id_spq_ets_uri = new ASN1ObjectIdentifier(id_spq + ".1"); + /** SMIME SPQ UNOTICE: 1.2.840.113549.1.9.16.5.2 */ + static final ASN1ObjectIdentifier id_spq_ets_unotice = new ASN1ObjectIdentifier(id_spq + ".2"); + + // + // pkcs-12 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } + // + /** PKCS#12: 1.2.840.113549.1.12 */ + static final ASN1ObjectIdentifier pkcs_12 = new ASN1ObjectIdentifier("1.2.840.113549.1.12"); + /** PKCS#12: 1.2.840.113549.1.12.10.1 */ + static final ASN1ObjectIdentifier bagtypes = pkcs_12.branch("10.1"); + + /** PKCS#12: 1.2.840.113549.1.12.10.1.1 */ + static final ASN1ObjectIdentifier keyBag = bagtypes.branch("1"); + /** PKCS#12: 1.2.840.113549.1.12.10.1.2 */ + static final ASN1ObjectIdentifier pkcs8ShroudedKeyBag = bagtypes.branch("2"); + /** PKCS#12: 1.2.840.113549.1.12.10.1.3 */ + static final ASN1ObjectIdentifier certBag = bagtypes.branch("3"); + /** PKCS#12: 1.2.840.113549.1.12.10.1.4 */ + static final ASN1ObjectIdentifier crlBag = bagtypes.branch("4"); + /** PKCS#12: 1.2.840.113549.1.12.10.1.5 */ + static final ASN1ObjectIdentifier secretBag = bagtypes.branch("5"); + /** PKCS#12: 1.2.840.113549.1.12.10.1.6 */ + static final ASN1ObjectIdentifier safeContentsBag = bagtypes.branch("6"); + + /** PKCS#12: 1.2.840.113549.1.12.1 */ + static final ASN1ObjectIdentifier pkcs_12PbeIds = pkcs_12.branch("1"); + + /** PKCS#12: 1.2.840.113549.1.12.1.1 */ + static final ASN1ObjectIdentifier pbeWithSHAAnd128BitRC4 = pkcs_12PbeIds.branch("1"); + /** PKCS#12: 1.2.840.113549.1.12.1.2 */ + static final ASN1ObjectIdentifier pbeWithSHAAnd40BitRC4 = pkcs_12PbeIds.branch("2"); + /** PKCS#12: 1.2.840.113549.1.12.1.3 */ + static final ASN1ObjectIdentifier pbeWithSHAAnd3_KeyTripleDES_CBC = pkcs_12PbeIds.branch("3"); + /** PKCS#12: 1.2.840.113549.1.12.1.4 */ + static final ASN1ObjectIdentifier pbeWithSHAAnd2_KeyTripleDES_CBC = pkcs_12PbeIds.branch("4"); + /** PKCS#12: 1.2.840.113549.1.12.1.5 */ + static final ASN1ObjectIdentifier pbeWithSHAAnd128BitRC2_CBC = pkcs_12PbeIds.branch("5"); + /** PKCS#12: 1.2.840.113549.1.12.1.6 */ + static final ASN1ObjectIdentifier pbeWithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6"); + + /** + * PKCS#12: 1.2.840.113549.1.12.1.6 + * @deprecated use pbeWithSHAAnd40BitRC2_CBC + */ + static final ASN1ObjectIdentifier pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6"); + + /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */ + static final ASN1ObjectIdentifier id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6"); + /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */ + static final ASN1ObjectIdentifier id_alg_CMSRC2wrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7"); +} + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/Pfx.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/Pfx.java new file mode 100644 index 000000000..77a8f405e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/Pfx.java @@ -0,0 +1,87 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.BERSequence; + +/** + * the infamous Pfx from PKCS12 + */ +public class Pfx + extends ASN1Object + implements PKCSObjectIdentifiers +{ + private ContentInfo contentInfo; + private MacData macData = null; + + private Pfx( + ASN1Sequence seq) + { + BigInteger version = ((ASN1Integer)seq.getObjectAt(0)).getValue(); + if (version.intValue() != 3) + { + throw new IllegalArgumentException("wrong version for PFX PDU"); + } + + contentInfo = ContentInfo.getInstance(seq.getObjectAt(1)); + + if (seq.size() == 3) + { + macData = MacData.getInstance(seq.getObjectAt(2)); + } + } + + public static Pfx getInstance( + Object obj) + { + if (obj instanceof Pfx) + { + return (Pfx)obj; + } + + if (obj != null) + { + return new Pfx(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public Pfx( + ContentInfo contentInfo, + MacData macData) + { + this.contentInfo = contentInfo; + this.macData = macData; + } + + public ContentInfo getAuthSafe() + { + return contentInfo; + } + + public MacData getMacData() + { + return macData; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(3)); + v.add(contentInfo); + + if (macData != null) + { + v.add(macData); + } + + return new BERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PrivateKeyInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PrivateKeyInfo.java new file mode 100644 index 000000000..40dd191fc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/PrivateKeyInfo.java @@ -0,0 +1,164 @@ +package org.spongycastle.asn1.pkcs; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class PrivateKeyInfo + extends ASN1Object +{ + private ASN1OctetString privKey; + private AlgorithmIdentifier algId; + private ASN1Set attributes; + + public static PrivateKeyInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static PrivateKeyInfo getInstance( + Object obj) + { + if (obj instanceof PrivateKeyInfo) + { + return (PrivateKeyInfo)obj; + } + else if (obj != null) + { + return new PrivateKeyInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public PrivateKeyInfo( + AlgorithmIdentifier algId, + ASN1Encodable privateKey) + throws IOException + { + this(algId, privateKey, null); + } + + public PrivateKeyInfo( + AlgorithmIdentifier algId, + ASN1Encodable privateKey, + ASN1Set attributes) + throws IOException + { + this.privKey = new DEROctetString(privateKey.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + this.algId = algId; + this.attributes = attributes; + } + + /** + * @deprectaed use PrivateKeyInfo.getInstance() + * @param seq + */ + public PrivateKeyInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + BigInteger version = ((ASN1Integer)e.nextElement()).getValue(); + if (version.intValue() != 0) + { + throw new IllegalArgumentException("wrong version for private key info"); + } + + algId = AlgorithmIdentifier.getInstance(e.nextElement()); + privKey = ASN1OctetString.getInstance(e.nextElement()); + + if (e.hasMoreElements()) + { + attributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false); + } + } + + public AlgorithmIdentifier getPrivateKeyAlgorithm() + { + return algId; + } + /** + * @deprecated use getPrivateKeyAlgorithm() + */ + public AlgorithmIdentifier getAlgorithmId() + { + return algId; + } + + public ASN1Encodable parsePrivateKey() + throws IOException + { + return ASN1Primitive.fromByteArray(privKey.getOctets()); + } + + /** + * @deprecated use parsePrivateKey() + */ + public ASN1Primitive getPrivateKey() + { + try + { + return parsePrivateKey().toASN1Primitive(); + } + catch (IOException e) + { + throw new IllegalStateException("unable to parse private key"); + } + } + + public ASN1Set getAttributes() + { + return attributes; + } + + /** + * write out an RSA private key with its associated information + * as described in PKCS8. + *
+ * PrivateKeyInfo ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, + * privateKey PrivateKey, + * attributes [0] IMPLICIT Attributes OPTIONAL + * } + * Version ::= INTEGER {v1(0)} (v1,...) + * + * PrivateKey ::= OCTET STRING + * + * Attributes ::= SET OF Attribute + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(0)); + v.add(algId); + v.add(privKey); + + if (attributes != null) + { + v.add(new DERTaggedObject(false, 0, attributes)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RC2CBCParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RC2CBCParameter.java new file mode 100644 index 000000000..c054c56ef --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RC2CBCParameter.java @@ -0,0 +1,93 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class RC2CBCParameter + extends ASN1Object +{ + ASN1Integer version; + ASN1OctetString iv; + + public static RC2CBCParameter getInstance( + Object o) + { + if (o instanceof RC2CBCParameter) + { + return (RC2CBCParameter)o; + } + if (o != null) + { + return new RC2CBCParameter(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public RC2CBCParameter( + byte[] iv) + { + this.version = null; + this.iv = new DEROctetString(iv); + } + + public RC2CBCParameter( + int parameterVersion, + byte[] iv) + { + this.version = new ASN1Integer(parameterVersion); + this.iv = new DEROctetString(iv); + } + + private RC2CBCParameter( + ASN1Sequence seq) + { + if (seq.size() == 1) + { + version = null; + iv = (ASN1OctetString)seq.getObjectAt(0); + } + else + { + version = (ASN1Integer)seq.getObjectAt(0); + iv = (ASN1OctetString)seq.getObjectAt(1); + } + } + + public BigInteger getRC2ParameterVersion() + { + if (version == null) + { + return null; + } + + return version.getValue(); + } + + public byte[] getIV() + { + return iv.getOctets(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != null) + { + v.add(version); + } + + v.add(iv); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAESOAEPparams.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAESOAEPparams.java new file mode 100644 index 000000000..3b735f198 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAESOAEPparams.java @@ -0,0 +1,155 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class RSAESOAEPparams + extends ASN1Object +{ + private AlgorithmIdentifier hashAlgorithm; + private AlgorithmIdentifier maskGenAlgorithm; + private AlgorithmIdentifier pSourceAlgorithm; + + public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE); + public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM); + public final static AlgorithmIdentifier DEFAULT_P_SOURCE_ALGORITHM = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0])); + + public static RSAESOAEPparams getInstance( + Object obj) + { + if (obj instanceof RSAESOAEPparams) + { + return (RSAESOAEPparams)obj; + } + else if (obj != null) + { + return new RSAESOAEPparams(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * The default version + */ + public RSAESOAEPparams() + { + hashAlgorithm = DEFAULT_HASH_ALGORITHM; + maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION; + pSourceAlgorithm = DEFAULT_P_SOURCE_ALGORITHM; + } + + public RSAESOAEPparams( + AlgorithmIdentifier hashAlgorithm, + AlgorithmIdentifier maskGenAlgorithm, + AlgorithmIdentifier pSourceAlgorithm) + { + this.hashAlgorithm = hashAlgorithm; + this.maskGenAlgorithm = maskGenAlgorithm; + this.pSourceAlgorithm = pSourceAlgorithm; + } + + /** + * @deprecated use getInstance() + * @param seq + */ + public RSAESOAEPparams( + ASN1Sequence seq) + { + hashAlgorithm = DEFAULT_HASH_ALGORITHM; + maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION; + pSourceAlgorithm = DEFAULT_P_SOURCE_ALGORITHM; + + for (int i = 0; i != seq.size(); i++) + { + ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(i); + + switch (o.getTagNo()) + { + case 0: + hashAlgorithm = AlgorithmIdentifier.getInstance(o, true); + break; + case 1: + maskGenAlgorithm = AlgorithmIdentifier.getInstance(o, true); + break; + case 2: + pSourceAlgorithm = AlgorithmIdentifier.getInstance(o, true); + break; + default: + throw new IllegalArgumentException("unknown tag"); + } + } + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public AlgorithmIdentifier getMaskGenAlgorithm() + { + return maskGenAlgorithm; + } + + public AlgorithmIdentifier getPSourceAlgorithm() + { + return pSourceAlgorithm; + } + + /** + *
+ * RSAES-OAEP-params ::= SEQUENCE { + * hashAlgorithm [0] OAEP-PSSDigestAlgorithms DEFAULT sha1, + * maskGenAlgorithm [1] PKCS1MGFAlgorithms DEFAULT mgf1SHA1, + * pSourceAlgorithm [2] PKCS1PSourceAlgorithms DEFAULT pSpecifiedEmpty + * } + * + * OAEP-PSSDigestAlgorithms ALGORITHM-IDENTIFIER ::= { + * { OID id-sha1 PARAMETERS NULL }| + * { OID id-sha256 PARAMETERS NULL }| + * { OID id-sha384 PARAMETERS NULL }| + * { OID id-sha512 PARAMETERS NULL }, + * ... -- Allows for future expansion -- + * } + * PKCS1MGFAlgorithms ALGORITHM-IDENTIFIER ::= { + * { OID id-mgf1 PARAMETERS OAEP-PSSDigestAlgorithms }, + * ... -- Allows for future expansion -- + * } + * PKCS1PSourceAlgorithms ALGORITHM-IDENTIFIER ::= { + * { OID id-pSpecified PARAMETERS OCTET STRING }, + * ... -- Allows for future expansion -- + * } + *+ * @return the asn1 primitive representing the parameters. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (!hashAlgorithm.equals(DEFAULT_HASH_ALGORITHM)) + { + v.add(new DERTaggedObject(true, 0, hashAlgorithm)); + } + + if (!maskGenAlgorithm.equals(DEFAULT_MASK_GEN_FUNCTION)) + { + v.add(new DERTaggedObject(true, 1, maskGenAlgorithm)); + } + + if (!pSourceAlgorithm.equals(DEFAULT_P_SOURCE_ALGORITHM)) + { + v.add(new DERTaggedObject(true, 2, pSourceAlgorithm)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPrivateKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPrivateKey.java new file mode 100644 index 000000000..28d7b9457 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPrivateKey.java @@ -0,0 +1,187 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class RSAPrivateKey + extends ASN1Object +{ + private BigInteger version; + private BigInteger modulus; + private BigInteger publicExponent; + private BigInteger privateExponent; + private BigInteger prime1; + private BigInteger prime2; + private BigInteger exponent1; + private BigInteger exponent2; + private BigInteger coefficient; + private ASN1Sequence otherPrimeInfos = null; + + public static RSAPrivateKey getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RSAPrivateKey getInstance( + Object obj) + { + if (obj instanceof RSAPrivateKey) + { + return (RSAPrivateKey)obj; + } + + if (obj != null) + { + return new RSAPrivateKey(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public RSAPrivateKey( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger prime1, + BigInteger prime2, + BigInteger exponent1, + BigInteger exponent2, + BigInteger coefficient) + { + this.version = BigInteger.valueOf(0); + this.modulus = modulus; + this.publicExponent = publicExponent; + this.privateExponent = privateExponent; + this.prime1 = prime1; + this.prime2 = prime2; + this.exponent1 = exponent1; + this.exponent2 = exponent2; + this.coefficient = coefficient; + } + + private RSAPrivateKey( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + BigInteger v = ((ASN1Integer)e.nextElement()).getValue(); + if (v.intValue() != 0 && v.intValue() != 1) + { + throw new IllegalArgumentException("wrong version for RSA private key"); + } + + version = v; + modulus = ((ASN1Integer)e.nextElement()).getValue(); + publicExponent = ((ASN1Integer)e.nextElement()).getValue(); + privateExponent = ((ASN1Integer)e.nextElement()).getValue(); + prime1 = ((ASN1Integer)e.nextElement()).getValue(); + prime2 = ((ASN1Integer)e.nextElement()).getValue(); + exponent1 = ((ASN1Integer)e.nextElement()).getValue(); + exponent2 = ((ASN1Integer)e.nextElement()).getValue(); + coefficient = ((ASN1Integer)e.nextElement()).getValue(); + + if (e.hasMoreElements()) + { + otherPrimeInfos = (ASN1Sequence)e.nextElement(); + } + } + + public BigInteger getVersion() + { + return version; + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return publicExponent; + } + + public BigInteger getPrivateExponent() + { + return privateExponent; + } + + public BigInteger getPrime1() + { + return prime1; + } + + public BigInteger getPrime2() + { + return prime2; + } + + public BigInteger getExponent1() + { + return exponent1; + } + + public BigInteger getExponent2() + { + return exponent2; + } + + public BigInteger getCoefficient() + { + return coefficient; + } + + /** + * This outputs the key in PKCS1v2 format. + *
+ * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + * + * Version ::= INTEGER { two-prime(0), multi(1) } + * (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --}) + *+ *
+ * This routine is written to output PKCS1 version 2.1, private keys. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(version)); // version + v.add(new ASN1Integer(getModulus())); + v.add(new ASN1Integer(getPublicExponent())); + v.add(new ASN1Integer(getPrivateExponent())); + v.add(new ASN1Integer(getPrime1())); + v.add(new ASN1Integer(getPrime2())); + v.add(new ASN1Integer(getExponent1())); + v.add(new ASN1Integer(getExponent2())); + v.add(new ASN1Integer(getCoefficient())); + + if (otherPrimeInfos != null) + { + v.add(otherPrimeInfos); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPrivateKeyStructure.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPrivateKeyStructure.java new file mode 100644 index 000000000..7671a518d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPrivateKeyStructure.java @@ -0,0 +1,189 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +/** + * @deprecated use RSAPrivateKey + */ +public class RSAPrivateKeyStructure + extends ASN1Object +{ + private int version; + private BigInteger modulus; + private BigInteger publicExponent; + private BigInteger privateExponent; + private BigInteger prime1; + private BigInteger prime2; + private BigInteger exponent1; + private BigInteger exponent2; + private BigInteger coefficient; + private ASN1Sequence otherPrimeInfos = null; + + public static RSAPrivateKeyStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RSAPrivateKeyStructure getInstance( + Object obj) + { + if (obj instanceof RSAPrivateKeyStructure) + { + return (RSAPrivateKeyStructure)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new RSAPrivateKeyStructure((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public RSAPrivateKeyStructure( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger prime1, + BigInteger prime2, + BigInteger exponent1, + BigInteger exponent2, + BigInteger coefficient) + { + this.version = 0; + this.modulus = modulus; + this.publicExponent = publicExponent; + this.privateExponent = privateExponent; + this.prime1 = prime1; + this.prime2 = prime2; + this.exponent1 = exponent1; + this.exponent2 = exponent2; + this.coefficient = coefficient; + } + + public RSAPrivateKeyStructure( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + BigInteger v = ((ASN1Integer)e.nextElement()).getValue(); + if (v.intValue() != 0 && v.intValue() != 1) + { + throw new IllegalArgumentException("wrong version for RSA private key"); + } + + version = v.intValue(); + modulus = ((ASN1Integer)e.nextElement()).getValue(); + publicExponent = ((ASN1Integer)e.nextElement()).getValue(); + privateExponent = ((ASN1Integer)e.nextElement()).getValue(); + prime1 = ((ASN1Integer)e.nextElement()).getValue(); + prime2 = ((ASN1Integer)e.nextElement()).getValue(); + exponent1 = ((ASN1Integer)e.nextElement()).getValue(); + exponent2 = ((ASN1Integer)e.nextElement()).getValue(); + coefficient = ((ASN1Integer)e.nextElement()).getValue(); + + if (e.hasMoreElements()) + { + otherPrimeInfos = (ASN1Sequence)e.nextElement(); + } + } + + public int getVersion() + { + return version; + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return publicExponent; + } + + public BigInteger getPrivateExponent() + { + return privateExponent; + } + + public BigInteger getPrime1() + { + return prime1; + } + + public BigInteger getPrime2() + { + return prime2; + } + + public BigInteger getExponent1() + { + return exponent1; + } + + public BigInteger getExponent2() + { + return exponent2; + } + + public BigInteger getCoefficient() + { + return coefficient; + } + + /** + * This outputs the key in PKCS1v2 format. + *
+ * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + * + * Version ::= INTEGER { two-prime(0), multi(1) } + * (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --}) + *+ *
+ * This routine is written to output PKCS1 version 2.1, private keys. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(version)); // version + v.add(new ASN1Integer(getModulus())); + v.add(new ASN1Integer(getPublicExponent())); + v.add(new ASN1Integer(getPrivateExponent())); + v.add(new ASN1Integer(getPrime1())); + v.add(new ASN1Integer(getPrime2())); + v.add(new ASN1Integer(getExponent1())); + v.add(new ASN1Integer(getExponent2())); + v.add(new ASN1Integer(getCoefficient())); + + if (otherPrimeInfos != null) + { + v.add(otherPrimeInfos); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPublicKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPublicKey.java new file mode 100644 index 000000000..5ea0172a7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSAPublicKey.java @@ -0,0 +1,95 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class RSAPublicKey + extends ASN1Object +{ + private BigInteger modulus; + private BigInteger publicExponent; + + public static RSAPublicKey getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RSAPublicKey getInstance( + Object obj) + { + if (obj instanceof RSAPublicKey) + { + return (RSAPublicKey)obj; + } + + if (obj != null) + { + return new RSAPublicKey(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public RSAPublicKey( + BigInteger modulus, + BigInteger publicExponent) + { + this.modulus = modulus; + this.publicExponent = publicExponent; + } + + private RSAPublicKey( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + modulus = ASN1Integer.getInstance(e.nextElement()).getPositiveValue(); + publicExponent = ASN1Integer.getInstance(e.nextElement()).getPositiveValue(); + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return publicExponent; + } + + /** + * This outputs the key in PKCS1v2 format. + *
+ * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * } + *+ *
+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(getModulus())); + v.add(new ASN1Integer(getPublicExponent())); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSASSAPSSparams.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSASSAPSSparams.java new file mode 100644 index 000000000..bca4e8c05 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/RSASSAPSSparams.java @@ -0,0 +1,172 @@ +package org.spongycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class RSASSAPSSparams + extends ASN1Object +{ + private AlgorithmIdentifier hashAlgorithm; + private AlgorithmIdentifier maskGenAlgorithm; + private ASN1Integer saltLength; + private ASN1Integer trailerField; + + public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE); + public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM); + public final static ASN1Integer DEFAULT_SALT_LENGTH = new ASN1Integer(20); + public final static ASN1Integer DEFAULT_TRAILER_FIELD = new ASN1Integer(1); + + public static RSASSAPSSparams getInstance( + Object obj) + { + if (obj instanceof RSASSAPSSparams) + { + return (RSASSAPSSparams)obj; + } + else if (obj != null) + { + return new RSASSAPSSparams(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * The default version + */ + public RSASSAPSSparams() + { + hashAlgorithm = DEFAULT_HASH_ALGORITHM; + maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION; + saltLength = DEFAULT_SALT_LENGTH; + trailerField = DEFAULT_TRAILER_FIELD; + } + + public RSASSAPSSparams( + AlgorithmIdentifier hashAlgorithm, + AlgorithmIdentifier maskGenAlgorithm, + ASN1Integer saltLength, + ASN1Integer trailerField) + { + this.hashAlgorithm = hashAlgorithm; + this.maskGenAlgorithm = maskGenAlgorithm; + this.saltLength = saltLength; + this.trailerField = trailerField; + } + + private RSASSAPSSparams( + ASN1Sequence seq) + { + hashAlgorithm = DEFAULT_HASH_ALGORITHM; + maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION; + saltLength = DEFAULT_SALT_LENGTH; + trailerField = DEFAULT_TRAILER_FIELD; + + for (int i = 0; i != seq.size(); i++) + { + ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(i); + + switch (o.getTagNo()) + { + case 0: + hashAlgorithm = AlgorithmIdentifier.getInstance(o, true); + break; + case 1: + maskGenAlgorithm = AlgorithmIdentifier.getInstance(o, true); + break; + case 2: + saltLength = ASN1Integer.getInstance(o, true); + break; + case 3: + trailerField = ASN1Integer.getInstance(o, true); + break; + default: + throw new IllegalArgumentException("unknown tag"); + } + } + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public AlgorithmIdentifier getMaskGenAlgorithm() + { + return maskGenAlgorithm; + } + + public BigInteger getSaltLength() + { + return saltLength.getValue(); + } + + public BigInteger getTrailerField() + { + return trailerField.getValue(); + } + + /** + *
+ * RSASSA-PSS-params ::= SEQUENCE { + * hashAlgorithm [0] OAEP-PSSDigestAlgorithms DEFAULT sha1, + * maskGenAlgorithm [1] PKCS1MGFAlgorithms DEFAULT mgf1SHA1, + * saltLength [2] INTEGER DEFAULT 20, + * trailerField [3] TrailerField DEFAULT trailerFieldBC + * } + * + * OAEP-PSSDigestAlgorithms ALGORITHM-IDENTIFIER ::= { + * { OID id-sha1 PARAMETERS NULL }| + * { OID id-sha256 PARAMETERS NULL }| + * { OID id-sha384 PARAMETERS NULL }| + * { OID id-sha512 PARAMETERS NULL }, + * ... -- Allows for future expansion -- + * } + * + * PKCS1MGFAlgorithms ALGORITHM-IDENTIFIER ::= { + * { OID id-mgf1 PARAMETERS OAEP-PSSDigestAlgorithms }, + * ... -- Allows for future expansion -- + * } + * + * TrailerField ::= INTEGER { trailerFieldBC(1) } + *+ * @return the asn1 primitive representing the parameters. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (!hashAlgorithm.equals(DEFAULT_HASH_ALGORITHM)) + { + v.add(new DERTaggedObject(true, 0, hashAlgorithm)); + } + + if (!maskGenAlgorithm.equals(DEFAULT_MASK_GEN_FUNCTION)) + { + v.add(new DERTaggedObject(true, 1, maskGenAlgorithm)); + } + + if (!saltLength.equals(DEFAULT_SALT_LENGTH)) + { + v.add(new DERTaggedObject(true, 2, saltLength)); + } + + if (!trailerField.equals(DEFAULT_TRAILER_FIELD)) + { + v.add(new DERTaggedObject(true, 3, trailerField)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SafeBag.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SafeBag.java new file mode 100644 index 000000000..b2201413c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SafeBag.java @@ -0,0 +1,96 @@ +package org.spongycastle.asn1.pkcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DLSequence; +import org.spongycastle.asn1.DLTaggedObject; + +public class SafeBag + extends ASN1Object +{ + private ASN1ObjectIdentifier bagId; + private ASN1Encodable bagValue; + private ASN1Set bagAttributes; + + public SafeBag( + ASN1ObjectIdentifier oid, + ASN1Encodable obj) + { + this.bagId = oid; + this.bagValue = obj; + this.bagAttributes = null; + } + + public SafeBag( + ASN1ObjectIdentifier oid, + ASN1Encodable obj, + ASN1Set bagAttributes) + { + this.bagId = oid; + this.bagValue = obj; + this.bagAttributes = bagAttributes; + } + + public static SafeBag getInstance( + Object obj) + { + if (obj instanceof SafeBag) + { + return (SafeBag)obj; + } + + if (obj != null) + { + return new SafeBag(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private SafeBag( + ASN1Sequence seq) + { + this.bagId = (ASN1ObjectIdentifier)seq.getObjectAt(0); + this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject(); + if (seq.size() == 3) + { + this.bagAttributes = (ASN1Set)seq.getObjectAt(2); + } + } + + public ASN1ObjectIdentifier getBagId() + { + return bagId; + } + + public ASN1Encodable getBagValue() + { + return bagValue; + } + + public ASN1Set getBagAttributes() + { + return bagAttributes; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(bagId); + v.add(new DLTaggedObject(true, 0, bagValue)); + + if (bagAttributes != null) + { + v.add(bagAttributes); + } + + return new DLSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SignedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SignedData.java new file mode 100644 index 000000000..c99fe6929 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SignedData.java @@ -0,0 +1,167 @@ +package org.spongycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * a PKCS#7 signed data object. + */ +public class SignedData + extends ASN1Object + implements PKCSObjectIdentifiers +{ + private ASN1Integer version; + private ASN1Set digestAlgorithms; + private ContentInfo contentInfo; + private ASN1Set certificates; + private ASN1Set crls; + private ASN1Set signerInfos; + + public static SignedData getInstance( + Object o) + { + if (o instanceof SignedData) + { + return (SignedData)o; + } + else if (o != null) + { + return new SignedData(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public SignedData( + ASN1Integer _version, + ASN1Set _digestAlgorithms, + ContentInfo _contentInfo, + ASN1Set _certificates, + ASN1Set _crls, + ASN1Set _signerInfos) + { + version = _version; + digestAlgorithms = _digestAlgorithms; + contentInfo = _contentInfo; + certificates = _certificates; + crls = _crls; + signerInfos = _signerInfos; + } + + public SignedData( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + version = (ASN1Integer)e.nextElement(); + digestAlgorithms = ((ASN1Set)e.nextElement()); + contentInfo = ContentInfo.getInstance(e.nextElement()); + + while (e.hasMoreElements()) + { + ASN1Primitive o = (ASN1Primitive)e.nextElement(); + + // + // an interesting feature of SignedData is that there appear to be varying implementations... + // for the moment we ignore anything which doesn't fit. + // + if (o instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagged = (ASN1TaggedObject)o; + + switch (tagged.getTagNo()) + { + case 0: + certificates = ASN1Set.getInstance(tagged, false); + break; + case 1: + crls = ASN1Set.getInstance(tagged, false); + break; + default: + throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo()); + } + } + else + { + signerInfos = (ASN1Set)o; + } + } + } + + public ASN1Integer getVersion() + { + return version; + } + + public ASN1Set getDigestAlgorithms() + { + return digestAlgorithms; + } + + public ContentInfo getContentInfo() + { + return contentInfo; + } + + public ASN1Set getCertificates() + { + return certificates; + } + + public ASN1Set getCRLs() + { + return crls; + } + + public ASN1Set getSignerInfos() + { + return signerInfos; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * SignedData ::= SEQUENCE { + * version Version, + * digestAlgorithms DigestAlgorithmIdentifiers, + * contentInfo ContentInfo, + * certificates + * [0] IMPLICIT ExtendedCertificatesAndCertificates + * OPTIONAL, + * crls + * [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(digestAlgorithms); + v.add(contentInfo); + + if (certificates != null) + { + v.add(new DERTaggedObject(false, 0, certificates)); + } + + if (crls != null) + { + v.add(new DERTaggedObject(false, 1, crls)); + } + + v.add(signerInfos); + + return new BERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SignerInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SignerInfo.java new file mode 100644 index 000000000..9285d0b39 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/pkcs/SignerInfo.java @@ -0,0 +1,178 @@ +package org.spongycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * a PKCS#7 signer info object. + */ +public class SignerInfo + extends ASN1Object +{ + private ASN1Integer version; + private IssuerAndSerialNumber issuerAndSerialNumber; + private AlgorithmIdentifier digAlgorithm; + private ASN1Set authenticatedAttributes; + private AlgorithmIdentifier digEncryptionAlgorithm; + private ASN1OctetString encryptedDigest; + private ASN1Set unauthenticatedAttributes; + + public static SignerInfo getInstance( + Object o) + { + if (o instanceof SignerInfo) + { + return (SignerInfo)o; + } + else if (o instanceof ASN1Sequence) + { + return new SignerInfo((ASN1Sequence)o); + } + + throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName()); + } + + public SignerInfo( + ASN1Integer version, + IssuerAndSerialNumber issuerAndSerialNumber, + AlgorithmIdentifier digAlgorithm, + ASN1Set authenticatedAttributes, + AlgorithmIdentifier digEncryptionAlgorithm, + ASN1OctetString encryptedDigest, + ASN1Set unauthenticatedAttributes) + { + this.version = version; + this.issuerAndSerialNumber = issuerAndSerialNumber; + this.digAlgorithm = digAlgorithm; + this.authenticatedAttributes = authenticatedAttributes; + this.digEncryptionAlgorithm = digEncryptionAlgorithm; + this.encryptedDigest = encryptedDigest; + this.unauthenticatedAttributes = unauthenticatedAttributes; + } + + public SignerInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + version = (ASN1Integer)e.nextElement(); + issuerAndSerialNumber = IssuerAndSerialNumber.getInstance(e.nextElement()); + digAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement()); + + Object obj = e.nextElement(); + + if (obj instanceof ASN1TaggedObject) + { + authenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)obj, false); + + digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement()); + } + else + { + authenticatedAttributes = null; + digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(obj); + } + + encryptedDigest = DEROctetString.getInstance(e.nextElement()); + + if (e.hasMoreElements()) + { + unauthenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false); + } + else + { + unauthenticatedAttributes = null; + } + } + + public ASN1Integer getVersion() + { + return version; + } + + public IssuerAndSerialNumber getIssuerAndSerialNumber() + { + return issuerAndSerialNumber; + } + + public ASN1Set getAuthenticatedAttributes() + { + return authenticatedAttributes; + } + + public AlgorithmIdentifier getDigestAlgorithm() + { + return digAlgorithm; + } + + public ASN1OctetString getEncryptedDigest() + { + return encryptedDigest; + } + + public AlgorithmIdentifier getDigestEncryptionAlgorithm() + { + return digEncryptionAlgorithm; + } + + public ASN1Set getUnauthenticatedAttributes() + { + return unauthenticatedAttributes; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * SignerInfo ::= SEQUENCE { + * version Version, + * issuerAndSerialNumber IssuerAndSerialNumber, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + * } + * + * EncryptedDigest ::= OCTET STRING + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(issuerAndSerialNumber); + v.add(digAlgorithm); + + if (authenticatedAttributes != null) + { + v.add(new DERTaggedObject(false, 0, authenticatedAttributes)); + } + + v.add(digEncryptionAlgorithm); + v.add(encryptedDigest); + + if (unauthenticatedAttributes != null) + { + v.add(new DERTaggedObject(false, 1, unauthenticatedAttributes)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/ECPrivateKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/ECPrivateKey.java new file mode 100644 index 000000000..288ec9d0b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/ECPrivateKey.java @@ -0,0 +1,143 @@ +package org.spongycastle.asn1.sec; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.util.BigIntegers; + +/** + * the elliptic curve private key object from SEC 1 + */ +public class ECPrivateKey + extends ASN1Object +{ + private ASN1Sequence seq; + + private ECPrivateKey( + ASN1Sequence seq) + { + this.seq = seq; + } + + public static ECPrivateKey getInstance( + Object obj) + { + if (obj instanceof ECPrivateKey) + { + return (ECPrivateKey)obj; + } + + if (obj != null) + { + return new ECPrivateKey(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ECPrivateKey( + BigInteger key) + { + byte[] bytes = BigIntegers.asUnsignedByteArray(key); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(1)); + v.add(new DEROctetString(bytes)); + + seq = new DERSequence(v); + } + + public ECPrivateKey( + BigInteger key, + ASN1Encodable parameters) + { + this(key, null, parameters); + } + + public ECPrivateKey( + BigInteger key, + DERBitString publicKey, + ASN1Encodable parameters) + { + byte[] bytes = BigIntegers.asUnsignedByteArray(key); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(1)); + v.add(new DEROctetString(bytes)); + + if (parameters != null) + { + v.add(new DERTaggedObject(true, 0, parameters)); + } + + if (publicKey != null) + { + v.add(new DERTaggedObject(true, 1, publicKey)); + } + + seq = new DERSequence(v); + } + + public BigInteger getKey() + { + ASN1OctetString octs = (ASN1OctetString)seq.getObjectAt(1); + + return new BigInteger(1, octs.getOctets()); + } + + public DERBitString getPublicKey() + { + return (DERBitString)getObjectInTag(1); + } + + public ASN1Primitive getParameters() + { + return getObjectInTag(0); + } + + private ASN1Primitive getObjectInTag(int tagNo) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Encodable obj = (ASN1Encodable)e.nextElement(); + + if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject tag = (ASN1TaggedObject)obj; + if (tag.getTagNo() == tagNo) + { + return tag.getObject().toASN1Primitive(); + } + } + } + return null; + } + + /** + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] Parameters OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL } + */ + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/ECPrivateKeyStructure.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/ECPrivateKeyStructure.java new file mode 100644 index 000000000..a1f637c72 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/ECPrivateKeyStructure.java @@ -0,0 +1,128 @@ +package org.spongycastle.asn1.sec; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.util.BigIntegers; + +/** + * the elliptic curve private key object from SEC 1 + * @deprecated use ECPrivateKey + */ +public class ECPrivateKeyStructure + extends ASN1Object +{ + private ASN1Sequence seq; + + public ECPrivateKeyStructure( + ASN1Sequence seq) + { + this.seq = seq; + } + + public ECPrivateKeyStructure( + BigInteger key) + { + byte[] bytes = BigIntegers.asUnsignedByteArray(key); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(1)); + v.add(new DEROctetString(bytes)); + + seq = new DERSequence(v); + } + + public ECPrivateKeyStructure( + BigInteger key, + ASN1Encodable parameters) + { + this(key, null, parameters); + } + + public ECPrivateKeyStructure( + BigInteger key, + DERBitString publicKey, + ASN1Encodable parameters) + { + byte[] bytes = BigIntegers.asUnsignedByteArray(key); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(1)); + v.add(new DEROctetString(bytes)); + + if (parameters != null) + { + v.add(new DERTaggedObject(true, 0, parameters)); + } + + if (publicKey != null) + { + v.add(new DERTaggedObject(true, 1, publicKey)); + } + + seq = new DERSequence(v); + } + + public BigInteger getKey() + { + ASN1OctetString octs = (ASN1OctetString)seq.getObjectAt(1); + + return new BigInteger(1, octs.getOctets()); + } + + public DERBitString getPublicKey() + { + return (DERBitString)getObjectInTag(1); + } + + public ASN1Primitive getParameters() + { + return getObjectInTag(0); + } + + private ASN1Primitive getObjectInTag(int tagNo) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Encodable obj = (ASN1Encodable)e.nextElement(); + + if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject tag = (ASN1TaggedObject)obj; + if (tag.getTagNo() == tagNo) + { + return (ASN1Primitive)((ASN1Encodable)tag.getObject()).toASN1Primitive(); + } + } + } + return null; + } + + /** + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] Parameters OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL } + */ + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/SECNamedCurves.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/SECNamedCurves.java new file mode 100644 index 000000000..1114d8c12 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/SECNamedCurves.java @@ -0,0 +1,1045 @@ +package org.spongycastle.asn1.sec; + +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECParametersHolder; +import org.spongycastle.math.ec.ECConstants; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; + +public class SECNamedCurves +{ + private static ECCurve configureCurve(ECCurve curve) + { +// int coord = ECCurve.COORD_JACOBIAN_MODIFIED; +// +// if (curve.getCoordinateSystem() != coord && curve.supportsCoordinateSystem(coord)) +// { +// return curve.configure() +// .setCoordinateSystem(coord) +//// .setMultiplier(new WNafL2RMultiplier()) +// .create(); +// } + + return curve; + } + + private static BigInteger fromHex( + String hex) + { + return new BigInteger(1, Hex.decode(hex)); + } + + /* + * secp112r1 + */ + static X9ECParametersHolder secp112r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = (2^128 - 3) / 76439 + BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B"); + BigInteger a = fromHex("DB7C2ABF62E35E668076BEAD2088"); + BigInteger b = fromHex("659EF8BA043916EEDE8911702B22"); + byte[] S = Hex.decode("00F50B028E4D696E676875615175290472783FB1"); + BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "09487239995A5EE76B55F9C2F098")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "09487239995A5EE76B55F9C2F098" + + "A89CE5AF8724C0A23E0E0FF77500")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp112r2 + */ + static X9ECParametersHolder secp112r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = (2^128 - 3) / 76439 + BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B"); + BigInteger a = fromHex("6127C24C05F38A0AAAF65C0EF02C"); + BigInteger b = fromHex("51DEF1815DB5ED74FCC34C85D709"); + byte[] S = Hex.decode("002757A1114D696E6768756151755316C05E0BD4"); + BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "4BA30AB5E892B4E1649DD0928643")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "4BA30AB5E892B4E1649DD0928643" + + "ADCD46F5882E3747DEF36E956E97")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp128r1 + */ + static X9ECParametersHolder secp128r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^128 - 2^97 - 1 + BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"); + BigInteger a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"); + BigInteger b = fromHex("E87579C11079F43DD824993C2CEE5ED3"); + byte[] S = Hex.decode("000E0D4D696E6768756151750CC03A4473D03679"); + BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "161FF7528B899B2D0C28607CA52C5B86")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "161FF7528B899B2D0C28607CA52C5B86" + + "CF5AC8395BAFEB13C02DA292DDED7A83")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp128r2 + */ + static X9ECParametersHolder secp128r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^128 - 2^97 - 1 + BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"); + BigInteger a = fromHex("D6031998D1B3BBFEBF59CC9BBFF9AEE1"); + BigInteger b = fromHex("5EEEFCA380D02919DC2C6558BB6D8A5D"); + byte[] S = Hex.decode("004D696E67687561517512D8F03431FCE63B88F4"); + BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "7B6AA5D85E572983E6FB32A7CDEBC140")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "7B6AA5D85E572983E6FB32A7CDEBC140" + + "27B6916A894D3AEE7106FE805FC34B44")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp160k1 + */ + static X9ECParametersHolder secp160k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"); + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(7); + byte[] S = null; + BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); +// ECPoint G = curve.decodePoint(Hex.decode("02" +// + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + + "938CF935318FDCED6BC28286531733C3F03C4FEE")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp160r1 + */ + static X9ECParametersHolder secp160r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^160 - 2^31 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"); + BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"); + BigInteger b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"); + byte[] S = Hex.decode("1053CDE42C14D696E67687561517533BF3F83345"); + BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "4A96B5688EF573284664698968C38BB913CBFC82")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "4A96B5688EF573284664698968C38BB913CBFC82" + + "23A628553168947D59DCC912042351377AC5FB32")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp160r2 + */ + static X9ECParametersHolder secp160r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"); + BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70"); + BigInteger b = fromHex("B4E134D3FB59EB8BAB57274904664D5AF50388BA"); + byte[] S = Hex.decode("B99B99B099B323E02709A4D696E6768756151751"); + BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "52DCB034293A117E1F4FF11B30F7199D3144CE6D")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "52DCB034293A117E1F4FF11B30F7199D3144CE6D" + + "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp192k1 + */ + static X9ECParametersHolder secp192k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"); + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(3); + byte[] S = null; + BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp192r1 + */ + static X9ECParametersHolder secp192r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^192 - 2^64 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"); + BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"); + BigInteger b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"); + byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); + BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp224k1 + */ + static X9ECParametersHolder secp224k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D"); + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(5); + byte[] S = null; + BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C" + + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp224r1 + */ + static X9ECParametersHolder secp224r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^224 - 2^96 + 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"); + BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"); + BigInteger b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"); + byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); + BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp256k1 + */ + static X9ECParametersHolder secp256k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(7); + byte[] S = null; + BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp256r1 + */ + static X9ECParametersHolder secp256r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1 + BigInteger p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + BigInteger a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + BigInteger b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); + byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90"); + BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp384r1 + */ + static X9ECParametersHolder secp384r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^384 - 2^128 - 2^96 + 2^32 - 1 + BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF"); + BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC"); + BigInteger b = fromHex("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF"); + byte[] S = Hex.decode("A335926AA319A27A1D00896A6773A4827ACDAC73"); + BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7" + + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * secp521r1 + */ + static X9ECParametersHolder secp521r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + // p = 2^521 - 1 + BigInteger p = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + BigInteger a = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC"); + BigInteger b = fromHex("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00"); + byte[] S = Hex.decode("D09E8800291CB85396CC6717393284AAA0DA64BA"); + BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66" + + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect113r1 + */ + static X9ECParametersHolder sect113r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 113; + int k = 9; + + BigInteger a = fromHex("003088250CA6E7C7FE649CE85820F7"); + BigInteger b = fromHex("00E8BEE4D3E2260744188BE0E9C723"); + byte[] S = Hex.decode("10E723AB14D696E6768756151756FEBF8FCB49A9"); + BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "009D73616F35F4AB1407D73562C10F")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "009D73616F35F4AB1407D73562C10F" + + "00A52830277958EE84D1315ED31886")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect113r2 + */ + static X9ECParametersHolder sect113r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 113; + int k = 9; + + BigInteger a = fromHex("00689918DBEC7E5A0DD6DFC0AA55C7"); + BigInteger b = fromHex("0095E9A9EC9B297BD4BF36E059184F"); + byte[] S = Hex.decode("10C0FB15760860DEF1EEF4D696E676875615175D"); + BigInteger n = fromHex("010000000000000108789B2496AF93"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "01A57A6A7B26CA5EF52FCDB8164797")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "01A57A6A7B26CA5EF52FCDB8164797" + + "00B3ADC94ED1FE674C06E695BABA1D")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect131r1 + */ + static X9ECParametersHolder sect131r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 131; + int k1 = 2; + int k2 = 3; + int k3 = 8; + + BigInteger a = fromHex("07A11B09A76B562144418FF3FF8C2570B8"); + BigInteger b = fromHex("0217C05610884B63B9C6C7291678F9D341"); + byte[] S = Hex.decode("4D696E676875615175985BD3ADBADA21B43A97E2"); + BigInteger n = fromHex("0400000000000000023123953A9464B54D"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "0081BAF91FDF9833C40F9C181343638399")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "0081BAF91FDF9833C40F9C181343638399" + + "078C6E7EA38C001F73C8134B1B4EF9E150")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect131r2 + */ + static X9ECParametersHolder sect131r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 131; + int k1 = 2; + int k2 = 3; + int k3 = 8; + + BigInteger a = fromHex("03E5A88919D7CAFCBF415F07C2176573B2"); + BigInteger b = fromHex("04B8266A46C55657AC734CE38F018F2192"); + byte[] S = Hex.decode("985BD3ADBAD4D696E676875615175A21B43A97E3"); + BigInteger n = fromHex("0400000000000000016954A233049BA98F"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "0356DCD8F2F95031AD652D23951BB366A8")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "0356DCD8F2F95031AD652D23951BB366A8" + + "0648F06D867940A5366D9E265DE9EB240F")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect163k1 + */ + static X9ECParametersHolder sect163k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 163; + int k1 = 3; + int k2 = 6; + int k3 = 7; + + BigInteger a = BigInteger.valueOf(1); + BigInteger b = BigInteger.valueOf(1); + byte[] S = null; + BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8" + + "0289070FB05D38FF58321F2E800536D538CCDAA3D9")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect163r1 + */ + static X9ECParametersHolder sect163r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 163; + int k1 = 3; + int k2 = 6; + int k3 = 7; + + BigInteger a = fromHex("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2"); + BigInteger b = fromHex("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9"); + byte[] S = Hex.decode("24B7B137C8A14D696E6768756151756FD0DA2E5C"); + BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "0369979697AB43897789566789567F787A7876A654")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "0369979697AB43897789566789567F787A7876A654" + + "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect163r2 + */ + static X9ECParametersHolder sect163r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 163; + int k1 = 3; + int k2 = 6; + int k3 = 7; + + BigInteger a = BigInteger.valueOf(1); + BigInteger b = fromHex("020A601907B8C953CA1481EB10512F78744A3205FD"); + byte[] S = Hex.decode("85E25BFE5C86226CDB12016F7553F9D0E693A268"); + BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "03F0EBA16286A2D57EA0991168D4994637E8343E36")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "03F0EBA16286A2D57EA0991168D4994637E8343E36" + + "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect193r1 + */ + static X9ECParametersHolder sect193r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 193; + int k = 15; + + BigInteger a = fromHex("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01"); + BigInteger b = fromHex("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814"); + byte[] S = Hex.decode("103FAEC74D696E676875615175777FC5B191EF30"); + BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1" + + "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect193r2 + */ + static X9ECParametersHolder sect193r2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 193; + int k = 15; + + BigInteger a = fromHex("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B"); + BigInteger b = fromHex("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE"); + byte[] S = Hex.decode("10B7B4D696E676875615175137C8A16FD0DA2211"); + BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F" + + "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect233k1 + */ + static X9ECParametersHolder sect233k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 233; + int k = 74; + + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(1); + byte[] S = null; + BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126" + + "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect233r1 + */ + static X9ECParametersHolder sect233r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 233; + int k = 74; + + BigInteger a = BigInteger.valueOf(1); + BigInteger b = fromHex("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD"); + byte[] S = Hex.decode("74D59FF07F6B413D0EA14B344B20A2DB049B50C3"); + BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B" + + "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect239k1 + */ + static X9ECParametersHolder sect239k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 239; + int k = 158; + + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(1); + byte[] S = null; + BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC" + + "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect283k1 + */ + static X9ECParametersHolder sect283k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 283; + int k1 = 5; + int k2 = 7; + int k3 = 12; + + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(1); + byte[] S = null; + BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836" + + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect283r1 + */ + static X9ECParametersHolder sect283r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 283; + int k1 = 5; + int k2 = 7; + int k3 = 12; + + BigInteger a = BigInteger.valueOf(1); + BigInteger b = fromHex("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5"); + byte[] S = Hex.decode("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE"); + BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053" + + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect409k1 + */ + static X9ECParametersHolder sect409k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 409; + int k = 87; + + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(1); + byte[] S = null; + BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746" + + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect409r1 + */ + static X9ECParametersHolder sect409r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 409; + int k = 87; + + BigInteger a = BigInteger.valueOf(1); + BigInteger b = fromHex("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F"); + byte[] S = Hex.decode("4099B5A457F9D69F79213D094C4BCD4D4262210B"); + BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7" + + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect571k1 + */ + static X9ECParametersHolder sect571k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 571; + int k1 = 2; + int k2 = 5; + int k3 = 10; + + BigInteger a = ECConstants.ZERO; + BigInteger b = BigInteger.valueOf(1); + byte[] S = null; + BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001"); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("02" + //+ "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972" + + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + /* + * sect571r1 + */ + static X9ECParametersHolder sect571r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + int m = 571; + int k1 = 2; + int k2 = 5; + int k3 = 10; + + BigInteger a = BigInteger.valueOf(1); + BigInteger b = fromHex("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A"); + byte[] S = Hex.decode("2AA058F73A0E33AB486B0F610410C53A7F132310"); + BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47"); + BigInteger h = BigInteger.valueOf(2); + + ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h)); + //ECPoint G = curve.decodePoint(Hex.decode("03" + //+ "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19")); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19" + + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + + static final Hashtable objIds = new Hashtable(); + static final Hashtable curves = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder) + { + objIds.put(name, oid); + names.put(oid, name); + curves.put(oid, holder); + } + + static + { + defineCurve("secp112r1", SECObjectIdentifiers.secp112r1, secp112r1); + defineCurve("secp112r2", SECObjectIdentifiers.secp112r2, secp112r2); + defineCurve("secp128r1", SECObjectIdentifiers.secp128r1, secp128r1); + defineCurve("secp128r2", SECObjectIdentifiers.secp128r2, secp128r2); + defineCurve("secp160k1", SECObjectIdentifiers.secp160k1, secp160k1); + defineCurve("secp160r1", SECObjectIdentifiers.secp160r1, secp160r1); + defineCurve("secp160r2", SECObjectIdentifiers.secp160r2, secp160r2); + defineCurve("secp192k1", SECObjectIdentifiers.secp192k1, secp192k1); + defineCurve("secp192r1", SECObjectIdentifiers.secp192r1, secp192r1); + defineCurve("secp224k1", SECObjectIdentifiers.secp224k1, secp224k1); + defineCurve("secp224r1", SECObjectIdentifiers.secp224r1, secp224r1); + defineCurve("secp256k1", SECObjectIdentifiers.secp256k1, secp256k1); + defineCurve("secp256r1", SECObjectIdentifiers.secp256r1, secp256r1); + defineCurve("secp384r1", SECObjectIdentifiers.secp384r1, secp384r1); + defineCurve("secp521r1", SECObjectIdentifiers.secp521r1, secp521r1); + + defineCurve("sect113r1", SECObjectIdentifiers.sect113r1, sect113r1); + defineCurve("sect113r2", SECObjectIdentifiers.sect113r2, sect113r2); + defineCurve("sect131r1", SECObjectIdentifiers.sect131r1, sect131r1); + defineCurve("sect131r2", SECObjectIdentifiers.sect131r2, sect131r2); + defineCurve("sect163k1", SECObjectIdentifiers.sect163k1, sect163k1); + defineCurve("sect163r1", SECObjectIdentifiers.sect163r1, sect163r1); + defineCurve("sect163r2", SECObjectIdentifiers.sect163r2, sect163r2); + defineCurve("sect193r1", SECObjectIdentifiers.sect193r1, sect193r1); + defineCurve("sect193r2", SECObjectIdentifiers.sect193r2, sect193r2); + defineCurve("sect233k1", SECObjectIdentifiers.sect233k1, sect233k1); + defineCurve("sect233r1", SECObjectIdentifiers.sect233r1, sect233r1); + defineCurve("sect239k1", SECObjectIdentifiers.sect239k1, sect239k1); + defineCurve("sect283k1", SECObjectIdentifiers.sect283k1, sect283k1); + defineCurve("sect283r1", SECObjectIdentifiers.sect283r1, sect283r1); + defineCurve("sect409k1", SECObjectIdentifiers.sect409k1, sect409k1); + defineCurve("sect409r1", SECObjectIdentifiers.sect409r1, sect409r1); + defineCurve("sect571k1", SECObjectIdentifiers.sect571k1, sect571k1); + defineCurve("sect571r1", SECObjectIdentifiers.sect571r1, sect571r1); + } + + public static X9ECParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + + if (oid != null) + { + return getByOID(oid); + } + + return null; + } + + /** + * return the X9ECParameters object for the named curve represented by + * the passed in object identifier. Null if the curve isn't present. + * + * @param oid an object identifier representing a named curve, if present. + */ + public static X9ECParameters getByOID( + ASN1ObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid); + + if (holder != null) + { + return holder.getParameters(); + } + + return null; + } + + /** + * return the object identifier signified by the passed in name. Null + * if there is no object identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static ASN1ObjectIdentifier getOID( + String name) + { + return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName( + ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/SECObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/SECObjectIdentifiers.java new file mode 100644 index 000000000..b5c48b942 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/sec/SECObjectIdentifiers.java @@ -0,0 +1,87 @@ +package org.spongycastle.asn1.sec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; + +/** + * Certicom object identifiers + *
+ * ellipticCurve OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) + * } + *+ */ +public interface SECObjectIdentifiers +{ + /** Base OID: 1.3.132.0 */ + static final ASN1ObjectIdentifier ellipticCurve = new ASN1ObjectIdentifier("1.3.132.0"); + + /** sect163k1 OID: 1.3.132.0.1 */ + static final ASN1ObjectIdentifier sect163k1 = ellipticCurve.branch("1"); + /** sect163r1 OID: 1.3.132.0.2 */ + static final ASN1ObjectIdentifier sect163r1 = ellipticCurve.branch("2"); + /** sect239k1 OID: 1.3.132.0.3 */ + static final ASN1ObjectIdentifier sect239k1 = ellipticCurve.branch("3"); + /** sect113r1 OID: 1.3.132.0.4 */ + static final ASN1ObjectIdentifier sect113r1 = ellipticCurve.branch("4"); + /** sect113r2 OID: 1.3.132.0.5 */ + static final ASN1ObjectIdentifier sect113r2 = ellipticCurve.branch("5"); + /** secp112r1 OID: 1.3.132.0.6 */ + static final ASN1ObjectIdentifier secp112r1 = ellipticCurve.branch("6"); + /** secp112r2 OID: 1.3.132.0.7 */ + static final ASN1ObjectIdentifier secp112r2 = ellipticCurve.branch("7"); + /** secp160r1 OID: 1.3.132.0.8 */ + static final ASN1ObjectIdentifier secp160r1 = ellipticCurve.branch("8"); + /** secp160k1 OID: 1.3.132.0.9 */ + static final ASN1ObjectIdentifier secp160k1 = ellipticCurve.branch("9"); + /** secp256k1 OID: 1.3.132.0.10 */ + static final ASN1ObjectIdentifier secp256k1 = ellipticCurve.branch("10"); + /** sect163r2 OID: 1.3.132.0.15 */ + static final ASN1ObjectIdentifier sect163r2 = ellipticCurve.branch("15"); + /** sect283k1 OID: 1.3.132.0.16 */ + static final ASN1ObjectIdentifier sect283k1 = ellipticCurve.branch("16"); + /** sect283r1 OID: 1.3.132.0.17 */ + static final ASN1ObjectIdentifier sect283r1 = ellipticCurve.branch("17"); + /** sect131r1 OID: 1.3.132.0.22 */ + static final ASN1ObjectIdentifier sect131r1 = ellipticCurve.branch("22"); + /** sect131r2 OID: 1.3.132.0.23 */ + static final ASN1ObjectIdentifier sect131r2 = ellipticCurve.branch("23"); + /** sect193r1 OID: 1.3.132.0.24 */ + static final ASN1ObjectIdentifier sect193r1 = ellipticCurve.branch("24"); + /** sect193r2 OID: 1.3.132.0.25 */ + static final ASN1ObjectIdentifier sect193r2 = ellipticCurve.branch("25"); + /** sect233k1 OID: 1.3.132.0.26 */ + static final ASN1ObjectIdentifier sect233k1 = ellipticCurve.branch("26"); + /** sect233r1 OID: 1.3.132.0.27 */ + static final ASN1ObjectIdentifier sect233r1 = ellipticCurve.branch("27"); + /** secp128r1 OID: 1.3.132.0.28 */ + static final ASN1ObjectIdentifier secp128r1 = ellipticCurve.branch("28"); + /** secp128r2 OID: 1.3.132.0.29 */ + static final ASN1ObjectIdentifier secp128r2 = ellipticCurve.branch("29"); + /** secp160r2 OID: 1.3.132.0.30 */ + static final ASN1ObjectIdentifier secp160r2 = ellipticCurve.branch("30"); + /** secp192k1 OID: 1.3.132.0.31 */ + static final ASN1ObjectIdentifier secp192k1 = ellipticCurve.branch("31"); + /** secp224k1 OID: 1.3.132.0.32 */ + static final ASN1ObjectIdentifier secp224k1 = ellipticCurve.branch("32"); + /** secp224r1 OID: 1.3.132.0.33 */ + static final ASN1ObjectIdentifier secp224r1 = ellipticCurve.branch("33"); + /** secp384r1 OID: 1.3.132.0.34 */ + static final ASN1ObjectIdentifier secp384r1 = ellipticCurve.branch("34"); + /** secp521r1 OID: 1.3.132.0.35 */ + static final ASN1ObjectIdentifier secp521r1 = ellipticCurve.branch("35"); + /** sect409k1 OID: 1.3.132.0.36 */ + static final ASN1ObjectIdentifier sect409k1 = ellipticCurve.branch("36"); + /** sect409r1 OID: 1.3.132.0.37 */ + static final ASN1ObjectIdentifier sect409r1 = ellipticCurve.branch("37"); + /** sect571k1 OID: 1.3.132.0.38 */ + static final ASN1ObjectIdentifier sect571k1 = ellipticCurve.branch("38"); + /** sect571r1 OID: 1.3.132.0.39 */ + static final ASN1ObjectIdentifier sect571r1 = ellipticCurve.branch("39"); + + /** secp192r1 OID: 1.3.132.0.prime192v1 */ + static final ASN1ObjectIdentifier secp192r1 = X9ObjectIdentifiers.prime192v1; + /** secp256r1 OID: 1.3.132.0.prime256v1 */ + static final ASN1ObjectIdentifier secp256r1 = X9ObjectIdentifiers.prime256v1; + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMEAttributes.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMEAttributes.java new file mode 100644 index 000000000..cf3f57e3f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMEAttributes.java @@ -0,0 +1,10 @@ +package org.spongycastle.asn1.smime; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +public interface SMIMEAttributes +{ + public static final ASN1ObjectIdentifier smimeCapabilities = PKCSObjectIdentifiers.pkcs_9_at_smimeCapabilities; + public static final ASN1ObjectIdentifier encrypKeyPref = PKCSObjectIdentifiers.id_aa_encrypKeyPref; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilities.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilities.java new file mode 100644 index 000000000..e25f5eb38 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilities.java @@ -0,0 +1,115 @@ +package org.spongycastle.asn1.smime; + +import java.util.Enumeration; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.cms.Attribute; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +/** + * Handler class for dealing with S/MIME Capabilities + */ +public class SMIMECapabilities + extends ASN1Object +{ + /** + * general preferences + */ + public static final ASN1ObjectIdentifier preferSignedData = PKCSObjectIdentifiers.preferSignedData; + public static final ASN1ObjectIdentifier canNotDecryptAny = PKCSObjectIdentifiers.canNotDecryptAny; + public static final ASN1ObjectIdentifier sMIMECapabilitesVersions = PKCSObjectIdentifiers.sMIMECapabilitiesVersions; + + /** + * encryption algorithms preferences + */ + public static final ASN1ObjectIdentifier dES_CBC = new ASN1ObjectIdentifier("1.3.14.3.2.7"); + public static final ASN1ObjectIdentifier dES_EDE3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC; + public static final ASN1ObjectIdentifier rC2_CBC = PKCSObjectIdentifiers.RC2_CBC; + + private ASN1Sequence capabilities; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static SMIMECapabilities getInstance( + Object o) + { + if (o == null || o instanceof SMIMECapabilities) + { + return (SMIMECapabilities)o; + } + + if (o instanceof ASN1Sequence) + { + return new SMIMECapabilities((ASN1Sequence)o); + } + + if (o instanceof Attribute) + { + return new SMIMECapabilities( + (ASN1Sequence)(((Attribute)o).getAttrValues().getObjectAt(0))); + } + + throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName()); + } + + public SMIMECapabilities( + ASN1Sequence seq) + { + capabilities = seq; + } + + /** + * returns a vector with 0 or more objects of all the capabilities + * matching the passed in capability OID. If the OID passed is null the + * entire set is returned. + */ + public Vector getCapabilities( + ASN1ObjectIdentifier capability) + { + Enumeration e = capabilities.getObjects(); + Vector list = new Vector(); + + if (capability == null) + { + while (e.hasMoreElements()) + { + SMIMECapability cap = SMIMECapability.getInstance(e.nextElement()); + + list.addElement(cap); + } + } + else + { + while (e.hasMoreElements()) + { + SMIMECapability cap = SMIMECapability.getInstance(e.nextElement()); + + if (capability.equals(cap.getCapabilityID())) + { + list.addElement(cap); + } + } + } + + return list; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * SMIMECapabilities ::= SEQUENCE OF SMIMECapability + *+ */ + public ASN1Primitive toASN1Primitive() + { + return capabilities; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilitiesAttribute.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilitiesAttribute.java new file mode 100644 index 000000000..53e749aa1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilitiesAttribute.java @@ -0,0 +1,16 @@ +package org.spongycastle.asn1.smime; + +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; +import org.spongycastle.asn1.cms.Attribute; + +public class SMIMECapabilitiesAttribute + extends Attribute +{ + public SMIMECapabilitiesAttribute( + SMIMECapabilityVector capabilities) + { + super(SMIMEAttributes.smimeCapabilities, + new DERSet(new DERSequence(capabilities.toASN1EncodableVector()))); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapability.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapability.java new file mode 100644 index 000000000..c3c4a9996 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapability.java @@ -0,0 +1,103 @@ +package org.spongycastle.asn1.smime; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; + +public class SMIMECapability + extends ASN1Object +{ + /** + * general preferences + */ + public static final ASN1ObjectIdentifier preferSignedData = PKCSObjectIdentifiers.preferSignedData; + public static final ASN1ObjectIdentifier canNotDecryptAny = PKCSObjectIdentifiers.canNotDecryptAny; + public static final ASN1ObjectIdentifier sMIMECapabilitiesVersions = PKCSObjectIdentifiers.sMIMECapabilitiesVersions; + + /** + * encryption algorithms preferences + */ + public static final ASN1ObjectIdentifier dES_CBC = new ASN1ObjectIdentifier("1.3.14.3.2.7"); + public static final ASN1ObjectIdentifier dES_EDE3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC; + public static final ASN1ObjectIdentifier rC2_CBC = PKCSObjectIdentifiers.RC2_CBC; + public static final ASN1ObjectIdentifier aES128_CBC = NISTObjectIdentifiers.id_aes128_CBC; + public static final ASN1ObjectIdentifier aES192_CBC = NISTObjectIdentifiers.id_aes192_CBC; + public static final ASN1ObjectIdentifier aES256_CBC = NISTObjectIdentifiers.id_aes256_CBC; + + private ASN1ObjectIdentifier capabilityID; + private ASN1Encodable parameters; + + public SMIMECapability( + ASN1Sequence seq) + { + capabilityID = (ASN1ObjectIdentifier)seq.getObjectAt(0); + + if (seq.size() > 1) + { + parameters = (ASN1Primitive)seq.getObjectAt(1); + } + } + + public SMIMECapability( + ASN1ObjectIdentifier capabilityID, + ASN1Encodable parameters) + { + this.capabilityID = capabilityID; + this.parameters = parameters; + } + + public static SMIMECapability getInstance( + Object obj) + { + if (obj == null || obj instanceof SMIMECapability) + { + return (SMIMECapability)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new SMIMECapability((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid SMIMECapability"); + } + + public ASN1ObjectIdentifier getCapabilityID() + { + return capabilityID; + } + + public ASN1Encodable getParameters() + { + return parameters; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * SMIMECapability ::= SEQUENCE { + * capabilityID OBJECT IDENTIFIER, + * parameters ANY DEFINED BY capabilityID OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(capabilityID); + + if (parameters != null) + { + v.add(parameters); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilityVector.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilityVector.java new file mode 100644 index 000000000..91834e388 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMECapabilityVector.java @@ -0,0 +1,50 @@ +package org.spongycastle.asn1.smime; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERSequence; + +/** + * Handler for creating a vector S/MIME Capabilities + */ +public class SMIMECapabilityVector +{ + private ASN1EncodableVector capabilities = new ASN1EncodableVector(); + + public void addCapability( + ASN1ObjectIdentifier capability) + { + capabilities.add(new DERSequence(capability)); + } + + public void addCapability( + ASN1ObjectIdentifier capability, + int value) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(capability); + v.add(new ASN1Integer(value)); + + capabilities.add(new DERSequence(v)); + } + + public void addCapability( + ASN1ObjectIdentifier capability, + ASN1Encodable params) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(capability); + v.add(params); + + capabilities.add(new DERSequence(v)); + } + + public ASN1EncodableVector toASN1EncodableVector() + { + return capabilities; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java new file mode 100644 index 000000000..2eacdee98 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java @@ -0,0 +1,48 @@ +package org.spongycastle.asn1.smime; + +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DERSet; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.cms.Attribute; +import org.spongycastle.asn1.cms.IssuerAndSerialNumber; +import org.spongycastle.asn1.cms.RecipientKeyIdentifier; + +/** + * The SMIMEEncryptionKeyPreference object. + *
+ * SMIMEEncryptionKeyPreference ::= CHOICE { + * issuerAndSerialNumber [0] IssuerAndSerialNumber, + * receipentKeyId [1] RecipientKeyIdentifier, + * subjectAltKeyIdentifier [2] SubjectKeyIdentifier + * } + *+ */ +public class SMIMEEncryptionKeyPreferenceAttribute + extends Attribute +{ + public SMIMEEncryptionKeyPreferenceAttribute( + IssuerAndSerialNumber issAndSer) + { + super(SMIMEAttributes.encrypKeyPref, + new DERSet(new DERTaggedObject(false, 0, issAndSer))); + } + + public SMIMEEncryptionKeyPreferenceAttribute( + RecipientKeyIdentifier rKeyId) + { + + super(SMIMEAttributes.encrypKeyPref, + new DERSet(new DERTaggedObject(false, 1, rKeyId))); + } + + /** + * @param sKeyId the subjectKeyIdentifier value (normally the X.509 one) + */ + public SMIMEEncryptionKeyPreferenceAttribute( + ASN1OctetString sKeyId) + { + + super(SMIMEAttributes.encrypKeyPref, + new DERSet(new DERTaggedObject(false, 2, sKeyId))); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/teletrust/TeleTrusTNamedCurves.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/teletrust/TeleTrusTNamedCurves.java new file mode 100644 index 000000000..7ea7c6e53 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/teletrust/TeleTrusTNamedCurves.java @@ -0,0 +1,356 @@ +package org.spongycastle.asn1.teletrust; + +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECParametersHolder; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; + +/** + * elliptic curves defined in "ECC Brainpool Standard Curves and Curve Generation" + * http://www.ecc-brainpool.org/download/draft_pkix_additional_ecc_dp.txt + */ +public class TeleTrusTNamedCurves +{ + private static ECCurve configureCurve(ECCurve curve) + { + return curve; + } + + static X9ECParametersHolder brainpoolP160r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q + new BigInteger("340E7BE2A280EB74E2BE61BADA745D97E8F7C300", 16), // a + new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("04BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC31667CB477A1A8EC338F94741669C976316DA6321")), // G + new BigInteger("E95E4A5F737059DC60DF5991D45029409E60FC09", 16), //n + new BigInteger("01", 16)); // h + } + }; + + static X9ECParametersHolder brainpoolP160t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + // new BigInteger("24DBFF5DEC9B986BBFE5295A29BFBAE45E0F5D0B", 16), // Z + new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q + new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620C", 16), // a' + new BigInteger("7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("04B199B13B9B34EFC1397E64BAEB05ACC265FF2378ADD6718B7C7C1961F0991B842443772152C9E0AD")), // G + new BigInteger("E95E4A5F737059DC60DF5991D45029409E60FC09", 16), //n + new BigInteger("01", 16)); // h + } + }; + + static X9ECParametersHolder brainpoolP192r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q + new BigInteger("6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", 16), // a + new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("04C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD614B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F")), // G + new BigInteger("C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", 16), //n + new BigInteger("01", 16)); // h + } + }; + + static X9ECParametersHolder brainpoolP192t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + //new BigInteger("1B6F5CC8DB4DC7AF19458A9CB80DC2295E5EB9C3732104CB") //Z + new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q + new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294", 16), // a' + new BigInteger("13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("043AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9")), // G' + new BigInteger("C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", 16), //n + new BigInteger("01", 16)); // h + } + }; + + static X9ECParametersHolder brainpoolP224r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q + new BigInteger("68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", 16), // a + new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD")), // G + new BigInteger("D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", 16), //n + new BigInteger("01", 16)); // n + } + }; + static X9ECParametersHolder brainpoolP224t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + //new BigInteger("2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F") //Z + new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q + new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC", 16), // a' + new BigInteger("4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("046AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D5800374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C")), // G' + new BigInteger("D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP256r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q + new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16), // a + new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")), // G + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP256t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + //new BigInteger("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0") //Z + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374", 16), // a' + new BigInteger("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("04A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE")), // G' + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP320r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q + new BigInteger("3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", 16), // a + new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("0443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1")), // G + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP320t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + //new BigInteger("15F75CAF668077F7E85B42EB01F0A81FF56ECD6191D55CB82B7D861458A18FEFC3E5AB7496F3C7B1") //Z + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E24", 16), // a' + new BigInteger("A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CEB5B4FEF422340353", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("04925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF3357F624A21BED5263BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B1B9BC0455FB0D2C3")), // G' + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP384r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q + new BigInteger("7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", 16), // a + new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315")), // G + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP384t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + //new BigInteger("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C") //Z + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC50", 16), // a' + new BigInteger("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("0418DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928")), // G' + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP512r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q + new BigInteger("7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", 16), // a + new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16))); // b + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("0481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892")), // G + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16), //n + new BigInteger("01", 16)); // h + } + }; + static X9ECParametersHolder brainpoolP512t1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve curve = configureCurve(new ECCurve.Fp( + //new BigInteger("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB") //Z + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0", 16), // a' + new BigInteger("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16))); // b' + + return new X9ECParameters( + curve, + curve.decodePoint(Hex.decode("04640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332")), // G' + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16), //n + new BigInteger("01", 16)); // h + } + }; + + static final Hashtable objIds = new Hashtable(); + static final Hashtable curves = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder) + { + objIds.put(name, oid); + names.put(oid, name); + curves.put(oid, holder); + } + + static + { + defineCurve("brainpoolp160r1", TeleTrusTObjectIdentifiers.brainpoolP160r1, brainpoolP160r1); + defineCurve("brainpoolp160t1", TeleTrusTObjectIdentifiers.brainpoolP160t1, brainpoolP160t1); + defineCurve("brainpoolp192r1", TeleTrusTObjectIdentifiers.brainpoolP192r1, brainpoolP192r1); + defineCurve("brainpoolp192t1", TeleTrusTObjectIdentifiers.brainpoolP192t1, brainpoolP192t1); + defineCurve("brainpoolp224r1", TeleTrusTObjectIdentifiers.brainpoolP224r1, brainpoolP224r1); + defineCurve("brainpoolp224t1", TeleTrusTObjectIdentifiers.brainpoolP224t1, brainpoolP224t1); + defineCurve("brainpoolp256r1", TeleTrusTObjectIdentifiers.brainpoolP256r1, brainpoolP256r1); + defineCurve("brainpoolp256t1", TeleTrusTObjectIdentifiers.brainpoolP256t1, brainpoolP256t1); + defineCurve("brainpoolp320r1", TeleTrusTObjectIdentifiers.brainpoolP320r1, brainpoolP320r1); + defineCurve("brainpoolp320t1", TeleTrusTObjectIdentifiers.brainpoolP320t1, brainpoolP320t1); + defineCurve("brainpoolp384r1", TeleTrusTObjectIdentifiers.brainpoolP384r1, brainpoolP384r1); + defineCurve("brainpoolp384t1", TeleTrusTObjectIdentifiers.brainpoolP384t1, brainpoolP384t1); + defineCurve("brainpoolp512r1", TeleTrusTObjectIdentifiers.brainpoolP512r1, brainpoolP512r1); + defineCurve("brainpoolp512t1", TeleTrusTObjectIdentifiers.brainpoolP512t1, brainpoolP512t1); + } + + public static X9ECParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + + if (oid != null) + { + return getByOID(oid); + } + + return null; + } + + /** + * return the X9ECParameters object for the named curve represented by + * the passed in object identifier. Null if the curve isn't present. + * + * @param oid an object identifier representing a named curve, if present. + */ + public static X9ECParameters getByOID( + ASN1ObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid); + + if (holder != null) + { + return holder.getParameters(); + } + + return null; + } + + /** + * return the object identifier signified by the passed in name. Null + * if there is no object identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static ASN1ObjectIdentifier getOID( + String name) + { + return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName( + ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } + + public static ASN1ObjectIdentifier getOID(short curvesize, boolean twisted) + { + return getOID("brainpoolP" + curvesize + (twisted ? "t" : "r") + "1"); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java new file mode 100644 index 000000000..32433a153 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java @@ -0,0 +1,75 @@ +package org.spongycastle.asn1.teletrust; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * TeleTrusT: + * { iso(1) identifier-organization(3) teleTrust(36) algorithm(3) + * + */ +public interface TeleTrusTObjectIdentifiers +{ + /** 1.3.36.3 */ + static final ASN1ObjectIdentifier teleTrusTAlgorithm = new ASN1ObjectIdentifier("1.3.36.3"); + + /** 1.3.36.3.2.1 */ + static final ASN1ObjectIdentifier ripemd160 = teleTrusTAlgorithm.branch("2.1"); + /** 1.3.36.3.2.2 */ + static final ASN1ObjectIdentifier ripemd128 = teleTrusTAlgorithm.branch("2.2"); + /** 1.3.36.3.2.3 */ + static final ASN1ObjectIdentifier ripemd256 = teleTrusTAlgorithm.branch("2.3"); + + /** 1.3.36.3.3.1 */ + static final ASN1ObjectIdentifier teleTrusTRSAsignatureAlgorithm = teleTrusTAlgorithm.branch("3.1"); + + /** 1.3.36.3.3.1.2 */ + static final ASN1ObjectIdentifier rsaSignatureWithripemd160 = teleTrusTRSAsignatureAlgorithm.branch("2"); + /** 1.3.36.3.3.1.3 */ + static final ASN1ObjectIdentifier rsaSignatureWithripemd128 = teleTrusTRSAsignatureAlgorithm.branch("3"); + /** 1.3.36.3.3.1.4 */ + static final ASN1ObjectIdentifier rsaSignatureWithripemd256 = teleTrusTRSAsignatureAlgorithm.branch("4"); + + /** 1.3.36.3.3.2 */ + static final ASN1ObjectIdentifier ecSign = teleTrusTAlgorithm.branch("3.2"); + + /** 1.3.36.3.3.2,1 */ + static final ASN1ObjectIdentifier ecSignWithSha1 = ecSign.branch("1"); + /** 1.3.36.3.3.2.2 */ + static final ASN1ObjectIdentifier ecSignWithRipemd160 = ecSign.branch("2"); + + /** 1.3.36.3.3.2.8 */ + static final ASN1ObjectIdentifier ecc_brainpool = teleTrusTAlgorithm.branch("3.2.8"); + /** 1.3.36.3.3.2.8.1 */ + static final ASN1ObjectIdentifier ellipticCurve = ecc_brainpool.branch("1"); + /** 1.3.36.3.3.2.8.1 */ + static final ASN1ObjectIdentifier versionOne = ellipticCurve.branch("1"); + + /** 1.3.36.3.3.2.8.1.1 */ + static final ASN1ObjectIdentifier brainpoolP160r1 = versionOne.branch("1"); + /** 1.3.36.3.3.2.8.1.2 */ + static final ASN1ObjectIdentifier brainpoolP160t1 = versionOne.branch("2"); + /** 1.3.36.3.3.2.8.1.3 */ + static final ASN1ObjectIdentifier brainpoolP192r1 = versionOne.branch("3"); + /** 1.3.36.3.3.2.8.1.4 */ + static final ASN1ObjectIdentifier brainpoolP192t1 = versionOne.branch("4"); + /** 1.3.36.3.3.2.8.1.5 */ + static final ASN1ObjectIdentifier brainpoolP224r1 = versionOne.branch("5"); + /** 1.3.36.3.3.2.8.1.6 */ + static final ASN1ObjectIdentifier brainpoolP224t1 = versionOne.branch("6"); + /** 1.3.36.3.3.2.8.1.7 */ + static final ASN1ObjectIdentifier brainpoolP256r1 = versionOne.branch("7"); + /** 1.3.36.3.3.2.8.1.8 */ + static final ASN1ObjectIdentifier brainpoolP256t1 = versionOne.branch("8"); + /** 1.3.36.3.3.2.8.1.9 */ + static final ASN1ObjectIdentifier brainpoolP320r1 = versionOne.branch("9"); + /** 1.3.36.3.3.2.8.1.10 */ + static final ASN1ObjectIdentifier brainpoolP320t1 = versionOne.branch("10"); + /** 1.3.36.3.3.2.8.1.11 */ + static final ASN1ObjectIdentifier brainpoolP384r1 = versionOne.branch("11"); + /** 1.3.36.3.3.2.8.1.12 */ + static final ASN1ObjectIdentifier brainpoolP384t1 = versionOne.branch("12"); + /** 1.3.36.3.3.2.8.1.13 */ + static final ASN1ObjectIdentifier brainpoolP512r1 = versionOne.branch("13"); + /** 1.3.36.3.3.2.8.1.14 */ + static final ASN1ObjectIdentifier brainpoolP512t1 = versionOne.branch("14"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/Accuracy.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/Accuracy.java new file mode 100644 index 000000000..910d22c1c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/Accuracy.java @@ -0,0 +1,173 @@ +package org.spongycastle.asn1.tsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + + +public class Accuracy + extends ASN1Object +{ + ASN1Integer seconds; + + ASN1Integer millis; + + ASN1Integer micros; + + // constantes + protected static final int MIN_MILLIS = 1; + + protected static final int MAX_MILLIS = 999; + + protected static final int MIN_MICROS = 1; + + protected static final int MAX_MICROS = 999; + + protected Accuracy() + { + } + + public Accuracy( + ASN1Integer seconds, + ASN1Integer millis, + ASN1Integer micros) + { + this.seconds = seconds; + + //Verifications + if (millis != null + && (millis.getValue().intValue() < MIN_MILLIS || millis + .getValue().intValue() > MAX_MILLIS)) + { + throw new IllegalArgumentException( + "Invalid millis field : not in (1..999)"); + } + else + { + this.millis = millis; + } + + if (micros != null + && (micros.getValue().intValue() < MIN_MICROS || micros + .getValue().intValue() > MAX_MICROS)) + { + throw new IllegalArgumentException( + "Invalid micros field : not in (1..999)"); + } + else + { + this.micros = micros; + } + + } + + private Accuracy(ASN1Sequence seq) + { + seconds = null; + millis = null; + micros = null; + + for (int i = 0; i < seq.size(); i++) + { + // seconds + if (seq.getObjectAt(i) instanceof ASN1Integer) + { + seconds = (ASN1Integer) seq.getObjectAt(i); + } + else if (seq.getObjectAt(i) instanceof DERTaggedObject) + { + DERTaggedObject extra = (DERTaggedObject) seq.getObjectAt(i); + + switch (extra.getTagNo()) + { + case 0: + millis = ASN1Integer.getInstance(extra, false); + if (millis.getValue().intValue() < MIN_MILLIS + || millis.getValue().intValue() > MAX_MILLIS) + { + throw new IllegalArgumentException( + "Invalid millis field : not in (1..999)."); + } + break; + case 1: + micros = ASN1Integer.getInstance(extra, false); + if (micros.getValue().intValue() < MIN_MICROS + || micros.getValue().intValue() > MAX_MICROS) + { + throw new IllegalArgumentException( + "Invalid micros field : not in (1..999)."); + } + break; + default: + throw new IllegalArgumentException("Invalig tag number"); + } + } + } + } + + public static Accuracy getInstance(Object o) + { + if (o instanceof Accuracy) + { + return (Accuracy) o; + } + + if (o != null) + { + return new Accuracy(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ASN1Integer getSeconds() + { + return seconds; + } + + public ASN1Integer getMillis() + { + return millis; + } + + public ASN1Integer getMicros() + { + return micros; + } + + /** + *
+ * Accuracy ::= SEQUENCE { + * seconds INTEGER OPTIONAL, + * millis [0] INTEGER (1..999) OPTIONAL, + * micros [1] INTEGER (1..999) OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (seconds != null) + { + v.add(seconds); + } + + if (millis != null) + { + v.add(new DERTaggedObject(false, 0, millis)); + } + + if (micros != null) + { + v.add(new DERTaggedObject(false, 1, micros)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/MessageImprint.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/MessageImprint.java new file mode 100644 index 000000000..05830447f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/MessageImprint.java @@ -0,0 +1,78 @@ +package org.spongycastle.asn1.tsp; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class MessageImprint + extends ASN1Object +{ + AlgorithmIdentifier hashAlgorithm; + byte[] hashedMessage; + + /** + * @param o + * @return a MessageImprint object. + */ + public static MessageImprint getInstance(Object o) + { + if (o instanceof MessageImprint) + { + return (MessageImprint)o; + } + + if (o != null) + { + return new MessageImprint(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private MessageImprint( + ASN1Sequence seq) + { + this.hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + this.hashedMessage = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets(); + } + + public MessageImprint( + AlgorithmIdentifier hashAlgorithm, + byte[] hashedMessage) + { + this.hashAlgorithm = hashAlgorithm; + this.hashedMessage = hashedMessage; + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public byte[] getHashedMessage() + { + return hashedMessage; + } + + /** + *
+ * MessageImprint ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * hashedMessage OCTET STRING } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(hashAlgorithm); + v.add(new DEROctetString(hashedMessage)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TSTInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TSTInfo.java new file mode 100644 index 000000000..71587414c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TSTInfo.java @@ -0,0 +1,233 @@ +package org.spongycastle.asn1.tsp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralName; + +public class TSTInfo + extends ASN1Object +{ + private ASN1Integer version; + private ASN1ObjectIdentifier tsaPolicyId; + private MessageImprint messageImprint; + private ASN1Integer serialNumber; + private ASN1GeneralizedTime genTime; + private Accuracy accuracy; + private ASN1Boolean ordering; + private ASN1Integer nonce; + private GeneralName tsa; + private Extensions extensions; + + public static TSTInfo getInstance(Object o) + { + if (o instanceof TSTInfo) + { + return (TSTInfo)o; + } + else if (o != null) + { + return new TSTInfo(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private TSTInfo(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + // version + version = ASN1Integer.getInstance(e.nextElement()); + + // tsaPolicy + tsaPolicyId = ASN1ObjectIdentifier.getInstance(e.nextElement()); + + // messageImprint + messageImprint = MessageImprint.getInstance(e.nextElement()); + + // serialNumber + serialNumber = ASN1Integer.getInstance(e.nextElement()); + + // genTime + genTime = ASN1GeneralizedTime.getInstance(e.nextElement()); + + // default for ordering + ordering = ASN1Boolean.getInstance(false); + + while (e.hasMoreElements()) + { + ASN1Object o = (ASN1Object) e.nextElement(); + + if (o instanceof ASN1TaggedObject) + { + DERTaggedObject tagged = (DERTaggedObject) o; + + switch (tagged.getTagNo()) + { + case 0: + tsa = GeneralName.getInstance(tagged, true); + break; + case 1: + extensions = Extensions.getInstance(tagged, false); + break; + default: + throw new IllegalArgumentException("Unknown tag value " + tagged.getTagNo()); + } + } + else if (o instanceof ASN1Sequence || o instanceof Accuracy) + { + accuracy = Accuracy.getInstance(o); + } + else if (o instanceof ASN1Boolean) + { + ordering = ASN1Boolean.getInstance(o); + } + else if (o instanceof ASN1Integer) + { + nonce = ASN1Integer.getInstance(o); + } + + } + } + + public TSTInfo(ASN1ObjectIdentifier tsaPolicyId, MessageImprint messageImprint, + ASN1Integer serialNumber, ASN1GeneralizedTime genTime, + Accuracy accuracy, ASN1Boolean ordering, ASN1Integer nonce, + GeneralName tsa, Extensions extensions) + { + version = new ASN1Integer(1); + this.tsaPolicyId = tsaPolicyId; + this.messageImprint = messageImprint; + this.serialNumber = serialNumber; + this.genTime = genTime; + + this.accuracy = accuracy; + this.ordering = ordering; + this.nonce = nonce; + this.tsa = tsa; + this.extensions = extensions; + } + + public ASN1Integer getVersion() + { + return version; + } + + public MessageImprint getMessageImprint() + { + return messageImprint; + } + + public ASN1ObjectIdentifier getPolicy() + { + return tsaPolicyId; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + public Accuracy getAccuracy() + { + return accuracy; + } + + public ASN1GeneralizedTime getGenTime() + { + return genTime; + } + + public ASN1Boolean getOrdering() + { + return ordering; + } + + public ASN1Integer getNonce() + { + return nonce; + } + + public GeneralName getTsa() + { + return tsa; + } + + public Extensions getExtensions() + { + return extensions; + } + + /** + *
+ * + * TSTInfo ::= SEQUENCE { + * version INTEGER { v1(1) }, + * policy TSAPolicyId, + * messageImprint MessageImprint, + * -- MUST have the same value as the similar field in + * -- TimeStampReq + * serialNumber INTEGER, + * -- Time-Stamping users MUST be ready to accommodate integers + * -- up to 160 bits. + * genTime GeneralizedTime, + * accuracy Accuracy OPTIONAL, + * ordering BOOLEAN DEFAULT FALSE, + * nonce INTEGER OPTIONAL, + * -- MUST be present if the similar field was present + * -- in TimeStampReq. In that case it MUST have the same value. + * tsa [0] GeneralName OPTIONAL, + * extensions [1] IMPLICIT Extensions OPTIONAL } + * + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + seq.add(version); + + seq.add(tsaPolicyId); + seq.add(messageImprint); + seq.add(serialNumber); + seq.add(genTime); + + if (accuracy != null) + { + seq.add(accuracy); + } + + if (ordering != null && ordering.isTrue()) + { + seq.add(ordering); + } + + if (nonce != null) + { + seq.add(nonce); + } + + if (tsa != null) + { + seq.add(new DERTaggedObject(true, 0, tsa)); + } + + if (extensions != null) + { + seq.add(new DERTaggedObject(false, 1, extensions)); + } + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TimeStampReq.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TimeStampReq.java new file mode 100644 index 000000000..f88ed2b7d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TimeStampReq.java @@ -0,0 +1,179 @@ +package org.spongycastle.asn1.tsp; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.Extensions; + +public class TimeStampReq + extends ASN1Object +{ + ASN1Integer version; + + MessageImprint messageImprint; + + ASN1ObjectIdentifier tsaPolicy; + + ASN1Integer nonce; + + ASN1Boolean certReq; + + Extensions extensions; + + public static TimeStampReq getInstance(Object o) + { + if (o instanceof TimeStampReq) + { + return (TimeStampReq) o; + } + else if (o != null) + { + return new TimeStampReq(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private TimeStampReq(ASN1Sequence seq) + { + int nbObjects = seq.size(); + + int seqStart = 0; + + // version + version = ASN1Integer.getInstance(seq.getObjectAt(seqStart)); + + seqStart++; + + // messageImprint + messageImprint = MessageImprint.getInstance(seq.getObjectAt(seqStart)); + + seqStart++; + + for (int opt = seqStart; opt < nbObjects; opt++) + { + // tsaPolicy + if (seq.getObjectAt(opt) instanceof ASN1ObjectIdentifier) + { + tsaPolicy = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(opt)); + } + // nonce + else if (seq.getObjectAt(opt) instanceof ASN1Integer) + { + nonce = ASN1Integer.getInstance(seq.getObjectAt(opt)); + } + // certReq + else if (seq.getObjectAt(opt) instanceof ASN1Boolean) + { + certReq = ASN1Boolean.getInstance(seq.getObjectAt(opt)); + } + // extensions + else if (seq.getObjectAt(opt) instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagged = (ASN1TaggedObject)seq.getObjectAt(opt); + if (tagged.getTagNo() == 0) + { + extensions = Extensions.getInstance(tagged, false); + } + } + } + } + + public TimeStampReq( + MessageImprint messageImprint, + ASN1ObjectIdentifier tsaPolicy, + ASN1Integer nonce, + ASN1Boolean certReq, + Extensions extensions) + { + // default + version = new ASN1Integer(1); + + this.messageImprint = messageImprint; + this.tsaPolicy = tsaPolicy; + this.nonce = nonce; + this.certReq = certReq; + this.extensions = extensions; + } + + public ASN1Integer getVersion() + { + return version; + } + + public MessageImprint getMessageImprint() + { + return messageImprint; + } + + public ASN1ObjectIdentifier getReqPolicy() + { + return tsaPolicy; + } + + public ASN1Integer getNonce() + { + return nonce; + } + + public ASN1Boolean getCertReq() + { + return certReq; + } + + public Extensions getExtensions() + { + return extensions; + } + + /** + *
+ * TimeStampReq ::= SEQUENCE { + * version INTEGER { v1(1) }, + * messageImprint MessageImprint, + * --a hash algorithm OID and the hash value of the data to be + * --time-stamped + * reqPolicy TSAPolicyId OPTIONAL, + * nonce INTEGER OPTIONAL, + * certReq BOOLEAN DEFAULT FALSE, + * extensions [0] IMPLICIT Extensions OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(messageImprint); + + if (tsaPolicy != null) + { + v.add(tsaPolicy); + } + + if (nonce != null) + { + v.add(nonce); + } + + if (certReq != null && certReq.isTrue()) + { + v.add(certReq); + } + + if (extensions != null) + { + v.add(new DERTaggedObject(false, 0, extensions)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TimeStampResp.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TimeStampResp.java new file mode 100644 index 000000000..eb73e0685 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/tsp/TimeStampResp.java @@ -0,0 +1,84 @@ +package org.spongycastle.asn1.tsp; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.cmp.PKIStatusInfo; +import org.spongycastle.asn1.cms.ContentInfo; + + +public class TimeStampResp + extends ASN1Object +{ + PKIStatusInfo pkiStatusInfo; + + ContentInfo timeStampToken; + + public static TimeStampResp getInstance(Object o) + { + if (o instanceof TimeStampResp) + { + return (TimeStampResp) o; + } + else if (o != null) + { + return new TimeStampResp(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private TimeStampResp(ASN1Sequence seq) + { + + Enumeration e = seq.getObjects(); + + // status + pkiStatusInfo = PKIStatusInfo.getInstance(e.nextElement()); + + if (e.hasMoreElements()) + { + timeStampToken = ContentInfo.getInstance(e.nextElement()); + } + } + + public TimeStampResp(PKIStatusInfo pkiStatusInfo, ContentInfo timeStampToken) + { + this.pkiStatusInfo = pkiStatusInfo; + this.timeStampToken = timeStampToken; + } + + public PKIStatusInfo getStatus() + { + return pkiStatusInfo; + } + + public ContentInfo getTimeStampToken() + { + return timeStampToken; + } + + /** + *
+ * TimeStampResp ::= SEQUENCE { + * status PKIStatusInfo, + * timeStampToken TimeStampToken OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(pkiStatusInfo); + if (timeStampToken != null) + { + v.add(timeStampToken); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145BinaryField.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145BinaryField.java new file mode 100644 index 000000000..4cd538700 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145BinaryField.java @@ -0,0 +1,119 @@ +package org.spongycastle.asn1.ua; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class DSTU4145BinaryField + extends ASN1Object +{ + + private int m, k, j, l; + + private DSTU4145BinaryField(ASN1Sequence seq) + { + m = ASN1Integer.getInstance(seq.getObjectAt(0)).getPositiveValue().intValue(); + + if (seq.getObjectAt(1) instanceof ASN1Integer) + { + k = ((ASN1Integer)seq.getObjectAt(1)).getPositiveValue().intValue(); + } + else if (seq.getObjectAt(1) instanceof ASN1Sequence) + { + ASN1Sequence coefs = ASN1Sequence.getInstance(seq.getObjectAt(1)); + + k = ASN1Integer.getInstance(coefs.getObjectAt(0)).getPositiveValue().intValue(); + j = ASN1Integer.getInstance(coefs.getObjectAt(1)).getPositiveValue().intValue(); + l = ASN1Integer.getInstance(coefs.getObjectAt(2)).getPositiveValue().intValue(); + } + else + { + throw new IllegalArgumentException("object parse error"); + } + } + + public static DSTU4145BinaryField getInstance(Object obj) + { + if (obj instanceof DSTU4145BinaryField) + { + return (DSTU4145BinaryField)obj; + } + + if (obj != null) + { + return new DSTU4145BinaryField(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public DSTU4145BinaryField(int m, int k1, int k2, int k3) + { + this.m = m; + this.k = k1; + this.j = k2; + this.l = k3; + } + + public int getM() + { + return m; + } + + public int getK1() + { + return k; + } + + public int getK2() + { + return j; + } + + public int getK3() + { + return l; + } + + public DSTU4145BinaryField(int m, int k) + { + this(m, k, 0, 0); + } + + /** + * BinaryField ::= SEQUENCE { + * M INTEGER, + * CHOICE {Trinomial, Pentanomial} + * Trinomial::= INTEGER + * Pentanomial::= SEQUENCE { + * k INTEGER, + * j INTEGER, + * l INTEGER} + */ + public ASN1Primitive toASN1Primitive() + { + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(m)); + if (j == 0) //Trinomial + { + v.add(new ASN1Integer(k)); + } + else + { + ASN1EncodableVector coefs = new ASN1EncodableVector(); + coefs.add(new ASN1Integer(k)); + coefs.add(new ASN1Integer(j)); + coefs.add(new ASN1Integer(l)); + + v.add(new DERSequence(coefs)); + } + + return new DERSequence(v); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145ECBinary.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145ECBinary.java new file mode 100644 index 000000000..945984745 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145ECBinary.java @@ -0,0 +1,144 @@ +package org.spongycastle.asn1.ua; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.util.Arrays; + +public class DSTU4145ECBinary + extends ASN1Object +{ + + BigInteger version = BigInteger.valueOf(0); + + DSTU4145BinaryField f; + ASN1Integer a; + ASN1OctetString b; + ASN1Integer n; + ASN1OctetString bp; + + public DSTU4145ECBinary(ECDomainParameters params) + { + if (!(params.getCurve() instanceof ECCurve.F2m)) + { + throw new IllegalArgumentException("only binary domain is possible"); + } + + // We always use big-endian in parameter encoding + ECCurve.F2m curve = (ECCurve.F2m)params.getCurve(); + f = new DSTU4145BinaryField(curve.getM(), curve.getK1(), curve.getK2(), curve.getK3()); + a = new ASN1Integer(curve.getA().toBigInteger()); + X9IntegerConverter converter = new X9IntegerConverter(); + b = new DEROctetString(converter.integerToBytes(curve.getB().toBigInteger(), converter.getByteLength(curve))); + n = new ASN1Integer(params.getN()); + bp = new DEROctetString(DSTU4145PointEncoder.encodePoint(params.getG())); + } + + private DSTU4145ECBinary(ASN1Sequence seq) + { + int index = 0; + + if (seq.getObjectAt(index) instanceof ASN1TaggedObject) + { + ASN1TaggedObject taggedVersion = (ASN1TaggedObject)seq.getObjectAt(index); + if (taggedVersion.isExplicit() && 0 == taggedVersion.getTagNo()) + { + version = ASN1Integer.getInstance(taggedVersion.getLoadedObject()).getValue(); + index++; + } + else + { + throw new IllegalArgumentException("object parse error"); + } + } + f = DSTU4145BinaryField.getInstance(seq.getObjectAt(index)); + index++; + a = ASN1Integer.getInstance(seq.getObjectAt(index)); + index++; + b = ASN1OctetString.getInstance(seq.getObjectAt(index)); + index++; + n = ASN1Integer.getInstance(seq.getObjectAt(index)); + index++; + bp = ASN1OctetString.getInstance(seq.getObjectAt(index)); + } + + public static DSTU4145ECBinary getInstance(Object obj) + { + if (obj instanceof DSTU4145ECBinary) + { + return (DSTU4145ECBinary)obj; + } + + if (obj != null) + { + return new DSTU4145ECBinary(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public DSTU4145BinaryField getField() + { + return f; + } + + public BigInteger getA() + { + return a.getValue(); + } + + public byte[] getB() + { + return Arrays.clone(b.getOctets()); + } + + public BigInteger getN() + { + return n.getValue(); + } + + public byte[] getG() + { + return Arrays.clone(bp.getOctets()); + } + + /** + * ECBinary ::= SEQUENCE { + * version [0] EXPLICIT INTEGER DEFAULT 0, + * f BinaryField, + * a INTEGER (0..1), + * b OCTET STRING, + * n INTEGER, + * bp OCTET STRING} + */ + public ASN1Primitive toASN1Primitive() + { + + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (0 != version.compareTo(BigInteger.valueOf(0))) + { + v.add(new DERTaggedObject(true, 0, new ASN1Integer(version))); + } + v.add(f); + v.add(a); + v.add(b); + v.add(n); + v.add(bp); + + return new DERSequence(v); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145NamedCurves.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145NamedCurves.java new file mode 100644 index 000000000..dcde92d4a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145NamedCurves.java @@ -0,0 +1,94 @@ +package org.spongycastle.asn1.ua; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class DSTU4145NamedCurves +{ + private static final BigInteger ZERO = BigInteger.valueOf(0); + private static final BigInteger ONE = BigInteger.valueOf(1); + + public static final ECDomainParameters[] params = new ECDomainParameters[10]; + static final ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[10]; + + //All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X + //where X is the curve number 0-9 + static final String oidBase = UAObjectIdentifiers.dstu4145le.getId() + ".2."; + + static + { + ECCurve.F2m[] curves = new ECCurve.F2m[10]; + curves[0] = new ECCurve.F2m(163, 3, 6, 7, ONE, new BigInteger("5FF6108462A2DC8210AB403925E638A19C1455D21", 16)); + curves[1] = new ECCurve.F2m(167, 6, ONE, new BigInteger("6EE3CEEB230811759F20518A0930F1A4315A827DAC", 16)); + curves[2] = new ECCurve.F2m(173, 1, 2, 10, ZERO, new BigInteger("108576C80499DB2FC16EDDF6853BBB278F6B6FB437D9", 16)); + curves[3] = new ECCurve.F2m(179, 1, 2, 4, ONE, new BigInteger("4A6E0856526436F2F88DD07A341E32D04184572BEB710", 16)); + curves[4] = new ECCurve.F2m(191, 9, ONE, new BigInteger("7BC86E2102902EC4D5890E8B6B4981ff27E0482750FEFC03", 16)); + curves[5] = new ECCurve.F2m(233, 1, 4, 9, ONE, new BigInteger("06973B15095675534C7CF7E64A21BD54EF5DD3B8A0326AA936ECE454D2C", 16)); + curves[6] = new ECCurve.F2m(257, 12, ZERO, new BigInteger("1CEF494720115657E18F938D7A7942394FF9425C1458C57861F9EEA6ADBE3BE10", 16)); + curves[7] = new ECCurve.F2m(307, 2, 4, 8, ONE, new BigInteger("393C7F7D53666B5054B5E6C6D3DE94F4296C0C599E2E2E241050DF18B6090BDC90186904968BB", 16)); + curves[8] = new ECCurve.F2m(367, 21, ONE, new BigInteger("43FC8AD242B0B7A6F3D1627AD5654447556B47BF6AA4A64B0C2AFE42CADAB8F93D92394C79A79755437B56995136", 16)); + curves[9] = new ECCurve.F2m(431, 1, 3, 5, ONE, new BigInteger("03CE10490F6A708FC26DFE8C3D27C4F94E690134D5BFF988D8D28AAEAEDE975936C66BAC536B18AE2DC312CA493117DAA469C640CAF3", 16)); + + ECPoint[] points = new ECPoint[10]; + points[0] = curves[0].createPoint(new BigInteger("2E2F85F5DD74CE983A5C4237229DAF8A3F35823BE", 16), new BigInteger("3826F008A8C51D7B95284D9D03FF0E00CE2CD723A", 16)); + points[1] = curves[1].createPoint(new BigInteger("7A1F6653786A68192803910A3D30B2A2018B21CD54", 16), new BigInteger("5F49EB26781C0EC6B8909156D98ED435E45FD59918", 16)); + points[2] = curves[2].createPoint(new BigInteger("4D41A619BCC6EADF0448FA22FAD567A9181D37389CA", 16), new BigInteger("10B51CC12849B234C75E6DD2028BF7FF5C1CE0D991A1", 16)); + points[3] = curves[3].createPoint(new BigInteger("6BA06FE51464B2BD26DC57F48819BA9954667022C7D03", 16), new BigInteger("25FBC363582DCEC065080CA8287AAFF09788A66DC3A9E", 16)); + points[4] = curves[4].createPoint(new BigInteger("714114B762F2FF4A7912A6D2AC58B9B5C2FCFE76DAEB7129", 16), new BigInteger("29C41E568B77C617EFE5902F11DB96FA9613CD8D03DB08DA", 16)); + points[5] = curves[5].createPoint(new BigInteger("3FCDA526B6CDF83BA1118DF35B3C31761D3545F32728D003EEB25EFE96", 16), new BigInteger("9CA8B57A934C54DEEDA9E54A7BBAD95E3B2E91C54D32BE0B9DF96D8D35", 16)); + points[6] = curves[6].createPoint(new BigInteger("02A29EF207D0E9B6C55CD260B306C7E007AC491CA1B10C62334A9E8DCD8D20FB7", 16), new BigInteger("10686D41FF744D4449FCCF6D8EEA03102E6812C93A9D60B978B702CF156D814EF", 16)); + points[7] = curves[7].createPoint(new BigInteger("216EE8B189D291A0224984C1E92F1D16BF75CCD825A087A239B276D3167743C52C02D6E7232AA", 16), new BigInteger("5D9306BACD22B7FAEB09D2E049C6E2866C5D1677762A8F2F2DC9A11C7F7BE8340AB2237C7F2A0", 16)); + points[8] = curves[8].createPoint(new BigInteger("324A6EDDD512F08C49A99AE0D3F961197A76413E7BE81A400CA681E09639B5FE12E59A109F78BF4A373541B3B9A1", 16), new BigInteger("1AB597A5B4477F59E39539007C7F977D1A567B92B043A49C6B61984C3FE3481AAF454CD41BA1F051626442B3C10", 16)); + points[9] = curves[9].createPoint(new BigInteger("1A62BA79D98133A16BBAE7ED9A8E03C32E0824D57AEF72F88986874E5AAE49C27BED49A2A95058068426C2171E99FD3B43C5947C857D", 16), new BigInteger("70B5E1E14031C1F70BBEFE96BDDE66F451754B4CA5F48DA241F331AA396B8D1839A855C1769B1EA14BA53308B5E2723724E090E02DB9", 16)); + + BigInteger[] n_s = new BigInteger[10]; + n_s[0] = new BigInteger("400000000000000000002BEC12BE2262D39BCF14D", 16); + n_s[1] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFB12EBCC7D7F29FF7701F", 16); + n_s[2] = new BigInteger("800000000000000000000189B4E67606E3825BB2831", 16); + n_s[3] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFB981960435FE5AB64236EF", 16); + n_s[4] = new BigInteger("40000000000000000000000069A779CAC1DABC6788F7474F", 16); + n_s[5] = new BigInteger("1000000000000000000000000000013E974E72F8A6922031D2603CFE0D7", 16); + n_s[6] = new BigInteger("800000000000000000000000000000006759213AF182E987D3E17714907D470D", 16); + n_s[7] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC079C2F3825DA70D390FBBA588D4604022B7B7", 16); + n_s[8] = new BigInteger("40000000000000000000000000000000000000000000009C300B75A3FA824F22428FD28CE8812245EF44049B2D49", 16); + n_s[9] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA3175458009A8C0A724F02F81AA8A1FCBAF80D90C7A95110504CF", 16); + + for (int i = 0; i < params.length; i++) + { + params[i] = new ECDomainParameters(curves[i], points[i], n_s[i]); + } + + for (int i = 0; i < oids.length; i++) + { + oids[i] = new ASN1ObjectIdentifier(oidBase + i); + } + } + + /** + * All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X + * where X is the curve number 0-9 + */ + public static ASN1ObjectIdentifier[] getOIDs() + { + return oids; + } + + /** + * All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X + * where X is the curve number 0-9 + */ + public static ECDomainParameters getByOID(ASN1ObjectIdentifier oid) + { + String oidStr = oid.getId(); + if (oidStr.startsWith(oidBase)) + { + int index = Integer.parseInt(oidStr.substring(oidStr.length() - 1)); + return params[index]; + } + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145Params.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145Params.java new file mode 100644 index 000000000..d6c60f8be --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145Params.java @@ -0,0 +1,121 @@ +package org.spongycastle.asn1.ua; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +public class DSTU4145Params + extends ASN1Object +{ + private static final byte DEFAULT_DKE[] = { + (byte)0xa9, (byte)0xd6, (byte)0xeb, 0x45, (byte)0xf1, 0x3c, 0x70, (byte)0x82, + (byte)0x80, (byte)0xc4, (byte)0x96, 0x7b, 0x23, 0x1f, 0x5e, (byte)0xad, + (byte)0xf6, 0x58, (byte)0xeb, (byte)0xa4, (byte)0xc0, 0x37, 0x29, 0x1d, + 0x38, (byte)0xd9, 0x6b, (byte)0xf0, 0x25, (byte)0xca, 0x4e, 0x17, + (byte)0xf8, (byte)0xe9, 0x72, 0x0d, (byte)0xc6, 0x15, (byte)0xb4, 0x3a, + 0x28, (byte)0x97, 0x5f, 0x0b, (byte)0xc1, (byte)0xde, (byte)0xa3, 0x64, + 0x38, (byte)0xb5, 0x64, (byte)0xea, 0x2c, 0x17, (byte)0x9f, (byte)0xd0, + 0x12, 0x3e, 0x6d, (byte)0xb8, (byte)0xfa, (byte)0xc5, 0x79, 0x04}; + + + private ASN1ObjectIdentifier namedCurve; + private DSTU4145ECBinary ecbinary; + private byte[] dke = DEFAULT_DKE; + + public DSTU4145Params(ASN1ObjectIdentifier namedCurve) + { + this.namedCurve = namedCurve; + } + + public DSTU4145Params(DSTU4145ECBinary ecbinary) + { + this.ecbinary = ecbinary; + } + + public boolean isNamedCurve() + { + return namedCurve != null; + } + + public DSTU4145ECBinary getECBinary() + { + return ecbinary; + } + + public byte[] getDKE() + { + return dke; + } + + public static byte[] getDefaultDKE() + { + return DEFAULT_DKE; + } + + public ASN1ObjectIdentifier getNamedCurve() + { + return namedCurve; + } + + public static DSTU4145Params getInstance(Object obj) + { + if (obj instanceof DSTU4145Params) + { + return (DSTU4145Params)obj; + } + + if (obj != null) + { + ASN1Sequence seq = ASN1Sequence.getInstance(obj); + DSTU4145Params params; + + if (seq.getObjectAt(0) instanceof ASN1ObjectIdentifier) + { + params = new DSTU4145Params(ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0))); + } + else + { + params = new DSTU4145Params(DSTU4145ECBinary.getInstance(seq.getObjectAt(0))); + } + + if (seq.size() == 2) + { + params.dke = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets(); + if (params.dke.length != DSTU4145Params.DEFAULT_DKE.length) + { + throw new IllegalArgumentException("object parse error"); + } + } + + return params; + } + + throw new IllegalArgumentException("object parse error"); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (namedCurve != null) + { + v.add(namedCurve); + } + else + { + v.add(ecbinary); + } + + if (!org.spongycastle.util.Arrays.areEqual(dke, DEFAULT_DKE)) + { + v.add(new DEROctetString(dke)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145PointEncoder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145PointEncoder.java new file mode 100644 index 000000000..06e2ef86d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145PointEncoder.java @@ -0,0 +1,156 @@ +package org.spongycastle.asn1.ua; + +import java.math.BigInteger; +import java.util.Random; + +import org.spongycastle.math.ec.ECConstants; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECFieldElement; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.util.Arrays; + +/** + * DSTU4145 encodes points somewhat differently than X9.62 + * It compresses the point to the size of the field element + */ +public abstract class DSTU4145PointEncoder +{ + private static BigInteger trace(ECFieldElement fe) + { + ECFieldElement t = fe; + for (int i = 0; i < fe.getFieldSize() - 1; i++) + { + t = t.square().add(fe); + } + return t.toBigInteger(); + } + + /** + * Solves a quadratic equation
z2 + z = beta
(X9.62
+ * D.1.6) The other solution is z + 1
.
+ *
+ * @param beta The value to solve the qradratic equation for.
+ * @return the solution for z2 + z = beta
or
+ * null
if no solution exists.
+ */
+ private static ECFieldElement solveQuadraticEquation(ECCurve curve, ECFieldElement beta)
+ {
+ if (beta.isZero())
+ {
+ return beta;
+ }
+
+ ECFieldElement zeroElement = curve.fromBigInteger(ECConstants.ZERO);
+
+ ECFieldElement z = null;
+ ECFieldElement gamma = null;
+
+ Random rand = new Random();
+ int m = beta.getFieldSize();
+ do
+ {
+ ECFieldElement t = curve.fromBigInteger(new BigInteger(m, rand));
+ z = zeroElement;
+ ECFieldElement w = beta;
+ for (int i = 1; i <= m - 1; i++)
+ {
+ ECFieldElement w2 = w.square();
+ z = z.square().add(w2.multiply(t));
+ w = w2.add(beta);
+ }
+ if (!w.isZero())
+ {
+ return null;
+ }
+ gamma = z.square().add(z);
+ }
+ while (gamma.isZero());
+
+ return z;
+ }
+
+ public static byte[] encodePoint(ECPoint Q)
+ {
+ /*if (!Q.isCompressed())
+ Q=new ECPoint.F2m(Q.getCurve(),Q.getX(),Q.getY(),true);
+
+ byte[] bytes=Q.getEncoded();
+
+ if (bytes[0]==0x02)
+ bytes[bytes.length-1]&=0xFE;
+ else if (bytes[0]==0x02)
+ bytes[bytes.length-1]|=0x01;
+
+ return Arrays.copyOfRange(bytes, 1, bytes.length);*/
+
+ Q = Q.normalize();
+
+ ECFieldElement x = Q.getAffineXCoord();
+
+ byte[] bytes = x.getEncoded();
+
+ if (!x.isZero())
+ {
+ ECFieldElement y = Q.getAffineYCoord().divide(x);
+ if (trace(y).equals(ECConstants.ONE))
+ {
+ bytes[bytes.length - 1] |= 0x01;
+ }
+ else
+ {
+ bytes[bytes.length - 1] &= 0xFE;
+ }
+ }
+
+ return bytes;
+ }
+
+ public static ECPoint decodePoint(ECCurve curve, byte[] bytes)
+ {
+ /*byte[] bp_enc=new byte[bytes.length+1];
+ if (0==(bytes[bytes.length-1]&0x1))
+ bp_enc[0]=0x02;
+ else
+ bp_enc[0]=0x03;
+ System.arraycopy(bytes, 0, bp_enc, 1, bytes.length);
+ if (!trace(curve.fromBigInteger(new BigInteger(1, bytes))).equals(curve.getA().toBigInteger()))
+ bp_enc[bp_enc.length-1]^=0x01;
+
+ return curve.decodePoint(bp_enc);*/
+
+ BigInteger k = BigInteger.valueOf(bytes[bytes.length - 1] & 0x1);
+ if (!trace(curve.fromBigInteger(new BigInteger(1, bytes))).equals(curve.getA().toBigInteger()))
+ {
+ bytes = Arrays.clone(bytes);
+ bytes[bytes.length - 1] ^= 0x01;
+ }
+ ECFieldElement xp = curve.fromBigInteger(new BigInteger(1, bytes));
+ ECFieldElement yp = null;
+ if (xp.isZero())
+ {
+ yp = (ECFieldElement.F2m)curve.getB();
+ for (int i = 0; i < curve.getFieldSize() - 1; i++)
+ {
+ yp = yp.square();
+ }
+ }
+ else
+ {
+ ECFieldElement beta = xp.add(curve.getA()).add(
+ curve.getB().multiply(xp.square().invert()));
+ ECFieldElement z = solveQuadraticEquation(curve, beta);
+ if (z == null)
+ {
+ throw new RuntimeException("Invalid point compression");
+ }
+ if (!trace(z).equals(k))
+ {
+ z = z.addOne();
+ }
+ yp = xp.multiply(z);
+ }
+
+ return new ECPoint.F2m(curve, xp, yp);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145PublicKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145PublicKey.java
new file mode 100644
index 000000000..d543df039
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/DSTU4145PublicKey.java
@@ -0,0 +1,46 @@
+package org.spongycastle.asn1.ua;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.math.ec.ECPoint;
+
+public class DSTU4145PublicKey
+ extends ASN1Object
+{
+
+ private ASN1OctetString pubKey;
+
+ public DSTU4145PublicKey(ECPoint pubKey)
+ {
+ // We always use big-endian in parameter encoding
+ this.pubKey = new DEROctetString(DSTU4145PointEncoder.encodePoint(pubKey));
+ }
+
+ private DSTU4145PublicKey(ASN1OctetString ocStr)
+ {
+ pubKey = ocStr;
+ }
+
+ public static DSTU4145PublicKey getInstance(Object obj)
+ {
+ if (obj instanceof DSTU4145PublicKey)
+ {
+ return (DSTU4145PublicKey)obj;
+ }
+
+ if (obj != null)
+ {
+ return new DSTU4145PublicKey(ASN1OctetString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return pubKey;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/UAObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/UAObjectIdentifiers.java
new file mode 100644
index 000000000..8794143f6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/ua/UAObjectIdentifiers.java
@@ -0,0 +1,23 @@
+package org.spongycastle.asn1.ua;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Ukrainian object identifiers
+ * + * {iso(1) member-body(2) Ukraine(804) root(2) security(1) cryptography(1) pki(1)} + *
+ * { ... pki-alg(1) pki-alg-sym(3) Dstu4145WithGost34311(1) PB(1)} + *
+ * DSTU4145 in polynomial basis has 2 oids, one for little-endian representation and one for big-endian + */ +public interface UAObjectIdentifiers +{ + /** Base OID: 1.2.804.2.1.1.1 */ + static final ASN1ObjectIdentifier UaOid = new ASN1ObjectIdentifier("1.2.804.2.1.1.1"); + + /** DSTU4145 Little Endian presentation. OID: 1.2.804.2.1.1.1.1.3.1.1 */ + static final ASN1ObjectIdentifier dstu4145le = UaOid.branch("1.3.1.1"); + /** DSTU4145 Big Endian presentation. OID: 1.2.804.2.1.1.1.1.3.1.1.1 */ + static final ASN1ObjectIdentifier dstu4145be = UaOid.branch("1.3.1.1.1.1"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/ASN1Dump.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/ASN1Dump.java new file mode 100644 index 000000000..7241b622d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/ASN1Dump.java @@ -0,0 +1,404 @@ +package org.spongycastle.asn1.util; + +import java.io.IOException; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERApplicationSpecific; +import org.spongycastle.asn1.BERConstructedOctetString; +import org.spongycastle.asn1.BEROctetString; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.BERSet; +import org.spongycastle.asn1.BERTaggedObject; +import org.spongycastle.asn1.BERTags; +import org.spongycastle.asn1.DERApplicationSpecific; +import org.spongycastle.asn1.DERBMPString; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERBoolean; +import org.spongycastle.asn1.DEREnumerated; +import org.spongycastle.asn1.DERExternal; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERT61String; +import org.spongycastle.asn1.DERUTCTime; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.DERVisibleString; +import org.spongycastle.util.encoders.Hex; + +public class ASN1Dump +{ + private static final String TAB = " "; + private static final int SAMPLE_SIZE = 32; + + /** + * dump a DER object as a formatted string with indentation + * + * @param obj the ASN1Primitive to be dumped out. + */ + static void _dumpAsString( + String indent, + boolean verbose, + ASN1Primitive obj, + StringBuffer buf) + { + String nl = System.getProperty("line.separator"); + if (obj instanceof ASN1Sequence) + { + Enumeration e = ((ASN1Sequence)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + if (obj instanceof BERSequence) + { + buf.append("BER Sequence"); + } + else if (obj instanceof DERSequence) + { + buf.append("DER Sequence"); + } + else + { + buf.append("Sequence"); + } + + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null || o.equals(DERNull.INSTANCE)) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof ASN1Primitive) + { + _dumpAsString(tab, verbose, (ASN1Primitive)o, buf); + } + else + { + _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf); + } + } + } + else if (obj instanceof ASN1TaggedObject) + { + String tab = indent + TAB; + + buf.append(indent); + if (obj instanceof BERTaggedObject) + { + buf.append("BER Tagged ["); + } + else + { + buf.append("Tagged ["); + } + + ASN1TaggedObject o = (ASN1TaggedObject)obj; + + buf.append(Integer.toString(o.getTagNo())); + buf.append(']'); + + if (!o.isExplicit()) + { + buf.append(" IMPLICIT "); + } + + buf.append(nl); + + if (o.isEmpty()) + { + buf.append(tab); + buf.append("EMPTY"); + buf.append(nl); + } + else + { + _dumpAsString(tab, verbose, o.getObject(), buf); + } + } + else if (obj instanceof ASN1Set) + { + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + + if (obj instanceof BERSet) + { + buf.append("BER Set"); + } + else + { + buf.append("DER Set"); + } + + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof ASN1Primitive) + { + _dumpAsString(tab, verbose, (ASN1Primitive)o, buf); + } + else + { + _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf); + } + } + } + else if (obj instanceof ASN1OctetString) + { + ASN1OctetString oct = (ASN1OctetString)obj; + + if (obj instanceof BEROctetString || obj instanceof BERConstructedOctetString) + { + buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] "); + } + else + { + buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] "); + } + if (verbose) + { + buf.append(dumpBinaryDataAsString(indent, oct.getOctets())); + } + else + { + buf.append(nl); + } + } + else if (obj instanceof ASN1ObjectIdentifier) + { + buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl); + } + else if (obj instanceof DERBoolean) + { + buf.append(indent + "Boolean(" + ((DERBoolean)obj).isTrue() + ")" + nl); + } + else if (obj instanceof ASN1Integer) + { + buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl); + } + else if (obj instanceof DERBitString) + { + DERBitString bt = (DERBitString)obj; + buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] "); + if (verbose) + { + buf.append(dumpBinaryDataAsString(indent, bt.getBytes())); + } + else + { + buf.append(nl); + } + } + else if (obj instanceof DERIA5String) + { + buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl); + } + else if (obj instanceof DERUTF8String) + { + buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl); + } + else if (obj instanceof DERPrintableString) + { + buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl); + } + else if (obj instanceof DERVisibleString) + { + buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl); + } + else if (obj instanceof DERBMPString) + { + buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl); + } + else if (obj instanceof DERT61String) + { + buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl); + } + else if (obj instanceof DERUTCTime) + { + buf.append(indent + "UTCTime(" + ((DERUTCTime)obj).getTime() + ") " + nl); + } + else if (obj instanceof DERGeneralizedTime) + { + buf.append(indent + "GeneralizedTime(" + ((DERGeneralizedTime)obj).getTime() + ") " + nl); + } + else if (obj instanceof BERApplicationSpecific) + { + buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl)); + } + else if (obj instanceof DERApplicationSpecific) + { + buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl)); + } + else if (obj instanceof DEREnumerated) + { + DEREnumerated en = (DEREnumerated) obj; + buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl); + } + else if (obj instanceof DERExternal) + { + DERExternal ext = (DERExternal) obj; + buf.append(indent + "External " + nl); + String tab = indent + TAB; + if (ext.getDirectReference() != null) + { + buf.append(tab + "Direct Reference: " + ext.getDirectReference().getId() + nl); + } + if (ext.getIndirectReference() != null) + { + buf.append(tab + "Indirect Reference: " + ext.getIndirectReference().toString() + nl); + } + if (ext.getDataValueDescriptor() != null) + { + _dumpAsString(tab, verbose, ext.getDataValueDescriptor(), buf); + } + buf.append(tab + "Encoding: " + ext.getEncoding() + nl); + _dumpAsString(tab, verbose, ext.getExternalContent(), buf); + } + else + { + buf.append(indent + obj.toString() + nl); + } + } + + private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl) + { + DERApplicationSpecific app = (DERApplicationSpecific)obj; + StringBuffer buf = new StringBuffer(); + + if (app.isConstructed()) + { + try + { + ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE)); + buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl); + for (Enumeration e = s.getObjects(); e.hasMoreElements();) + { + _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf); + } + } + catch (IOException e) + { + buf.append(e); + } + return buf.toString(); + } + + return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + new String(Hex.encode(app.getContents())) + ")" + nl; + } + + /** + * dump out a DER object as a formatted string, in non-verbose mode. + * + * @param obj the ASN1Primitive to be dumped out. + * @return the resulting string. + */ + public static String dumpAsString( + Object obj) + { + return dumpAsString(obj, false); + } + + /** + * Dump out the object as a string. + * + * @param obj the object to be dumped + * @param verbose if true, dump out the contents of octet and bit strings. + * @return the resulting string. + */ + public static String dumpAsString( + Object obj, + boolean verbose) + { + StringBuffer buf = new StringBuffer(); + + if (obj instanceof ASN1Primitive) + { + _dumpAsString("", verbose, (ASN1Primitive)obj, buf); + } + else if (obj instanceof ASN1Encodable) + { + _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf); + } + else + { + return "unknown object type " + obj.toString(); + } + + return buf.toString(); + } + + private static String dumpBinaryDataAsString(String indent, byte[] bytes) + { + String nl = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + + indent += TAB; + + buf.append(nl); + for (int i = 0; i < bytes.length; i += SAMPLE_SIZE) + { + if (bytes.length - i > SAMPLE_SIZE) + { + buf.append(indent); + buf.append(new String(Hex.encode(bytes, i, SAMPLE_SIZE))); + buf.append(TAB); + buf.append(calculateAscString(bytes, i, SAMPLE_SIZE)); + buf.append(nl); + } + else + { + buf.append(indent); + buf.append(new String(Hex.encode(bytes, i, bytes.length - i))); + for (int j = bytes.length - i; j != SAMPLE_SIZE; j++) + { + buf.append(" "); + } + buf.append(TAB); + buf.append(calculateAscString(bytes, i, bytes.length - i)); + buf.append(nl); + } + } + + return buf.toString(); + } + + private static String calculateAscString(byte[] bytes, int off, int len) + { + StringBuffer buf = new StringBuffer(); + + for (int i = off; i != off + len; i++) + { + if (bytes[i] >= ' ' && bytes[i] <= '~') + { + buf.append((char)bytes[i]); + } + } + + return buf.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/DERDump.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/DERDump.java new file mode 100644 index 000000000..c434abc25 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/DERDump.java @@ -0,0 +1,41 @@ +package org.spongycastle.asn1.util; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Primitive; + +/** + * @deprecated use ASN1Dump. + */ +public class DERDump + extends ASN1Dump +{ + /** + * dump out a DER object as a formatted string + * + * @param obj the ASN1Primitive to be dumped out. + */ + public static String dumpAsString( + ASN1Primitive obj) + { + StringBuffer buf = new StringBuffer(); + + _dumpAsString("", false, obj, buf); + + return buf.toString(); + } + + /** + * dump out a DER object as a formatted string + * + * @param obj the ASN1Primitive to be dumped out. + */ + public static String dumpAsString( + ASN1Encodable obj) + { + StringBuffer buf = new StringBuffer(); + + _dumpAsString("", false, obj.toASN1Primitive(), buf); + + return buf.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/Dump.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/Dump.java new file mode 100644 index 000000000..6293e7b3f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/util/Dump.java @@ -0,0 +1,22 @@ +package org.spongycastle.asn1.util; + +import java.io.FileInputStream; + +import org.spongycastle.asn1.ASN1InputStream; + +public class Dump +{ + public static void main( + String args[]) + throws Exception + { + FileInputStream fIn = new FileInputStream(args[0]); + ASN1InputStream bIn = new ASN1InputStream(fIn); + Object obj = null; + + while ((obj = bIn.readObject()) != null) + { + System.out.println(ASN1Dump.dumpAsString(obj)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java new file mode 100644 index 000000000..57fa4c25d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java @@ -0,0 +1,72 @@ +package org.spongycastle.asn1.x500; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class AttributeTypeAndValue + extends ASN1Object +{ + private ASN1ObjectIdentifier type; + private ASN1Encodable value; + + private AttributeTypeAndValue(ASN1Sequence seq) + { + type = (ASN1ObjectIdentifier)seq.getObjectAt(0); + value = (ASN1Encodable)seq.getObjectAt(1); + } + + public static AttributeTypeAndValue getInstance(Object o) + { + if (o instanceof AttributeTypeAndValue) + { + return (AttributeTypeAndValue)o; + } + else if (o != null) + { + return new AttributeTypeAndValue(ASN1Sequence.getInstance(o)); + } + + throw new IllegalArgumentException("null value in getInstance()"); + } + + public AttributeTypeAndValue( + ASN1ObjectIdentifier type, + ASN1Encodable value) + { + this.type = type; + this.value = value; + } + + public ASN1ObjectIdentifier getType() + { + return type; + } + + public ASN1Encodable getValue() + { + return value; + } + + /** + *
+ * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * value ANY DEFINED BY type } + *+ * @return a basic ASN.1 object representation. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(type); + v.add(value); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java new file mode 100644 index 000000000..8e61383b6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java @@ -0,0 +1,125 @@ +package org.spongycastle.asn1.x500; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBMPString; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERT61String; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.DERUniversalString; + +public class DirectoryString + extends ASN1Object + implements ASN1Choice, ASN1String +{ + private ASN1String string; + + public static DirectoryString getInstance(Object o) + { + if (o == null || o instanceof DirectoryString) + { + return (DirectoryString)o; + } + + if (o instanceof DERT61String) + { + return new DirectoryString((DERT61String)o); + } + + if (o instanceof DERPrintableString) + { + return new DirectoryString((DERPrintableString)o); + } + + if (o instanceof DERUniversalString) + { + return new DirectoryString((DERUniversalString)o); + } + + if (o instanceof DERUTF8String) + { + return new DirectoryString((DERUTF8String)o); + } + + if (o instanceof DERBMPString) + { + return new DirectoryString((DERBMPString)o); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName()); + } + + public static DirectoryString getInstance(ASN1TaggedObject o, boolean explicit) + { + if (!explicit) + { + throw new IllegalArgumentException("choice item must be explicitly tagged"); + } + + return getInstance(o.getObject()); + } + + private DirectoryString( + DERT61String string) + { + this.string = string; + } + + private DirectoryString( + DERPrintableString string) + { + this.string = string; + } + + private DirectoryString( + DERUniversalString string) + { + this.string = string; + } + + private DirectoryString( + DERUTF8String string) + { + this.string = string; + } + + private DirectoryString( + DERBMPString string) + { + this.string = string; + } + + public DirectoryString(String string) + { + this.string = new DERUTF8String(string); + } + + public String getString() + { + return string.getString(); + } + + public String toString() + { + return string.getString(); + } + + /** + *
+ * DirectoryString ::= CHOICE { + * teletexString TeletexString (SIZE (1..MAX)), + * printableString PrintableString (SIZE (1..MAX)), + * universalString UniversalString (SIZE (1..MAX)), + * utf8String UTF8String (SIZE (1..MAX)), + * bmpString BMPString (SIZE (1..MAX)) } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return ((ASN1Encodable)string).toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/RDN.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/RDN.java new file mode 100644 index 000000000..5e70cfb32 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/RDN.java @@ -0,0 +1,119 @@ +package org.spongycastle.asn1.x500; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; + +public class RDN + extends ASN1Object +{ + private ASN1Set values; + + private RDN(ASN1Set values) + { + this.values = values; + } + + public static RDN getInstance(Object obj) + { + if (obj instanceof RDN) + { + return (RDN)obj; + } + else if (obj != null) + { + return new RDN(ASN1Set.getInstance(obj)); + } + + return null; + } + + /** + * Create a single valued RDN. + * + * @param oid RDN type. + * @param value RDN value. + */ + public RDN(ASN1ObjectIdentifier oid, ASN1Encodable value) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(oid); + v.add(value); + + this.values = new DERSet(new DERSequence(v)); + } + + public RDN(AttributeTypeAndValue attrTAndV) + { + this.values = new DERSet(attrTAndV); + } + + /** + * Create a multi-valued RDN. + * + * @param aAndVs attribute type/value pairs making up the RDN + */ + public RDN(AttributeTypeAndValue[] aAndVs) + { + this.values = new DERSet(aAndVs); + } + + public boolean isMultiValued() + { + return this.values.size() > 1; + } + + /** + * Return the number of AttributeTypeAndValue objects in this RDN, + * + * @return size of RDN, greater than 1 if multi-valued. + */ + public int size() + { + return this.values.size(); + } + + public AttributeTypeAndValue getFirst() + { + if (this.values.size() == 0) + { + return null; + } + + return AttributeTypeAndValue.getInstance(this.values.getObjectAt(0)); + } + + public AttributeTypeAndValue[] getTypesAndValues() + { + AttributeTypeAndValue[] tmp = new AttributeTypeAndValue[values.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = AttributeTypeAndValue.getInstance(values.getObjectAt(i)); + } + + return tmp; + } + + /** + *
+ * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + *+ * @return this object as an ASN1Primitive type + */ + public ASN1Primitive toASN1Primitive() + { + return values; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500Name.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500Name.java new file mode 100644 index 000000000..a5e01b312 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500Name.java @@ -0,0 +1,326 @@ +package org.spongycastle.asn1.x500; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.style.BCStyle; + +/** + *
+ * Name ::= CHOICE { + * RDNSequence } + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * value ANY } + *+ */ +public class X500Name + extends ASN1Object + implements ASN1Choice +{ + private static X500NameStyle defaultStyle = BCStyle.INSTANCE; + + private boolean isHashCodeCalculated; + private int hashCodeValue; + + private X500NameStyle style; + private RDN[] rdns; + + public X500Name(X500NameStyle style, X500Name name) + { + this.rdns = name.rdns; + this.style = style; + } + + /** + * Return a X500Name based on the passed in tagged object. + * + * @param obj tag object holding name. + * @param explicit true if explicitly tagged false otherwise. + * @return the X500Name + */ + public static X500Name getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + // must be true as choice item + return getInstance(ASN1Sequence.getInstance(obj, true)); + } + + public static X500Name getInstance( + Object obj) + { + if (obj instanceof X500Name) + { + return (X500Name)obj; + } + else if (obj != null) + { + return new X500Name(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static X500Name getInstance( + X500NameStyle style, + Object obj) + { + if (obj instanceof X500Name) + { + return getInstance(style, ((X500Name)obj).toASN1Primitive()); + } + else if (obj != null) + { + return new X500Name(style, ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Constructor from ASN1Sequence + * + * the principal will be a list of constructed sets, each containing an (OID, String) pair. + */ + private X500Name( + ASN1Sequence seq) + { + this(defaultStyle, seq); + } + + private X500Name( + X500NameStyle style, + ASN1Sequence seq) + { + this.style = style; + this.rdns = new RDN[seq.size()]; + + int index = 0; + + for (Enumeration e = seq.getObjects(); e.hasMoreElements();) + { + rdns[index++] = RDN.getInstance(e.nextElement()); + } + } + + public X500Name( + RDN[] rDNs) + { + this(defaultStyle, rDNs); + } + + public X500Name( + X500NameStyle style, + RDN[] rDNs) + { + this.rdns = rDNs; + this.style = style; + } + + public X500Name( + String dirName) + { + this(defaultStyle, dirName); + } + + public X500Name( + X500NameStyle style, + String dirName) + { + this(style.fromString(dirName)); + + this.style = style; + } + + /** + * return an array of RDNs in structure order. + * + * @return an array of RDN objects. + */ + public RDN[] getRDNs() + { + RDN[] tmp = new RDN[this.rdns.length]; + + System.arraycopy(rdns, 0, tmp, 0, tmp.length); + + return tmp; + } + + /** + * return an array of OIDs contained in the attribute type of each RDN in structure order. + * + * @return an array, possibly zero length, of ASN1ObjectIdentifiers objects. + */ + public ASN1ObjectIdentifier[] getAttributeTypes() + { + int count = 0; + + for (int i = 0; i != rdns.length; i++) + { + RDN rdn = rdns[i]; + + count += rdn.size(); + } + + ASN1ObjectIdentifier[] res = new ASN1ObjectIdentifier[count]; + + count = 0; + + for (int i = 0; i != rdns.length; i++) + { + RDN rdn = rdns[i]; + + if (rdn.isMultiValued()) + { + AttributeTypeAndValue[] attr = rdn.getTypesAndValues(); + for (int j = 0; j != attr.length; j++) + { + res[count++] = attr[j].getType(); + } + } + else if (rdn.size() != 0) + { + res[count++] = rdn.getFirst().getType(); + } + } + + return res; + } + + /** + * return an array of RDNs containing the attribute type given by OID in structure order. + * + * @param attributeType the type OID we are looking for. + * @return an array, possibly zero length, of RDN objects. + */ + public RDN[] getRDNs(ASN1ObjectIdentifier attributeType) + { + RDN[] res = new RDN[rdns.length]; + int count = 0; + + for (int i = 0; i != rdns.length; i++) + { + RDN rdn = rdns[i]; + + if (rdn.isMultiValued()) + { + AttributeTypeAndValue[] attr = rdn.getTypesAndValues(); + for (int j = 0; j != attr.length; j++) + { + if (attr[j].getType().equals(attributeType)) + { + res[count++] = rdn; + break; + } + } + } + else + { + if (rdn.getFirst().getType().equals(attributeType)) + { + res[count++] = rdn; + } + } + } + + RDN[] tmp = new RDN[count]; + + System.arraycopy(res, 0, tmp, 0, tmp.length); + + return tmp; + } + + public ASN1Primitive toASN1Primitive() + { + return new DERSequence(rdns); + } + + public int hashCode() + { + if (isHashCodeCalculated) + { + return hashCodeValue; + } + + isHashCodeCalculated = true; + + hashCodeValue = style.calculateHashCode(this); + + return hashCodeValue; + } + + /** + * test for equality - note: case is ignored. + */ + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + + if (!(obj instanceof X500Name || obj instanceof ASN1Sequence)) + { + return false; + } + + ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive(); + + if (this.toASN1Primitive().equals(derO)) + { + return true; + } + + try + { + return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive()))); + } + catch (Exception e) + { + return false; + } + } + + public String toString() + { + return style.toString(this); + } + + /** + * Set the default style for X500Name construction. + * + * @param style an X500NameStyle + */ + public static void setDefaultStyle(X500NameStyle style) + { + if (style == null) + { + throw new NullPointerException("cannot set style to null"); + } + + defaultStyle = style; + } + + /** + * Return the current default style. + * + * @return default style for X500Name construction. + */ + public static X500NameStyle getDefaultStyle() + { + return defaultStyle; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java new file mode 100644 index 000000000..6640eff95 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java @@ -0,0 +1,87 @@ +package org.spongycastle.asn1.x500; + +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x500.style.BCStyle; + +public class X500NameBuilder +{ + private X500NameStyle template; + private Vector rdns = new Vector(); + + public X500NameBuilder() + { + this(BCStyle.INSTANCE); + } + + public X500NameBuilder(X500NameStyle template) + { + this.template = template; + } + + public X500NameBuilder addRDN(ASN1ObjectIdentifier oid, String value) + { + this.addRDN(oid, template.stringToValue(oid, value)); + + return this; + } + + public X500NameBuilder addRDN(ASN1ObjectIdentifier oid, ASN1Encodable value) + { + rdns.addElement(new RDN(oid, value)); + + return this; + } + + public X500NameBuilder addRDN(AttributeTypeAndValue attrTAndV) + { + rdns.addElement(new RDN(attrTAndV)); + + return this; + } + + public X500NameBuilder addMultiValuedRDN(ASN1ObjectIdentifier[] oids, String[] values) + { + ASN1Encodable[] vals = new ASN1Encodable[values.length]; + + for (int i = 0; i != vals.length; i++) + { + vals[i] = template.stringToValue(oids[i], values[i]); + } + + return addMultiValuedRDN(oids, vals); + } + + public X500NameBuilder addMultiValuedRDN(ASN1ObjectIdentifier[] oids, ASN1Encodable[] values) + { + AttributeTypeAndValue[] avs = new AttributeTypeAndValue[oids.length]; + + for (int i = 0; i != oids.length; i++) + { + avs[i] = new AttributeTypeAndValue(oids[i], values[i]); + } + + return addMultiValuedRDN(avs); + } + + public X500NameBuilder addMultiValuedRDN(AttributeTypeAndValue[] attrTAndVs) + { + rdns.addElement(new RDN(attrTAndVs)); + + return this; + } + + public X500Name build() + { + RDN[] vals = new RDN[rdns.size()]; + + for (int i = 0; i != vals.length; i++) + { + vals[i] = (RDN)rdns.elementAt(i); + } + + return new X500Name(template, vals); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java new file mode 100644 index 000000000..8c7161705 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java @@ -0,0 +1,79 @@ +package org.spongycastle.asn1.x500; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * It turns out that the number of standard ways the fields in a DN should be + * encoded into their ASN.1 counterparts is rapidly approaching the + * number of machines on the internet. By default the X500Name class + * will produce UTF8Strings in line with the current recommendations (RFC 3280). + *
+ */ +public interface X500NameStyle +{ + /** + * Convert the passed in String value into the appropriate ASN.1 + * encoded object. + * + * @param oid the OID associated with the value in the DN. + * @param value the value of the particular DN component. + * @return the ASN.1 equivalent for the value. + */ + ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value); + + /** + * Return the OID associated with the passed in name. + * + * @param attrName the string to match. + * @return an OID + */ + ASN1ObjectIdentifier attrNameToOID(String attrName); + + /** + * Return an array of RDN generated from the passed in String. + * @param dirName the String representation. + * @return an array of corresponding RDNs. + */ + RDN[] fromString(String dirName); + + /** + * Return true if the two names are equal. + * + * @param name1 first name for comparison. + * @param name2 second name for comparison. + * @return true if name1 = name 2, false otherwise. + */ + boolean areEqual(X500Name name1, X500Name name2); + + /** + * Calculate a hashCode for the passed in name. + * + * @param name the name the hashCode is required for. + * @return the calculated hashCode. + */ + int calculateHashCode(X500Name name); + + /** + * Convert the passed in X500Name to a String. + * @param name the name to convert. + * @return a String representation. + */ + String toString(X500Name name); + + /** + * Return the display name for toString() associated with the OID. + * + * @param oid the OID of interest. + * @return the name displayed in toString(), null if no mapping provided. + */ + String oidToDisplayName(ASN1ObjectIdentifier oid); + + /** + * Return the acceptable names in a String DN that map to OID. + * + * @param oid the OID of interest. + * @return an array of String aliases for the OID, zero length if there are none. + */ + String[] oidToAttrNames(ASN1ObjectIdentifier oid); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java new file mode 100644 index 000000000..23609e726 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java @@ -0,0 +1,36 @@ +package org.spongycastle.asn1.x500.style; + +import org.spongycastle.asn1.x500.RDN; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x500.X500NameStyle; + +/** + * Variation of BCStyle that insists on strict ordering for equality + * and hashCode comparisons + */ +public class BCStrictStyle + extends BCStyle +{ + public static final X500NameStyle INSTANCE = new BCStrictStyle(); + + public boolean areEqual(X500Name name1, X500Name name2) + { + RDN[] rdns1 = name1.getRDNs(); + RDN[] rdns2 = name2.getRDNs(); + + if (rdns1.length != rdns2.length) + { + return false; + } + + for (int i = 0; i != rdns1.length; i++) + { + if (!rdnAreEqual(rdns1[i], rdns2[i])) + { + return false; + } + } + + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java new file mode 100644 index 000000000..b4d01f44f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java @@ -0,0 +1,481 @@ +package org.spongycastle.asn1.x500.style; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x500.AttributeTypeAndValue; +import org.spongycastle.asn1.x500.RDN; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x500.X500NameStyle; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; + +public class BCStyle + implements X500NameStyle +{ + /** + * country code - StringType(SIZE(2)) + */ + public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6"); + + /** + * organization - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10"); + + /** + * organizational unit name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11"); + + /** + * Title + */ + public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12"); + + /** + * common name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3"); + + /** + * device serial number name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5"); + + /** + * street - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9"); + + /** + * device serial number name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier SERIALNUMBER = SN; + + /** + * locality name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7"); + + /** + * state, or province name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8"); + + /** + * Naming attributes of type X520name + */ + public static final ASN1ObjectIdentifier SURNAME = new ASN1ObjectIdentifier("2.5.4.4"); + public static final ASN1ObjectIdentifier GIVENNAME = new ASN1ObjectIdentifier("2.5.4.42"); + public static final ASN1ObjectIdentifier INITIALS = new ASN1ObjectIdentifier("2.5.4.43"); + public static final ASN1ObjectIdentifier GENERATION = new ASN1ObjectIdentifier("2.5.4.44"); + public static final ASN1ObjectIdentifier UNIQUE_IDENTIFIER = new ASN1ObjectIdentifier("2.5.4.45"); + + /** + * businessCategory - DirectoryString(SIZE(1..128) + */ + public static final ASN1ObjectIdentifier BUSINESS_CATEGORY = new ASN1ObjectIdentifier( + "2.5.4.15"); + + /** + * postalCode - DirectoryString(SIZE(1..40) + */ + public static final ASN1ObjectIdentifier POSTAL_CODE = new ASN1ObjectIdentifier( + "2.5.4.17"); + + /** + * dnQualifier - DirectoryString(SIZE(1..64) + */ + public static final ASN1ObjectIdentifier DN_QUALIFIER = new ASN1ObjectIdentifier( + "2.5.4.46"); + + /** + * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64) + */ + public static final ASN1ObjectIdentifier PSEUDONYM = new ASN1ObjectIdentifier( + "2.5.4.65"); + + + /** + * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z + */ + public static final ASN1ObjectIdentifier DATE_OF_BIRTH = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.1"); + + /** + * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128) + */ + public static final ASN1ObjectIdentifier PLACE_OF_BIRTH = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.2"); + + /** + * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f" + */ + public static final ASN1ObjectIdentifier GENDER = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.3"); + + /** + * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166 + * codes only + */ + public static final ASN1ObjectIdentifier COUNTRY_OF_CITIZENSHIP = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.4"); + + /** + * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166 + * codes only + */ + public static final ASN1ObjectIdentifier COUNTRY_OF_RESIDENCE = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.5"); + + + /** + * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64) + */ + public static final ASN1ObjectIdentifier NAME_AT_BIRTH = new ASN1ObjectIdentifier("1.3.36.8.3.14"); + + /** + * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF + * DirectoryString(SIZE(1..30)) + */ + public static final ASN1ObjectIdentifier POSTAL_ADDRESS = new ASN1ObjectIdentifier("2.5.4.16"); + + /** + * RFC 2256 dmdName + */ + public static final ASN1ObjectIdentifier DMD_NAME = new ASN1ObjectIdentifier("2.5.4.54"); + + /** + * id-at-telephoneNumber + */ + public static final ASN1ObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber; + + /** + * id-at-name + */ + public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name; + + /** + * Email address (RSA PKCS#9 extension) - IA5String. + *
Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here. + */ + public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress; + + /** + * more from PKCS#9 + */ + public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName; + public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress; + + /** + * email address in Verisign certificates + */ + public static final ASN1ObjectIdentifier E = EmailAddress; + + /* + * others... + */ + public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25"); + + /** + * LDAP User id. + */ + public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1"); + + /** + * default look up table translating OID values into their common symbols following + * the convention in RFC 2253 with a few extras + */ + private static final Hashtable DefaultSymbols = new Hashtable(); + + /** + * look up table translating common symbols into their OIDS. + */ + private static final Hashtable DefaultLookUp = new Hashtable(); + + static + { + DefaultSymbols.put(C, "C"); + DefaultSymbols.put(O, "O"); + DefaultSymbols.put(T, "T"); + DefaultSymbols.put(OU, "OU"); + DefaultSymbols.put(CN, "CN"); + DefaultSymbols.put(L, "L"); + DefaultSymbols.put(ST, "ST"); + DefaultSymbols.put(SN, "SERIALNUMBER"); + DefaultSymbols.put(EmailAddress, "E"); + DefaultSymbols.put(DC, "DC"); + DefaultSymbols.put(UID, "UID"); + DefaultSymbols.put(STREET, "STREET"); + DefaultSymbols.put(SURNAME, "SURNAME"); + DefaultSymbols.put(GIVENNAME, "GIVENNAME"); + DefaultSymbols.put(INITIALS, "INITIALS"); + DefaultSymbols.put(GENERATION, "GENERATION"); + DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress"); + DefaultSymbols.put(UnstructuredName, "unstructuredName"); + DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier"); + DefaultSymbols.put(DN_QUALIFIER, "DN"); + DefaultSymbols.put(PSEUDONYM, "Pseudonym"); + DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress"); + DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth"); + DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship"); + DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence"); + DefaultSymbols.put(GENDER, "Gender"); + DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth"); + DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth"); + DefaultSymbols.put(POSTAL_CODE, "PostalCode"); + DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory"); + DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber"); + DefaultSymbols.put(NAME, "Name"); + + DefaultLookUp.put("c", C); + DefaultLookUp.put("o", O); + DefaultLookUp.put("t", T); + DefaultLookUp.put("ou", OU); + DefaultLookUp.put("cn", CN); + DefaultLookUp.put("l", L); + DefaultLookUp.put("st", ST); + DefaultLookUp.put("sn", SN); + DefaultLookUp.put("serialnumber", SN); + DefaultLookUp.put("street", STREET); + DefaultLookUp.put("emailaddress", E); + DefaultLookUp.put("dc", DC); + DefaultLookUp.put("e", E); + DefaultLookUp.put("uid", UID); + DefaultLookUp.put("surname", SURNAME); + DefaultLookUp.put("givenname", GIVENNAME); + DefaultLookUp.put("initials", INITIALS); + DefaultLookUp.put("generation", GENERATION); + DefaultLookUp.put("unstructuredaddress", UnstructuredAddress); + DefaultLookUp.put("unstructuredname", UnstructuredName); + DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER); + DefaultLookUp.put("dn", DN_QUALIFIER); + DefaultLookUp.put("pseudonym", PSEUDONYM); + DefaultLookUp.put("postaladdress", POSTAL_ADDRESS); + DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH); + DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP); + DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE); + DefaultLookUp.put("gender", GENDER); + DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH); + DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH); + DefaultLookUp.put("postalcode", POSTAL_CODE); + DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY); + DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER); + DefaultLookUp.put("name", NAME); + } + + /** + * Singleton instance. + */ + public static final X500NameStyle INSTANCE = new BCStyle(); + + protected final Hashtable defaultLookUp; + protected final Hashtable defaultSymbols; + + protected BCStyle() + { + defaultSymbols = copyHashTable(DefaultSymbols); + defaultLookUp = copyHashTable(DefaultLookUp); + } + + public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value) + { + if (value.length() != 0 && value.charAt(0) == '#') + { + try + { + return IETFUtils.valueFromHexString(value, 1); + } + catch (IOException e) + { + throw new RuntimeException("can't recode value for oid " + oid.getId()); + } + } + else + { + if (value.length() != 0 && value.charAt(0) == '\\') + { + value = value.substring(1); + } + if (oid.equals(EmailAddress) || oid.equals(DC)) + { + return new DERIA5String(value); + } + else if (oid.equals(DATE_OF_BIRTH)) // accept time string as well as # (for compatibility) + { + return new ASN1GeneralizedTime(value); + } + else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER) + || oid.equals(TELEPHONE_NUMBER)) + { + return new DERPrintableString(value); + } + } + + return new DERUTF8String(value); + } + + public String oidToDisplayName(ASN1ObjectIdentifier oid) + { + return (String)DefaultSymbols.get(oid); + } + + public String[] oidToAttrNames(ASN1ObjectIdentifier oid) + { + return IETFUtils.findAttrNamesForOID(oid, defaultLookUp); + } + + public ASN1ObjectIdentifier attrNameToOID(String attrName) + { + return IETFUtils.decodeAttrName(attrName, defaultLookUp); + } + + public boolean areEqual(X500Name name1, X500Name name2) + { + RDN[] rdns1 = name1.getRDNs(); + RDN[] rdns2 = name2.getRDNs(); + + if (rdns1.length != rdns2.length) + { + return false; + } + + boolean reverse = false; + + if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null) + { + reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward + } + + for (int i = 0; i != rdns1.length; i++) + { + if (!foundMatch(reverse, rdns1[i], rdns2)) + { + return false; + } + } + + return true; + } + + private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs) + { + if (reverse) + { + for (int i = possRDNs.length - 1; i >= 0; i--) + { + if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) + { + possRDNs[i] = null; + return true; + } + } + } + else + { + for (int i = 0; i != possRDNs.length; i++) + { + if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) + { + possRDNs[i] = null; + return true; + } + } + } + + return false; + } + + protected boolean rdnAreEqual(RDN rdn1, RDN rdn2) + { + return IETFUtils.rDNAreEqual(rdn1, rdn2); + } + + public RDN[] fromString(String dirName) + { + return IETFUtils.rDNsFromString(dirName, this); + } + + public int calculateHashCode(X500Name name) + { + int hashCodeValue = 0; + RDN[] rdns = name.getRDNs(); + + // this needs to be order independent, like equals + for (int i = 0; i != rdns.length; i++) + { + if (rdns[i].isMultiValued()) + { + AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues(); + + for (int j = 0; j != atv.length; j++) + { + hashCodeValue ^= atv[j].getType().hashCode(); + hashCodeValue ^= calcHashCode(atv[j].getValue()); + } + } + else + { + hashCodeValue ^= rdns[i].getFirst().getType().hashCode(); + hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue()); + } + } + + return hashCodeValue; + } + + private int calcHashCode(ASN1Encodable enc) + { + String value = IETFUtils.valueToString(enc); + + value = IETFUtils.canonicalize(value); + + return value.hashCode(); + } + + public String toString(X500Name name) + { + StringBuffer buf = new StringBuffer(); + boolean first = true; + + RDN[] rdns = name.getRDNs(); + + for (int i = 0; i < rdns.length; i++) + { + if (first) + { + first = false; + } + else + { + buf.append(','); + } + + IETFUtils.appendRDN(buf, rdns[i], defaultSymbols); + } + + return buf.toString(); + } + + private static Hashtable copyHashTable(Hashtable paramsMap) + { + Hashtable newTable = new Hashtable(); + + Enumeration keys = paramsMap.keys(); + while (keys.hasMoreElements()) + { + Object key = keys.nextElement(); + newTable.put(key, paramsMap.get(key)); + } + + return newTable; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java new file mode 100644 index 000000000..14961e4c9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java @@ -0,0 +1,572 @@ +package org.spongycastle.asn1.x500.style; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.DERUniversalString; +import org.spongycastle.asn1.x500.AttributeTypeAndValue; +import org.spongycastle.asn1.x500.RDN; +import org.spongycastle.asn1.x500.X500NameBuilder; +import org.spongycastle.asn1.x500.X500NameStyle; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; + +public class IETFUtils +{ + private static String unescape(String elt) + { + if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0)) + { + return elt.trim(); + } + + char[] elts = elt.toCharArray(); + boolean escaped = false; + boolean quoted = false; + StringBuffer buf = new StringBuffer(elt.length()); + int start = 0; + + // if it's an escaped hash string and not an actual encoding in string form + // we need to leave it escaped. + if (elts[0] == '\\') + { + if (elts[1] == '#') + { + start = 2; + buf.append("\\#"); + } + } + + boolean nonWhiteSpaceEncountered = false; + int lastEscaped = 0; + char hex1 = 0; + + for (int i = start; i != elts.length; i++) + { + char c = elts[i]; + + if (c != ' ') + { + nonWhiteSpaceEncountered = true; + } + + if (c == '"') + { + if (!escaped) + { + quoted = !quoted; + } + else + { + buf.append(c); + } + escaped = false; + } + else if (c == '\\' && !(escaped || quoted)) + { + escaped = true; + lastEscaped = buf.length(); + } + else + { + if (c == ' ' && !escaped && !nonWhiteSpaceEncountered) + { + continue; + } + if (escaped && isHexDigit(c)) + { + if (hex1 != 0) + { + buf.append((char)(convertHex(hex1) * 16 + convertHex(c))); + escaped = false; + hex1 = 0; + continue; + } + hex1 = c; + continue; + } + buf.append(c); + escaped = false; + } + } + + if (buf.length() > 0) + { + while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1)) + { + buf.setLength(buf.length() - 1); + } + } + + return buf.toString(); + } + + private static boolean isHexDigit(char c) + { + return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); + } + + private static int convertHex(char c) + { + if ('0' <= c && c <= '9') + { + return c - '0'; + } + if ('a' <= c && c <= 'f') + { + return c - 'a' + 10; + } + return c - 'A' + 10; + } + + public static RDN[] rDNsFromString(String name, X500NameStyle x500Style) + { + X500NameTokenizer nTok = new X500NameTokenizer(name); + X500NameBuilder builder = new X500NameBuilder(x500Style); + + while (nTok.hasMoreTokens()) + { + String token = nTok.nextToken(); + + if (token.indexOf('+') > 0) + { + X500NameTokenizer pTok = new X500NameTokenizer(token, '+'); + X500NameTokenizer vTok = new X500NameTokenizer(pTok.nextToken(), '='); + + String attr = vTok.nextToken(); + + if (!vTok.hasMoreTokens()) + { + throw new IllegalArgumentException("badly formatted directory string"); + } + + String value = vTok.nextToken(); + ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim()); + + if (pTok.hasMoreTokens()) + { + Vector oids = new Vector(); + Vector values = new Vector(); + + oids.addElement(oid); + values.addElement(unescape(value)); + + while (pTok.hasMoreTokens()) + { + vTok = new X500NameTokenizer(pTok.nextToken(), '='); + + attr = vTok.nextToken(); + + if (!vTok.hasMoreTokens()) + { + throw new IllegalArgumentException("badly formatted directory string"); + } + + value = vTok.nextToken(); + oid = x500Style.attrNameToOID(attr.trim()); + + + oids.addElement(oid); + values.addElement(unescape(value)); + } + + builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values)); + } + else + { + builder.addRDN(oid, unescape(value)); + } + } + else + { + X500NameTokenizer vTok = new X500NameTokenizer(token, '='); + + String attr = vTok.nextToken(); + + if (!vTok.hasMoreTokens()) + { + throw new IllegalArgumentException("badly formatted directory string"); + } + + String value = vTok.nextToken(); + ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim()); + + builder.addRDN(oid, unescape(value)); + } + } + + return builder.build().getRDNs(); + } + + private static String[] toValueArray(Vector values) + { + String[] tmp = new String[values.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = (String)values.elementAt(i); + } + + return tmp; + } + + private static ASN1ObjectIdentifier[] toOIDArray(Vector oids) + { + ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = (ASN1ObjectIdentifier)oids.elementAt(i); + } + + return tmp; + } + + public static String[] findAttrNamesForOID( + ASN1ObjectIdentifier oid, + Hashtable lookup) + { + int count = 0; + for (Enumeration en = lookup.elements(); en.hasMoreElements();) + { + if (oid.equals(en.nextElement())) + { + count++; + } + } + + String[] aliases = new String[count]; + count = 0; + + for (Enumeration en = lookup.keys(); en.hasMoreElements();) + { + String key = (String)en.nextElement(); + if (oid.equals(lookup.get(key))) + { + aliases[count++] = key; + } + } + + return aliases; + } + + public static ASN1ObjectIdentifier decodeAttrName( + String name, + Hashtable lookUp) + { + if (Strings.toUpperCase(name).startsWith("OID.")) + { + return new ASN1ObjectIdentifier(name.substring(4)); + } + else if (name.charAt(0) >= '0' && name.charAt(0) <= '9') + { + return new ASN1ObjectIdentifier(name); + } + + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name)); + if (oid == null) + { + throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name"); + } + + return oid; + } + + public static ASN1Encodable valueFromHexString( + String str, + int off) + throws IOException + { + byte[] data = new byte[(str.length() - off) / 2]; + for (int index = 0; index != data.length; index++) + { + char left = str.charAt((index * 2) + off); + char right = str.charAt((index * 2) + off + 1); + + data[index] = (byte)((convertHex(left) << 4) | convertHex(right)); + } + + return ASN1Primitive.fromByteArray(data); + } + + public static void appendRDN( + StringBuffer buf, + RDN rdn, + Hashtable oidSymbols) + { + if (rdn.isMultiValued()) + { + AttributeTypeAndValue[] atv = rdn.getTypesAndValues(); + boolean firstAtv = true; + + for (int j = 0; j != atv.length; j++) + { + if (firstAtv) + { + firstAtv = false; + } + else + { + buf.append('+'); + } + + IETFUtils.appendTypeAndValue(buf, atv[j], oidSymbols); + } + } + else + { + IETFUtils.appendTypeAndValue(buf, rdn.getFirst(), oidSymbols); + } + } + + public static void appendTypeAndValue( + StringBuffer buf, + AttributeTypeAndValue typeAndValue, + Hashtable oidSymbols) + { + String sym = (String)oidSymbols.get(typeAndValue.getType()); + + if (sym != null) + { + buf.append(sym); + } + else + { + buf.append(typeAndValue.getType().getId()); + } + + buf.append('='); + + buf.append(valueToString(typeAndValue.getValue())); + } + + public static String valueToString(ASN1Encodable value) + { + StringBuffer vBuf = new StringBuffer(); + + if (value instanceof ASN1String && !(value instanceof DERUniversalString)) + { + String v = ((ASN1String)value).getString(); + if (v.length() > 0 && v.charAt(0) == '#') + { + vBuf.append("\\" + v); + } + else + { + vBuf.append(v); + } + } + else + { + try + { + vBuf.append("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)))); + } + catch (IOException e) + { + throw new IllegalArgumentException("Other value has no encoded form"); + } + } + + int end = vBuf.length(); + int index = 0; + + if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#') + { + index += 2; + } + + while (index != end) + { + if ((vBuf.charAt(index) == ',') + || (vBuf.charAt(index) == '"') + || (vBuf.charAt(index) == '\\') + || (vBuf.charAt(index) == '+') + || (vBuf.charAt(index) == '=') + || (vBuf.charAt(index) == '<') + || (vBuf.charAt(index) == '>') + || (vBuf.charAt(index) == ';')) + { + vBuf.insert(index, "\\"); + index++; + end++; + } + + index++; + } + + int start = 0; + if (vBuf.length() > 0) + { + while (vBuf.length() > start && vBuf.charAt(start) == ' ') + { + vBuf.insert(start, "\\"); + start += 2; + } + } + + int endBuf = vBuf.length() - 1; + + while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ') + { + vBuf.insert(endBuf, '\\'); + endBuf--; + } + + return vBuf.toString(); + } + + private static String bytesToString( + byte[] data) + { + char[] cs = new char[data.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(data[i] & 0xff); + } + + return new String(cs); + } + + public static String canonicalize(String s) + { + String value = Strings.toLowerCase(s.trim()); + + if (value.length() > 0 && value.charAt(0) == '#') + { + ASN1Primitive obj = decodeObject(value); + + if (obj instanceof ASN1String) + { + value = Strings.toLowerCase(((ASN1String)obj).getString().trim()); + } + } + + value = stripInternalSpaces(value); + + return value; + } + + private static ASN1Primitive decodeObject(String oValue) + { + try + { + return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1))); + } + catch (IOException e) + { + throw new IllegalStateException("unknown encoding in name: " + e); + } + } + + public static String stripInternalSpaces( + String str) + { + StringBuffer res = new StringBuffer(); + + if (str.length() != 0) + { + char c1 = str.charAt(0); + + res.append(c1); + + for (int k = 1; k < str.length(); k++) + { + char c2 = str.charAt(k); + if (!(c1 == ' ' && c2 == ' ')) + { + res.append(c2); + } + c1 = c2; + } + } + + return res.toString(); + } + + public static boolean rDNAreEqual(RDN rdn1, RDN rdn2) + { + if (rdn1.isMultiValued()) + { + if (rdn2.isMultiValued()) + { + AttributeTypeAndValue[] atvs1 = rdn1.getTypesAndValues(); + AttributeTypeAndValue[] atvs2 = rdn2.getTypesAndValues(); + + if (atvs1.length != atvs2.length) + { + return false; + } + + for (int i = 0; i != atvs1.length; i++) + { + if (!atvAreEqual(atvs1[i], atvs2[i])) + { + return false; + } + } + } + else + { + return false; + } + } + else + { + if (!rdn2.isMultiValued()) + { + return atvAreEqual(rdn1.getFirst(), rdn2.getFirst()); + } + else + { + return false; + } + } + + return true; + } + + private static boolean atvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2) + { + if (atv1 == atv2) + { + return true; + } + + if (atv1 == null) + { + return false; + } + + if (atv2 == null) + { + return false; + } + + ASN1ObjectIdentifier o1 = atv1.getType(); + ASN1ObjectIdentifier o2 = atv2.getType(); + + if (!o1.equals(o2)) + { + return false; + } + + String v1 = IETFUtils.canonicalize(IETFUtils.valueToString(atv1.getValue())); + String v2 = IETFUtils.canonicalize(IETFUtils.valueToString(atv2.getValue())); + + if (!v1.equals(v2)) + { + return false; + } + + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java new file mode 100644 index 000000000..ce2cbea08 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java @@ -0,0 +1,380 @@ +package org.spongycastle.asn1.x500.style; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.x500.AttributeTypeAndValue; +import org.spongycastle.asn1.x500.RDN; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x500.X500NameStyle; + +public class RFC4519Style + implements X500NameStyle +{ + public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15"); + public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6"); + public static final ASN1ObjectIdentifier cn = new ASN1ObjectIdentifier("2.5.4.3"); + public static final ASN1ObjectIdentifier dc = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25"); + public static final ASN1ObjectIdentifier description = new ASN1ObjectIdentifier("2.5.4.13"); + public static final ASN1ObjectIdentifier destinationIndicator = new ASN1ObjectIdentifier("2.5.4.27"); + public static final ASN1ObjectIdentifier distinguishedName = new ASN1ObjectIdentifier("2.5.4.49"); + public static final ASN1ObjectIdentifier dnQualifier = new ASN1ObjectIdentifier("2.5.4.46"); + public static final ASN1ObjectIdentifier enhancedSearchGuide = new ASN1ObjectIdentifier("2.5.4.47"); + public static final ASN1ObjectIdentifier facsimileTelephoneNumber = new ASN1ObjectIdentifier("2.5.4.23"); + public static final ASN1ObjectIdentifier generationQualifier = new ASN1ObjectIdentifier("2.5.4.44"); + public static final ASN1ObjectIdentifier givenName = new ASN1ObjectIdentifier("2.5.4.42"); + public static final ASN1ObjectIdentifier houseIdentifier = new ASN1ObjectIdentifier("2.5.4.51"); + public static final ASN1ObjectIdentifier initials = new ASN1ObjectIdentifier("2.5.4.43"); + public static final ASN1ObjectIdentifier internationalISDNNumber = new ASN1ObjectIdentifier("2.5.4.25"); + public static final ASN1ObjectIdentifier l = new ASN1ObjectIdentifier("2.5.4.7"); + public static final ASN1ObjectIdentifier member = new ASN1ObjectIdentifier("2.5.4.31"); + public static final ASN1ObjectIdentifier name = new ASN1ObjectIdentifier("2.5.4.41"); + public static final ASN1ObjectIdentifier o = new ASN1ObjectIdentifier("2.5.4.10"); + public static final ASN1ObjectIdentifier ou = new ASN1ObjectIdentifier("2.5.4.11"); + public static final ASN1ObjectIdentifier owner = new ASN1ObjectIdentifier("2.5.4.32"); + public static final ASN1ObjectIdentifier physicalDeliveryOfficeName = new ASN1ObjectIdentifier("2.5.4.19"); + public static final ASN1ObjectIdentifier postalAddress = new ASN1ObjectIdentifier("2.5.4.16"); + public static final ASN1ObjectIdentifier postalCode = new ASN1ObjectIdentifier("2.5.4.17"); + public static final ASN1ObjectIdentifier postOfficeBox = new ASN1ObjectIdentifier("2.5.4.18"); + public static final ASN1ObjectIdentifier preferredDeliveryMethod = new ASN1ObjectIdentifier("2.5.4.28"); + public static final ASN1ObjectIdentifier registeredAddress = new ASN1ObjectIdentifier("2.5.4.26"); + public static final ASN1ObjectIdentifier roleOccupant = new ASN1ObjectIdentifier("2.5.4.33"); + public static final ASN1ObjectIdentifier searchGuide = new ASN1ObjectIdentifier("2.5.4.14"); + public static final ASN1ObjectIdentifier seeAlso = new ASN1ObjectIdentifier("2.5.4.34"); + public static final ASN1ObjectIdentifier serialNumber = new ASN1ObjectIdentifier("2.5.4.5"); + public static final ASN1ObjectIdentifier sn = new ASN1ObjectIdentifier("2.5.4.4"); + public static final ASN1ObjectIdentifier st = new ASN1ObjectIdentifier("2.5.4.8"); + public static final ASN1ObjectIdentifier street = new ASN1ObjectIdentifier("2.5.4.9"); + public static final ASN1ObjectIdentifier telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20"); + public static final ASN1ObjectIdentifier teletexTerminalIdentifier = new ASN1ObjectIdentifier("2.5.4.22"); + public static final ASN1ObjectIdentifier telexNumber = new ASN1ObjectIdentifier("2.5.4.21"); + public static final ASN1ObjectIdentifier title = new ASN1ObjectIdentifier("2.5.4.12"); + public static final ASN1ObjectIdentifier uid = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1"); + public static final ASN1ObjectIdentifier uniqueMember = new ASN1ObjectIdentifier("2.5.4.50"); + public static final ASN1ObjectIdentifier userPassword = new ASN1ObjectIdentifier("2.5.4.35"); + public static final ASN1ObjectIdentifier x121Address = new ASN1ObjectIdentifier("2.5.4.24"); + public static final ASN1ObjectIdentifier x500UniqueIdentifier = new ASN1ObjectIdentifier("2.5.4.45"); + + /** + * default look up table translating OID values into their common symbols following + * the convention in RFC 2253 with a few extras + */ + private static final Hashtable DefaultSymbols = new Hashtable(); + + /** + * look up table translating common symbols into their OIDS. + */ + private static final Hashtable DefaultLookUp = new Hashtable(); + + static + { + DefaultSymbols.put(businessCategory, "businessCategory"); + DefaultSymbols.put(c, "c"); + DefaultSymbols.put(cn, "cn"); + DefaultSymbols.put(dc, "dc"); + DefaultSymbols.put(description, "description"); + DefaultSymbols.put(destinationIndicator, "destinationIndicator"); + DefaultSymbols.put(distinguishedName, "distinguishedName"); + DefaultSymbols.put(dnQualifier, "dnQualifier"); + DefaultSymbols.put(enhancedSearchGuide, "enhancedSearchGuide"); + DefaultSymbols.put(facsimileTelephoneNumber, "facsimileTelephoneNumber"); + DefaultSymbols.put(generationQualifier, "generationQualifier"); + DefaultSymbols.put(givenName, "givenName"); + DefaultSymbols.put(houseIdentifier, "houseIdentifier"); + DefaultSymbols.put(initials, "initials"); + DefaultSymbols.put(internationalISDNNumber, "internationalISDNNumber"); + DefaultSymbols.put(l, "l"); + DefaultSymbols.put(member, "member"); + DefaultSymbols.put(name, "name"); + DefaultSymbols.put(o, "o"); + DefaultSymbols.put(ou, "ou"); + DefaultSymbols.put(owner, "owner"); + DefaultSymbols.put(physicalDeliveryOfficeName, "physicalDeliveryOfficeName"); + DefaultSymbols.put(postalAddress, "postalAddress"); + DefaultSymbols.put(postalCode, "postalCode"); + DefaultSymbols.put(postOfficeBox, "postOfficeBox"); + DefaultSymbols.put(preferredDeliveryMethod, "preferredDeliveryMethod"); + DefaultSymbols.put(registeredAddress, "registeredAddress"); + DefaultSymbols.put(roleOccupant, "roleOccupant"); + DefaultSymbols.put(searchGuide, "searchGuide"); + DefaultSymbols.put(seeAlso, "seeAlso"); + DefaultSymbols.put(serialNumber, "serialNumber"); + DefaultSymbols.put(sn, "sn"); + DefaultSymbols.put(st, "st"); + DefaultSymbols.put(street, "street"); + DefaultSymbols.put(telephoneNumber, "telephoneNumber"); + DefaultSymbols.put(teletexTerminalIdentifier, "teletexTerminalIdentifier"); + DefaultSymbols.put(telexNumber, "telexNumber"); + DefaultSymbols.put(title, "title"); + DefaultSymbols.put(uid, "uid"); + DefaultSymbols.put(uniqueMember, "uniqueMember"); + DefaultSymbols.put(userPassword, "userPassword"); + DefaultSymbols.put(x121Address, "x121Address"); + DefaultSymbols.put(x500UniqueIdentifier, "x500UniqueIdentifier"); + + DefaultLookUp.put("businesscategory", businessCategory); + DefaultLookUp.put("c", c); + DefaultLookUp.put("cn", cn); + DefaultLookUp.put("dc", dc); + DefaultLookUp.put("description", description); + DefaultLookUp.put("destinationindicator", destinationIndicator); + DefaultLookUp.put("distinguishedname", distinguishedName); + DefaultLookUp.put("dnqualifier", dnQualifier); + DefaultLookUp.put("enhancedsearchguide", enhancedSearchGuide); + DefaultLookUp.put("facsimiletelephonenumber", facsimileTelephoneNumber); + DefaultLookUp.put("generationqualifier", generationQualifier); + DefaultLookUp.put("givenname", givenName); + DefaultLookUp.put("houseidentifier", houseIdentifier); + DefaultLookUp.put("initials", initials); + DefaultLookUp.put("internationalisdnnumber", internationalISDNNumber); + DefaultLookUp.put("l", l); + DefaultLookUp.put("member", member); + DefaultLookUp.put("name", name); + DefaultLookUp.put("o", o); + DefaultLookUp.put("ou", ou); + DefaultLookUp.put("owner", owner); + DefaultLookUp.put("physicaldeliveryofficename", physicalDeliveryOfficeName); + DefaultLookUp.put("postaladdress", postalAddress); + DefaultLookUp.put("postalcode", postalCode); + DefaultLookUp.put("postofficebox", postOfficeBox); + DefaultLookUp.put("preferreddeliverymethod", preferredDeliveryMethod); + DefaultLookUp.put("registeredaddress", registeredAddress); + DefaultLookUp.put("roleoccupant", roleOccupant); + DefaultLookUp.put("searchguide", searchGuide); + DefaultLookUp.put("seealso", seeAlso); + DefaultLookUp.put("serialnumber", serialNumber); + DefaultLookUp.put("sn", sn); + DefaultLookUp.put("st", st); + DefaultLookUp.put("street", street); + DefaultLookUp.put("telephonenumber", telephoneNumber); + DefaultLookUp.put("teletexterminalidentifier", teletexTerminalIdentifier); + DefaultLookUp.put("telexnumber", telexNumber); + DefaultLookUp.put("title", title); + DefaultLookUp.put("uid", uid); + DefaultLookUp.put("uniquemember", uniqueMember); + DefaultLookUp.put("userpassword", userPassword); + DefaultLookUp.put("x121address", x121Address); + DefaultLookUp.put("x500uniqueidentifier", x500UniqueIdentifier); + + // TODO: need to add correct matching for equality comparisons. + } + + /** + * Singleton instance. + */ + public static final X500NameStyle INSTANCE = new RFC4519Style(); + + protected final Hashtable defaultLookUp; + protected final Hashtable defaultSymbols; + + protected RFC4519Style() + { + defaultSymbols = copyHashTable(DefaultSymbols); + defaultLookUp = copyHashTable(DefaultLookUp); + } + + public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value) + { + if (value.length() != 0 && value.charAt(0) == '#') + { + try + { + return IETFUtils.valueFromHexString(value, 1); + } + catch (IOException e) + { + throw new RuntimeException("can't recode value for oid " + oid.getId()); + } + } + else + { + if (value.length() != 0 && value.charAt(0) == '\\') + { + value = value.substring(1); + } + if (oid.equals(dc)) + { + return new DERIA5String(value); + } + else if (oid.equals(c) || oid.equals(serialNumber) || oid.equals(dnQualifier) + || oid.equals(telephoneNumber)) + { + return new DERPrintableString(value); + } + } + + return new DERUTF8String(value); + } + + public String oidToDisplayName(ASN1ObjectIdentifier oid) + { + return (String)DefaultSymbols.get(oid); + } + + public String[] oidToAttrNames(ASN1ObjectIdentifier oid) + { + return IETFUtils.findAttrNamesForOID(oid, defaultLookUp); + } + + public ASN1ObjectIdentifier attrNameToOID(String attrName) + { + return IETFUtils.decodeAttrName(attrName, defaultLookUp); + } + + public boolean areEqual(X500Name name1, X500Name name2) + { + RDN[] rdns1 = name1.getRDNs(); + RDN[] rdns2 = name2.getRDNs(); + + if (rdns1.length != rdns2.length) + { + return false; + } + + boolean reverse = false; + + if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null) + { + reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward + } + + for (int i = 0; i != rdns1.length; i++) + { + if (!foundMatch(reverse, rdns1[i], rdns2)) + { + return false; + } + } + + return true; + } + + private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs) + { + if (reverse) + { + for (int i = possRDNs.length - 1; i >= 0; i--) + { + if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) + { + possRDNs[i] = null; + return true; + } + } + } + else + { + for (int i = 0; i != possRDNs.length; i++) + { + if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) + { + possRDNs[i] = null; + return true; + } + } + } + + return false; + } + + protected boolean rdnAreEqual(RDN rdn1, RDN rdn2) + { + return IETFUtils.rDNAreEqual(rdn1, rdn2); + } + + // parse backwards + public RDN[] fromString(String dirName) + { + RDN[] tmp = IETFUtils.rDNsFromString(dirName, this); + RDN[] res = new RDN[tmp.length]; + + for (int i = 0; i != tmp.length; i++) + { + res[res.length - i - 1] = tmp[i]; + } + + return res; + } + + public int calculateHashCode(X500Name name) + { + int hashCodeValue = 0; + RDN[] rdns = name.getRDNs(); + + // this needs to be order independent, like equals + for (int i = 0; i != rdns.length; i++) + { + if (rdns[i].isMultiValued()) + { + AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues(); + + for (int j = 0; j != atv.length; j++) + { + hashCodeValue ^= atv[j].getType().hashCode(); + hashCodeValue ^= calcHashCode(atv[j].getValue()); + } + } + else + { + hashCodeValue ^= rdns[i].getFirst().getType().hashCode(); + hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue()); + } + } + + return hashCodeValue; + } + + private int calcHashCode(ASN1Encodable enc) + { + String value = IETFUtils.valueToString(enc); + + value = IETFUtils.canonicalize(value); + + return value.hashCode(); + } + + // convert in reverse + public String toString(X500Name name) + { + StringBuffer buf = new StringBuffer(); + boolean first = true; + + RDN[] rdns = name.getRDNs(); + + for (int i = rdns.length - 1; i >= 0; i--) + { + if (first) + { + first = false; + } + else + { + buf.append(','); + } + + IETFUtils.appendRDN(buf, rdns[i], defaultSymbols); + } + + return buf.toString(); + } + + private static Hashtable copyHashTable(Hashtable paramsMap) + { + Hashtable newTable = new Hashtable(); + + Enumeration keys = paramsMap.keys(); + while (keys.hasMoreElements()) + { + Object key = keys.nextElement(); + newTable.put(key, paramsMap.get(key)); + } + + return newTable; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java new file mode 100644 index 000000000..d880c63f5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java @@ -0,0 +1,90 @@ +package org.spongycastle.asn1.x500.style; + +/** + * class for breaking up an X500 Name into it's component tokens, ala + * java.util.StringTokenizer. We need this class as some of the + * lightweight Java environment don't support classes like + * StringTokenizer. + */ +class X500NameTokenizer +{ + private String value; + private int index; + private char separator; + private StringBuffer buf = new StringBuffer(); + + public X500NameTokenizer( + String oid) + { + this(oid, ','); + } + + public X500NameTokenizer( + String oid, + char separator) + { + this.value = oid; + this.index = -1; + this.separator = separator; + } + + public boolean hasMoreTokens() + { + return (index != value.length()); + } + + public String nextToken() + { + if (index == value.length()) + { + return null; + } + + int end = index + 1; + boolean quoted = false; + boolean escaped = false; + + buf.setLength(0); + + while (end != value.length()) + { + char c = value.charAt(end); + + if (c == '"') + { + if (!escaped) + { + quoted = !quoted; + } + buf.append(c); + escaped = false; + } + else + { + if (escaped || quoted) + { + buf.append(c); + escaped = false; + } + else if (c == '\\') + { + buf.append(c); + escaped = true; + } + else if (c == separator) + { + break; + } + else + { + buf.append(c); + } + } + end++; + } + + index = end; + + return buf.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java new file mode 100644 index 000000000..29a57949b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java @@ -0,0 +1,98 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * The AccessDescription object. + *
+ * AccessDescription ::= SEQUENCE { + * accessMethod OBJECT IDENTIFIER, + * accessLocation GeneralName } + *+ */ +public class AccessDescription + extends ASN1Object +{ + public final static ASN1ObjectIdentifier id_ad_caIssuers = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.2"); + + public final static ASN1ObjectIdentifier id_ad_ocsp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1"); + + ASN1ObjectIdentifier accessMethod = null; + GeneralName accessLocation = null; + + public static AccessDescription getInstance( + Object obj) + { + if (obj instanceof AccessDescription) + { + return (AccessDescription)obj; + } + else if (obj != null) + { + return new AccessDescription(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private AccessDescription( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("wrong number of elements in sequence"); + } + + accessMethod = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + accessLocation = GeneralName.getInstance(seq.getObjectAt(1)); + } + + /** + * create an AccessDescription with the oid and location provided. + */ + public AccessDescription( + ASN1ObjectIdentifier oid, + GeneralName location) + { + accessMethod = oid; + accessLocation = location; + } + + /** + * + * @return the access method. + */ + public ASN1ObjectIdentifier getAccessMethod() + { + return accessMethod; + } + + /** + * + * @return the access location + */ + public GeneralName getAccessLocation() + { + return accessLocation; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector accessDescription = new ASN1EncodableVector(); + + accessDescription.add(accessMethod); + accessDescription.add(accessLocation); + + return new DERSequence(accessDescription); + } + + public String toString() + { + return ("AccessDescription: Oid(" + this.accessMethod.getId() + ")"); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java new file mode 100644 index 000000000..0c07c49b9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java @@ -0,0 +1,173 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DERSequence; + +public class AlgorithmIdentifier + extends ASN1Object +{ + private ASN1ObjectIdentifier objectId; + private ASN1Encodable parameters; + private boolean parametersDefined = false; + + public static AlgorithmIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static AlgorithmIdentifier getInstance( + Object obj) + { + if (obj== null || obj instanceof AlgorithmIdentifier) + { + return (AlgorithmIdentifier)obj; + } + + // TODO: delete + if (obj instanceof ASN1ObjectIdentifier) + { + return new AlgorithmIdentifier((ASN1ObjectIdentifier)obj); + } + + // TODO: delete + if (obj instanceof String) + { + return new AlgorithmIdentifier((String)obj); + } + + return new AlgorithmIdentifier(ASN1Sequence.getInstance(obj)); + } + + public AlgorithmIdentifier( + ASN1ObjectIdentifier objectId) + { + this.objectId = objectId; + } + + /** + * @deprecated use ASN1ObjectIdentifier + * @param objectId + */ + public AlgorithmIdentifier( + String objectId) + { + this.objectId = new ASN1ObjectIdentifier(objectId); + } + + /** + * @deprecated use ASN1ObjectIdentifier + * @param objectId + */ + public AlgorithmIdentifier( + DERObjectIdentifier objectId) + { + this.objectId = new ASN1ObjectIdentifier(objectId.getId()); + } + + /** + * @deprecated use ASN1ObjectIdentifier + * @param objectId + * @param parameters + */ + public AlgorithmIdentifier( + DERObjectIdentifier objectId, + ASN1Encodable parameters) + { + parametersDefined = true; + this.objectId = new ASN1ObjectIdentifier(objectId.getId()); + this.parameters = parameters; + } + + public AlgorithmIdentifier( + ASN1ObjectIdentifier objectId, + ASN1Encodable parameters) + { + parametersDefined = true; + this.objectId = objectId; + this.parameters = parameters; + } + + /** + * @deprecated use AlgorithmIdentifier.getInstance() + * @param seq + */ + public AlgorithmIdentifier( + ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + objectId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + + if (seq.size() == 2) + { + parametersDefined = true; + parameters = seq.getObjectAt(1); + } + else + { + parameters = null; + } + } + + public ASN1ObjectIdentifier getAlgorithm() + { + return new ASN1ObjectIdentifier(objectId.getId()); + } + + /** + * @deprecated use getAlgorithm + * @return + */ + public ASN1ObjectIdentifier getObjectId() + { + return objectId; + } + + public ASN1Encodable getParameters() + { + return parameters; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(objectId); + + if (parametersDefined) + { + if (parameters != null) + { + v.add(parameters); + } + else + { + v.add(DERNull.INSTANCE); + } + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java new file mode 100644 index 000000000..cb027fd12 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java @@ -0,0 +1,91 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +public class AttCertIssuer + extends ASN1Object + implements ASN1Choice +{ + ASN1Encodable obj; + ASN1Primitive choiceObj; + + public static AttCertIssuer getInstance( + Object obj) + { + if (obj == null || obj instanceof AttCertIssuer) + { + return (AttCertIssuer)obj; + } + else if (obj instanceof V2Form) + { + return new AttCertIssuer(V2Form.getInstance(obj)); + } + else if (obj instanceof GeneralNames) + { + return new AttCertIssuer((GeneralNames)obj); + } + else if (obj instanceof ASN1TaggedObject) + { + return new AttCertIssuer(V2Form.getInstance((ASN1TaggedObject)obj, false)); + } + else if (obj instanceof ASN1Sequence) + { + return new AttCertIssuer(GeneralNames.getInstance(obj)); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public static AttCertIssuer getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + /** + * Don't use this one if you are trying to be RFC 3281 compliant. + * Use it for v1 attribute certificates only. + * + * @param names our GeneralNames structure + */ + public AttCertIssuer( + GeneralNames names) + { + obj = names; + choiceObj = obj.toASN1Primitive(); + } + + public AttCertIssuer( + V2Form v2Form) + { + obj = v2Form; + choiceObj = new DERTaggedObject(false, 0, obj); + } + + public ASN1Encodable getIssuer() + { + return obj; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * AttCertIssuer ::= CHOICE { + * v1Form GeneralNames, -- MUST NOT be used in this + * -- profile + * v2Form [0] V2Form -- v2 only + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return choiceObj; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java new file mode 100644 index 000000000..eec4d6340 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java @@ -0,0 +1,84 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class AttCertValidityPeriod + extends ASN1Object +{ + ASN1GeneralizedTime notBeforeTime; + ASN1GeneralizedTime notAfterTime; + + public static AttCertValidityPeriod getInstance( + Object obj) + { + if (obj instanceof AttCertValidityPeriod) + { + return (AttCertValidityPeriod)obj; + } + else if (obj != null) + { + return new AttCertValidityPeriod(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private AttCertValidityPeriod( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + notBeforeTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(0)); + notAfterTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(1)); + } + + /** + * @param notBeforeTime + * @param notAfterTime + */ + public AttCertValidityPeriod( + ASN1GeneralizedTime notBeforeTime, + ASN1GeneralizedTime notAfterTime) + { + this.notBeforeTime = notBeforeTime; + this.notAfterTime = notAfterTime; + } + + public ASN1GeneralizedTime getNotBeforeTime() + { + return notBeforeTime; + } + + public ASN1GeneralizedTime getNotAfterTime() + { + return notAfterTime; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * AttCertValidityPeriod ::= SEQUENCE { + * notBeforeTime GeneralizedTime, + * notAfterTime GeneralizedTime + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(notBeforeTime); + v.add(notAfterTime); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Attribute.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Attribute.java new file mode 100644 index 000000000..5a501e1f5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Attribute.java @@ -0,0 +1,93 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.DERSequence; + +public class Attribute + extends ASN1Object +{ + private ASN1ObjectIdentifier attrType; + private ASN1Set attrValues; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static Attribute getInstance( + Object o) + { + if (o instanceof Attribute) + { + return (Attribute)o; + } + + if (o != null) + { + return new Attribute(ASN1Sequence.getInstance(o)); + } + + return null; + } + + private Attribute( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + attrType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + attrValues = ASN1Set.getInstance(seq.getObjectAt(1)); + } + + public Attribute( + ASN1ObjectIdentifier attrType, + ASN1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public ASN1ObjectIdentifier getAttrType() + { + return new ASN1ObjectIdentifier(attrType.getId()); + } + + public ASN1Encodable[] getAttributeValues() + { + return attrValues.toArray(); + } + + public ASN1Set getAttrValues() + { + return attrValues; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Attribute ::= SEQUENCE { + * attrType OBJECT IDENTIFIER, + * attrValues SET OF AttributeValue + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(attrType); + v.add(attrValues); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java new file mode 100644 index 000000000..7af85919b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java @@ -0,0 +1,97 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; + +public class AttributeCertificate + extends ASN1Object +{ + AttributeCertificateInfo acinfo; + AlgorithmIdentifier signatureAlgorithm; + DERBitString signatureValue; + + /** + * @param obj + * @return an AttributeCertificate object + */ + public static AttributeCertificate getInstance(Object obj) + { + if (obj instanceof AttributeCertificate) + { + return (AttributeCertificate)obj; + } + else if (obj != null) + { + return new AttributeCertificate(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public AttributeCertificate( + AttributeCertificateInfo acinfo, + AlgorithmIdentifier signatureAlgorithm, + DERBitString signatureValue) + { + this.acinfo = acinfo; + this.signatureAlgorithm = signatureAlgorithm; + this.signatureValue = signatureValue; + } + + /** + * @deprecated use getInstance() method. + */ + public AttributeCertificate( + ASN1Sequence seq) + { + if (seq.size() != 3) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + this.acinfo = AttributeCertificateInfo.getInstance(seq.getObjectAt(0)); + this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + this.signatureValue = DERBitString.getInstance(seq.getObjectAt(2)); + } + + public AttributeCertificateInfo getAcinfo() + { + return acinfo; + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return signatureAlgorithm; + } + + public DERBitString getSignatureValue() + { + return signatureValue; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * AttributeCertificate ::= SEQUENCE { + * acinfo AttributeCertificateInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(acinfo); + v.add(signatureAlgorithm); + v.add(signatureValue); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java new file mode 100644 index 000000000..33e3e86ff --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java @@ -0,0 +1,180 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; + +public class AttributeCertificateInfo + extends ASN1Object +{ + private ASN1Integer version; + private Holder holder; + private AttCertIssuer issuer; + private AlgorithmIdentifier signature; + private ASN1Integer serialNumber; + private AttCertValidityPeriod attrCertValidityPeriod; + private ASN1Sequence attributes; + private DERBitString issuerUniqueID; + private Extensions extensions; + + public static AttributeCertificateInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static AttributeCertificateInfo getInstance( + Object obj) + { + if (obj instanceof AttributeCertificateInfo) + { + return (AttributeCertificateInfo)obj; + } + else if (obj != null) + { + return new AttributeCertificateInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private AttributeCertificateInfo( + ASN1Sequence seq) + { + if (seq.size() < 6 || seq.size() > 9) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + int start; + if (seq.getObjectAt(0) instanceof ASN1Integer) // in version 1 certs version is DEFAULT v1(0) + { + this.version = ASN1Integer.getInstance(seq.getObjectAt(0)); + start = 1; + } + else + { + this.version = new ASN1Integer(0); + start = 0; + } + + this.holder = Holder.getInstance(seq.getObjectAt(start)); + this.issuer = AttCertIssuer.getInstance(seq.getObjectAt(start + 1)); + this.signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(start + 2)); + this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(start + 3)); + this.attrCertValidityPeriod = AttCertValidityPeriod.getInstance(seq.getObjectAt(start + 4)); + this.attributes = ASN1Sequence.getInstance(seq.getObjectAt(start + 5)); + + for (int i = start + 6; i < seq.size(); i++) + { + ASN1Encodable obj = seq.getObjectAt(i); + + if (obj instanceof DERBitString) + { + this.issuerUniqueID = DERBitString.getInstance(seq.getObjectAt(i)); + } + else if (obj instanceof ASN1Sequence || obj instanceof Extensions) + { + this.extensions = Extensions.getInstance(seq.getObjectAt(i)); + } + } + } + + public ASN1Integer getVersion() + { + return version; + } + + public Holder getHolder() + { + return holder; + } + + public AttCertIssuer getIssuer() + { + return issuer; + } + + public AlgorithmIdentifier getSignature() + { + return signature; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + public AttCertValidityPeriod getAttrCertValidityPeriod() + { + return attrCertValidityPeriod; + } + + public ASN1Sequence getAttributes() + { + return attributes; + } + + public DERBitString getIssuerUniqueID() + { + return issuerUniqueID; + } + + public Extensions getExtensions() + { + return extensions; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * AttributeCertificateInfo ::= SEQUENCE { + * version AttCertVersion -- version is v2, + * holder Holder, + * issuer AttCertIssuer, + * signature AlgorithmIdentifier, + * serialNumber CertificateSerialNumber, + * attrCertValidityPeriod AttCertValidityPeriod, + * attributes SEQUENCE OF Attribute, + * issuerUniqueID UniqueIdentifier OPTIONAL, + * extensions Extensions OPTIONAL + * } + * + * AttCertVersion ::= INTEGER { v2(1) } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version.getValue().intValue() != 0) + { + v.add(version); + } + v.add(holder); + v.add(issuer); + v.add(signature); + v.add(serialNumber); + v.add(attrCertValidityPeriod); + v.add(attributes); + + if (issuerUniqueID != null) + { + v.add(issuerUniqueID); + } + + if (extensions != null) + { + v.add(extensions); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java new file mode 100644 index 000000000..a6c8ae7ab --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java @@ -0,0 +1,101 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * The AuthorityInformationAccess object. + *
+ * id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } + * + * AuthorityInfoAccessSyntax ::= + * SEQUENCE SIZE (1..MAX) OF AccessDescription + * AccessDescription ::= SEQUENCE { + * accessMethod OBJECT IDENTIFIER, + * accessLocation GeneralName } + * + * id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } + * id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } + * id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } + *+ */ +public class AuthorityInformationAccess + extends ASN1Object +{ + private AccessDescription[] descriptions; + + public static AuthorityInformationAccess getInstance( + Object obj) + { + if (obj instanceof AuthorityInformationAccess) + { + return (AuthorityInformationAccess)obj; + } + + if (obj != null) + { + return new AuthorityInformationAccess(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private AuthorityInformationAccess( + ASN1Sequence seq) + { + if (seq.size() < 1) + { + throw new IllegalArgumentException("sequence may not be empty"); + } + + descriptions = new AccessDescription[seq.size()]; + + for (int i = 0; i != seq.size(); i++) + { + descriptions[i] = AccessDescription.getInstance(seq.getObjectAt(i)); + } + } + + /** + * create an AuthorityInformationAccess with the oid and location provided. + */ + public AuthorityInformationAccess( + ASN1ObjectIdentifier oid, + GeneralName location) + { + descriptions = new AccessDescription[1]; + + descriptions[0] = new AccessDescription(oid, location); + } + + + /** + * + * @return the access descriptions contained in this object. + */ + public AccessDescription[] getAccessDescriptions() + { + return descriptions; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + + for (int i = 0; i != descriptions.length; i++) + { + vec.add(descriptions[i]); + } + + return new DERSequence(vec); + } + + public String toString() + { + return ("AuthorityInformationAccess: Oid(" + this.descriptions[0].getAccessMethod().getId() + ")"); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java new file mode 100644 index 000000000..10401fcb8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java @@ -0,0 +1,232 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; + +/** + * The AuthorityKeyIdentifier object. + *
+ * id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } + * + * AuthorityKeyIdentifier ::= SEQUENCE { + * keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL, + * authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL, + * authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL } + * + * KeyIdentifier ::= OCTET STRING + *+ * + */ +public class AuthorityKeyIdentifier + extends ASN1Object +{ + ASN1OctetString keyidentifier=null; + GeneralNames certissuer=null; + ASN1Integer certserno=null; + + public static AuthorityKeyIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static AuthorityKeyIdentifier getInstance( + Object obj) + { + if (obj instanceof AuthorityKeyIdentifier) + { + return (AuthorityKeyIdentifier)obj; + } + if (obj != null) + { + return new AuthorityKeyIdentifier(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static AuthorityKeyIdentifier fromExtensions(Extensions extensions) + { + return AuthorityKeyIdentifier.getInstance(extensions.getExtensionParsedValue(Extension.authorityKeyIdentifier)); + } + + protected AuthorityKeyIdentifier( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1TaggedObject o = DERTaggedObject.getInstance(e.nextElement()); + + switch (o.getTagNo()) + { + case 0: + this.keyidentifier = ASN1OctetString.getInstance(o, false); + break; + case 1: + this.certissuer = GeneralNames.getInstance(o, false); + break; + case 2: + this.certserno = ASN1Integer.getInstance(o, false); + break; + default: + throw new IllegalArgumentException("illegal tag"); + } + } + } + + /** + * + * Calulates the keyidentifier using a SHA1 hash over the BIT STRING + * from SubjectPublicKeyInfo as defined in RFC2459. + * + * Example of making a AuthorityKeyIdentifier: + *
+ * SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream( + * publicKey.getEncoded()).readObject()); + * AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki); + *+ * + **/ + public AuthorityKeyIdentifier( + SubjectPublicKeyInfo spki) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + this.keyidentifier = new DEROctetString(resBuf); + } + + /** + * create an AuthorityKeyIdentifier with the GeneralNames tag and + * the serial number provided as well. + */ + public AuthorityKeyIdentifier( + SubjectPublicKeyInfo spki, + GeneralNames name, + BigInteger serialNumber) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + + this.keyidentifier = new DEROctetString(resBuf); + this.certissuer = GeneralNames.getInstance(name.toASN1Primitive()); + this.certserno = new ASN1Integer(serialNumber); + } + + /** + * create an AuthorityKeyIdentifier with the GeneralNames tag and + * the serial number provided. + */ + public AuthorityKeyIdentifier( + GeneralNames name, + BigInteger serialNumber) + { + this.keyidentifier = null; + this.certissuer = GeneralNames.getInstance(name.toASN1Primitive()); + this.certserno = new ASN1Integer(serialNumber); + } + + /** + * create an AuthorityKeyIdentifier with a precomputed key identifier + */ + public AuthorityKeyIdentifier( + byte[] keyIdentifier) + { + this.keyidentifier = new DEROctetString(keyIdentifier); + this.certissuer = null; + this.certserno = null; + } + + /** + * create an AuthorityKeyIdentifier with a precomputed key identifier + * and the GeneralNames tag and the serial number provided as well. + */ + public AuthorityKeyIdentifier( + byte[] keyIdentifier, + GeneralNames name, + BigInteger serialNumber) + { + this.keyidentifier = new DEROctetString(keyIdentifier); + this.certissuer = GeneralNames.getInstance(name.toASN1Primitive()); + this.certserno = new ASN1Integer(serialNumber); + } + + public byte[] getKeyIdentifier() + { + if (keyidentifier != null) + { + return keyidentifier.getOctets(); + } + + return null; + } + + public GeneralNames getAuthorityCertIssuer() + { + return certissuer; + } + + public BigInteger getAuthorityCertSerialNumber() + { + if (certserno != null) + { + return certserno.getValue(); + } + + return null; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (keyidentifier != null) + { + v.add(new DERTaggedObject(false, 0, keyidentifier)); + } + + if (certissuer != null) + { + v.add(new DERTaggedObject(false, 1, certissuer)); + } + + if (certserno != null) + { + v.add(new DERTaggedObject(false, 2, certserno)); + } + + + return new DERSequence(v); + } + + public String toString() + { + return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")"); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java new file mode 100644 index 000000000..f7ad39be4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java @@ -0,0 +1,164 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBoolean; +import org.spongycastle.asn1.DERSequence; + +public class BasicConstraints + extends ASN1Object +{ + ASN1Boolean cA = ASN1Boolean.getInstance(false); + ASN1Integer pathLenConstraint = null; + + public static BasicConstraints getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static BasicConstraints getInstance( + Object obj) + { + if (obj instanceof BasicConstraints) + { + return (BasicConstraints)obj; + } + if (obj instanceof X509Extension) + { + return getInstance(X509Extension.convertValueToObject((X509Extension)obj)); + } + if (obj != null) + { + return new BasicConstraints(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static BasicConstraints fromExtensions(Extensions extensions) + { + return BasicConstraints.getInstance(extensions.getExtensionParsedValue(Extension.basicConstraints)); + } + + private BasicConstraints( + ASN1Sequence seq) + { + if (seq.size() == 0) + { + this.cA = null; + this.pathLenConstraint = null; + } + else + { + if (seq.getObjectAt(0) instanceof DERBoolean) + { + this.cA = DERBoolean.getInstance(seq.getObjectAt(0)); + } + else + { + this.cA = null; + this.pathLenConstraint = ASN1Integer.getInstance(seq.getObjectAt(0)); + } + if (seq.size() > 1) + { + if (this.cA != null) + { + this.pathLenConstraint = ASN1Integer.getInstance(seq.getObjectAt(1)); + } + else + { + throw new IllegalArgumentException("wrong sequence in constructor"); + } + } + } + } + + public BasicConstraints( + boolean cA) + { + if (cA) + { + this.cA = ASN1Boolean.getInstance(true); + } + else + { + this.cA = null; + } + this.pathLenConstraint = null; + } + + /** + * create a cA=true object for the given path length constraint. + * + * @param pathLenConstraint + */ + public BasicConstraints( + int pathLenConstraint) + { + this.cA = ASN1Boolean.getInstance(true); + this.pathLenConstraint = new ASN1Integer(pathLenConstraint); + } + + public boolean isCA() + { + return (cA != null) && cA.isTrue(); + } + + public BigInteger getPathLenConstraint() + { + if (pathLenConstraint != null) + { + return pathLenConstraint.getValue(); + } + + return null; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * BasicConstraints := SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (cA != null) + { + v.add(cA); + } + + if (pathLenConstraint != null) // yes some people actually do this when cA is false... + { + v.add(pathLenConstraint); + } + + return new DERSequence(v); + } + + public String toString() + { + if (pathLenConstraint == null) + { + if (cA == null) + { + return "BasicConstraints: isCa(false)"; + } + return "BasicConstraints: isCa(" + this.isCA() + ")"; + } + return "BasicConstraints: isCa(" + this.isCA() + "), pathLenConstraint = " + pathLenConstraint.getValue(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java new file mode 100644 index 000000000..739e98b5d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java @@ -0,0 +1,100 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class CRLDistPoint + extends ASN1Object +{ + ASN1Sequence seq = null; + + public static CRLDistPoint getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static CRLDistPoint getInstance( + Object obj) + { + if (obj instanceof CRLDistPoint) + { + return (CRLDistPoint)obj; + } + else if (obj != null) + { + return new CRLDistPoint(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CRLDistPoint( + ASN1Sequence seq) + { + this.seq = seq; + } + + public CRLDistPoint( + DistributionPoint[] points) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != points.length; i++) + { + v.add(points[i]); + } + + seq = new DERSequence(v); + } + + /** + * Return the distribution points making up the sequence. + * + * @return DistributionPoint[] + */ + public DistributionPoint[] getDistributionPoints() + { + DistributionPoint[] dp = new DistributionPoint[seq.size()]; + + for (int i = 0; i != seq.size(); i++) + { + dp[i] = DistributionPoint.getInstance(seq.getObjectAt(i)); + } + + return dp; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * CRLDistPoint ::= SEQUENCE SIZE {1..MAX} OF DistributionPoint + *+ */ + public ASN1Primitive toASN1Primitive() + { + return seq; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String sep = System.getProperty("line.separator"); + + buf.append("CRLDistPoint:"); + buf.append(sep); + DistributionPoint dp[] = getDistributionPoints(); + for (int i = 0; i != dp.length; i++) + { + buf.append(" "); + buf.append(dp[i]); + buf.append(sep); + } + return buf.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java new file mode 100644 index 000000000..7699fde5b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java @@ -0,0 +1,54 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; + +/** + * The CRLNumber object. + *
+ * CRLNumber::= INTEGER(0..MAX) + *+ */ +public class CRLNumber + extends ASN1Object +{ + private BigInteger number; + + public CRLNumber( + BigInteger number) + { + this.number = number; + } + + public BigInteger getCRLNumber() + { + return number; + } + + public String toString() + { + return "CRLNumber: " + getCRLNumber(); + } + + public ASN1Primitive toASN1Primitive() + { + return new ASN1Integer(number); + } + + public static CRLNumber getInstance(Object o) + { + if (o instanceof CRLNumber) + { + return (CRLNumber)o; + } + else if (o != null) + { + return new CRLNumber(ASN1Integer.getInstance(o).getValue()); + } + + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java new file mode 100644 index 000000000..c1071699f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java @@ -0,0 +1,151 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1Enumerated; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.util.Integers; + +/** + * The CRLReason enumeration. + *
+ * CRLReason ::= ENUMERATED { + * unspecified (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6), + * removeFromCRL (8), + * privilegeWithdrawn (9), + * aACompromise (10) + * } + *+ */ +public class CRLReason + extends ASN1Object +{ + /** + * @deprecated use lower case version + */ + public static final int UNSPECIFIED = 0; + /** + * @deprecated use lower case version + */ + public static final int KEY_COMPROMISE = 1; + /** + * @deprecated use lower case version + */ + public static final int CA_COMPROMISE = 2; + /** + * @deprecated use lower case version + */ + public static final int AFFILIATION_CHANGED = 3; + /** + * @deprecated use lower case version + */ + public static final int SUPERSEDED = 4; + /** + * @deprecated use lower case version + */ + public static final int CESSATION_OF_OPERATION = 5; + /** + * @deprecated use lower case version + */ + public static final int CERTIFICATE_HOLD = 6; + /** + * @deprecated use lower case version + */ + public static final int REMOVE_FROM_CRL = 8; + /** + * @deprecated use lower case version + */ + public static final int PRIVILEGE_WITHDRAWN = 9; + /** + * @deprecated use lower case version + */ + public static final int AA_COMPROMISE = 10; + + public static final int unspecified = 0; + public static final int keyCompromise = 1; + public static final int cACompromise = 2; + public static final int affiliationChanged = 3; + public static final int superseded = 4; + public static final int cessationOfOperation = 5; + public static final int certificateHold = 6; + // 7 -> unknown + public static final int removeFromCRL = 8; + public static final int privilegeWithdrawn = 9; + public static final int aACompromise = 10; + + private static final String[] reasonString = + { + "unspecified", "keyCompromise", "cACompromise", "affiliationChanged", + "superseded", "cessationOfOperation", "certificateHold", "unknown", + "removeFromCRL", "privilegeWithdrawn", "aACompromise" + }; + + private static final Hashtable table = new Hashtable(); + + private ASN1Enumerated value; + + public static CRLReason getInstance(Object o) + { + if (o instanceof CRLReason) + { + return (CRLReason)o; + } + else if (o != null) + { + return lookup(ASN1Enumerated.getInstance(o).getValue().intValue()); + } + + return null; + } + + private CRLReason( + int reason) + { + value = new ASN1Enumerated(reason); + } + + public String toString() + { + String str; + int reason = getValue().intValue(); + if (reason < 0 || reason > 10) + { + str = "invalid"; + } + else + { + str = reasonString[reason]; + } + return "CRLReason: " + str; + } + + public BigInteger getValue() + { + return value.getValue(); + } + + public ASN1Primitive toASN1Primitive() + { + return value; + } + + public static CRLReason lookup(int value) + { + Integer idx = Integers.valueOf(value); + + if (!table.containsKey(idx)) + { + table.put(idx, new CRLReason(value)); + } + + return (CRLReason)table.get(idx); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java new file mode 100644 index 000000000..142b83e70 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java @@ -0,0 +1,57 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; + + +/** + * CertPolicyId, used in the CertificatePolicies and PolicyMappings + * X509V3 Extensions. + * + *
+ * CertPolicyId ::= OBJECT IDENTIFIER + *+ */ +/** + * CertPolicyId, used in the CertificatePolicies and PolicyMappings + * X509V3 Extensions. + * + *
+ * CertPolicyId ::= OBJECT IDENTIFIER + *+ */ +public class CertPolicyId + extends ASN1Object +{ + private ASN1ObjectIdentifier id; + + private CertPolicyId(ASN1ObjectIdentifier id) + { + this.id = id; + } + + public static CertPolicyId getInstance(Object o) + { + if (o instanceof CertPolicyId) + { + return (CertPolicyId)o; + } + else if (o != null) + { + return new CertPolicyId(ASN1ObjectIdentifier.getInstance(o)); + } + + return null; + } + + public String getId() + { + return id.getId(); + } + + public ASN1Primitive toASN1Primitive() + { + return id; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Certificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Certificate.java new file mode 100644 index 000000000..8d1b69f29 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Certificate.java @@ -0,0 +1,131 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.x500.X500Name; + +/** + * an X509Certificate structure. + *
+ * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + *+ */ +public class Certificate + extends ASN1Object +{ + ASN1Sequence seq; + TBSCertificate tbsCert; + AlgorithmIdentifier sigAlgId; + DERBitString sig; + + public static Certificate getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static Certificate getInstance( + Object obj) + { + if (obj instanceof Certificate) + { + return (Certificate)obj; + } + else if (obj != null) + { + return new Certificate(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private Certificate( + ASN1Sequence seq) + { + this.seq = seq; + + // + // correct x509 certficate + // + if (seq.size() == 3) + { + tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0)); + sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + + sig = DERBitString.getInstance(seq.getObjectAt(2)); + } + else + { + throw new IllegalArgumentException("sequence wrong size for a certificate"); + } + } + + public TBSCertificate getTBSCertificate() + { + return tbsCert; + } + + public ASN1Integer getVersion() + { + return tbsCert.getVersion(); + } + + public int getVersionNumber() + { + return tbsCert.getVersionNumber(); + } + + public ASN1Integer getSerialNumber() + { + return tbsCert.getSerialNumber(); + } + + public X500Name getIssuer() + { + return tbsCert.getIssuer(); + } + + public Time getStartDate() + { + return tbsCert.getStartDate(); + } + + public Time getEndDate() + { + return tbsCert.getEndDate(); + } + + public X500Name getSubject() + { + return tbsCert.getSubject(); + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return tbsCert.getSubjectPublicKeyInfo(); + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return sigAlgId; + } + + public DERBitString getSignature() + { + return sig; + } + + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java new file mode 100644 index 000000000..5d0681983 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java @@ -0,0 +1,144 @@ + +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.X500Name; + +/** + * PKIX RFC-2459 + * + * The X.509 v2 CRL syntax is as follows. For signature calculation, + * the data that is to be signed is ASN.1 DER encoded. + * + *
+ * CertificateList ::= SEQUENCE { + * tbsCertList TBSCertList, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + *+ */ +public class CertificateList + extends ASN1Object +{ + TBSCertList tbsCertList; + AlgorithmIdentifier sigAlgId; + DERBitString sig; + boolean isHashCodeSet = false; + int hashCodeValue; + + public static CertificateList getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static CertificateList getInstance( + Object obj) + { + if (obj instanceof CertificateList) + { + return (CertificateList)obj; + } + else if (obj != null) + { + return new CertificateList(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * @deprecated use getInstance() method. + * @param seq + */ + public CertificateList( + ASN1Sequence seq) + { + if (seq.size() == 3) + { + tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0)); + sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + sig = DERBitString.getInstance(seq.getObjectAt(2)); + } + else + { + throw new IllegalArgumentException("sequence wrong size for CertificateList"); + } + } + + public TBSCertList getTBSCertList() + { + return tbsCertList; + } + + public TBSCertList.CRLEntry[] getRevokedCertificates() + { + return tbsCertList.getRevokedCertificates(); + } + + public Enumeration getRevokedCertificateEnumeration() + { + return tbsCertList.getRevokedCertificateEnumeration(); + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return sigAlgId; + } + + public DERBitString getSignature() + { + return sig; + } + + public int getVersionNumber() + { + return tbsCertList.getVersionNumber(); + } + + public X500Name getIssuer() + { + return tbsCertList.getIssuer(); + } + + public Time getThisUpdate() + { + return tbsCertList.getThisUpdate(); + } + + public Time getNextUpdate() + { + return tbsCertList.getNextUpdate(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(tbsCertList); + v.add(sigAlgId); + v.add(sig); + + return new DERSequence(v); + } + + public int hashCode() + { + if (!isHashCodeSet) + { + hashCodeValue = super.hashCode(); + isHashCodeSet = true; + } + + return hashCodeValue; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java new file mode 100644 index 000000000..91465bb5d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java @@ -0,0 +1,169 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * This class helps to support crossCerfificatePairs in a LDAP directory + * according RFC 2587 + * + *
+ * crossCertificatePairATTRIBUTE::={ + * WITH SYNTAX CertificatePair + * EQUALITY MATCHING RULE certificatePairExactMatch + * ID joint-iso-ccitt(2) ds(5) attributeType(4) crossCertificatePair(40)} + *+ * + *
The forward elements of the crossCertificatePair attribute of a + * CA's directory entry shall be used to store all, except self-issued + * certificates issued to this CA. Optionally, the reverse elements of the + * crossCertificatePair attribute, of a CA's directory entry may contain a + * subset of certificates issued by this CA to other CAs. When both the forward + * and the reverse elements are present in a single attribute value, issuer name + * in one certificate shall match the subject name in the other and vice versa, + * and the subject public key in one certificate shall be capable of verifying + * the digital signature on the other certificate and vice versa. + * + * When a reverse element is present, the forward element value and the reverse + * element value need not be stored in the same attribute value; in other words, + * they can be stored in either a single attribute value or two attribute + * values.+ * + *
+ * CertificatePair ::= SEQUENCE { + * forward [0] Certificate OPTIONAL, + * reverse [1] Certificate OPTIONAL, + * -- at least one of the pair shall be present -- } + *+ */ +public class CertificatePair + extends ASN1Object +{ + private Certificate forward; + + private Certificate reverse; + + public static CertificatePair getInstance(Object obj) + { + if (obj == null || obj instanceof CertificatePair) + { + return (CertificatePair)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new CertificatePair((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type CertificatePair: + * + *
+ * CertificatePair ::= SEQUENCE { + * forward [0] Certificate OPTIONAL, + * reverse [1] Certificate OPTIONAL, + * -- at least one of the pair shall be present -- } + *+ * + * @param seq The ASN.1 sequence. + */ + private CertificatePair(ASN1Sequence seq) + { + if (seq.size() != 1 && seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement()); + if (o.getTagNo() == 0) + { + forward = Certificate.getInstance(o, true); + } + else if (o.getTagNo() == 1) + { + reverse = Certificate.getInstance(o, true); + } + else + { + throw new IllegalArgumentException("Bad tag number: " + + o.getTagNo()); + } + } + } + + /** + * Constructor from a given details. + * + * @param forward Certificates issued to this CA. + * @param reverse Certificates issued by this CA to other CAs. + */ + public CertificatePair(Certificate forward, Certificate reverse) + { + this.forward = forward; + this.reverse = reverse; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * CertificatePair ::= SEQUENCE { + * forward [0] Certificate OPTIONAL, + * reverse [1] Certificate OPTIONAL, + * -- at least one of the pair shall be present -- } + *+ * + * @return a ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + + if (forward != null) + { + vec.add(new DERTaggedObject(0, forward)); + } + if (reverse != null) + { + vec.add(new DERTaggedObject(1, reverse)); + } + + return new DERSequence(vec); + } + + /** + * @return Returns the forward. + */ + public Certificate getForward() + { + return forward; + } + + /** + * @return Returns the reverse. + */ + public Certificate getReverse() + { + return reverse; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java new file mode 100644 index 000000000..d8710ae7a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java @@ -0,0 +1,124 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class CertificatePolicies + extends ASN1Object +{ + private final PolicyInformation[] policyInformation; + + public static CertificatePolicies getInstance( + Object obj) + { + if (obj instanceof CertificatePolicies) + { + return (CertificatePolicies)obj; + } + + if (obj != null) + { + return new CertificatePolicies(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static CertificatePolicies getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Retrieve a CertificatePolicies for a passed in Extensions object, if present. + * + * @param extensions the extensions object to be examined. + * @return the CertificatePolicies, null if the extension is not present. + */ + public static CertificatePolicies fromExtensions(Extensions extensions) + { + return CertificatePolicies.getInstance(extensions.getExtensionParsedValue(Extension.certificatePolicies)); + } + + /** + * Construct a CertificatePolicies object containing one PolicyInformation. + * + * @param name the name to be contained. + */ + public CertificatePolicies( + PolicyInformation name) + { + this.policyInformation = new PolicyInformation[] { name }; + } + + public CertificatePolicies( + PolicyInformation[] policyInformation) + { + this.policyInformation = policyInformation; + } + + private CertificatePolicies( + ASN1Sequence seq) + { + this.policyInformation = new PolicyInformation[seq.size()]; + + for (int i = 0; i != seq.size(); i++) + { + policyInformation[i] = PolicyInformation.getInstance(seq.getObjectAt(i)); + } + } + + public PolicyInformation[] getPolicyInformation() + { + PolicyInformation[] tmp = new PolicyInformation[policyInformation.length]; + + System.arraycopy(policyInformation, 0, tmp, 0, policyInformation.length); + + return tmp; + } + + public PolicyInformation getPolicyInformation(ASN1ObjectIdentifier policyIdentifier) + { + for (int i = 0; i != policyInformation.length; i++) + { + if (policyIdentifier.equals(policyInformation[i].getPolicyIdentifier())) + { + return policyInformation[i]; + } + } + + return null; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * CertificatePolicies ::= SEQUENCE SIZE {1..MAX} OF PolicyInformation + *+ */ + public ASN1Primitive toASN1Primitive() + { + return new DERSequence(policyInformation); + } + + public String toString() + { + String p = null; + for (int i = 0; i < policyInformation.length; i++) + { + if (p != null) + { + p += ", "; + } + p += policyInformation[i]; + } + + return "CertificatePolicies: " + p; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java new file mode 100644 index 000000000..1b6b24059 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java @@ -0,0 +1,92 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class DSAParameter + extends ASN1Object +{ + ASN1Integer p, q, g; + + public static DSAParameter getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DSAParameter getInstance( + Object obj) + { + if (obj instanceof DSAParameter) + { + return (DSAParameter)obj; + } + + if(obj != null) + { + return new DSAParameter(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public DSAParameter( + BigInteger p, + BigInteger q, + BigInteger g) + { + this.p = new ASN1Integer(p); + this.q = new ASN1Integer(q); + this.g = new ASN1Integer(g); + } + + private DSAParameter( + ASN1Sequence seq) + { + if (seq.size() != 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + Enumeration e = seq.getObjects(); + + p = ASN1Integer.getInstance(e.nextElement()); + q = ASN1Integer.getInstance(e.nextElement()); + g = ASN1Integer.getInstance(e.nextElement()); + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getQ() + { + return q.getPositiveValue(); + } + + public BigInteger getG() + { + return g.getPositiveValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(p); + v.add(q); + v.add(g); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java new file mode 100644 index 000000000..b5f5cf61d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java @@ -0,0 +1,86 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +/** + * The DigestInfo object. + *
+ * DigestInfo::=SEQUENCE{ + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING } + *+ */ +public class DigestInfo + extends ASN1Object +{ + private byte[] digest; + private AlgorithmIdentifier algId; + + public static DigestInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DigestInfo getInstance( + Object obj) + { + if (obj instanceof DigestInfo) + { + return (DigestInfo)obj; + } + else if (obj != null) + { + return new DigestInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public DigestInfo( + AlgorithmIdentifier algId, + byte[] digest) + { + this.digest = digest; + this.algId = algId; + } + + public DigestInfo( + ASN1Sequence obj) + { + Enumeration e = obj.getObjects(); + + algId = AlgorithmIdentifier.getInstance(e.nextElement()); + digest = ASN1OctetString.getInstance(e.nextElement()).getOctets(); + } + + public AlgorithmIdentifier getAlgorithmId() + { + return algId; + } + + public byte[] getDigest() + { + return digest; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(algId); + v.add(new DEROctetString(digest)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java new file mode 100644 index 000000000..ad273cf84 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java @@ -0,0 +1,165 @@ + +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBMPString; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERUTF8String; +import org.spongycastle.asn1.DERVisibleString; + +/** + *
DisplayText
class, used in
+ * CertificatePolicies
X509 V3 extensions (in policy qualifiers).
+ *
+ * It stores a string in a chosen encoding. + *
+ * DisplayText ::= CHOICE { + * ia5String IA5String (SIZE (1..200)), + * visibleString VisibleString (SIZE (1..200)), + * bmpString BMPString (SIZE (1..200)), + * utf8String UTF8String (SIZE (1..200)) } + *+ * @see PolicyQualifierInfo + * @see PolicyInformation + */ +public class DisplayText + extends ASN1Object + implements ASN1Choice +{ + /** + * Constant corresponding to ia5String encoding. + * + */ + public static final int CONTENT_TYPE_IA5STRING = 0; + /** + * Constant corresponding to bmpString encoding. + * + */ + public static final int CONTENT_TYPE_BMPSTRING = 1; + /** + * Constant corresponding to utf8String encoding. + * + */ + public static final int CONTENT_TYPE_UTF8STRING = 2; + /** + * Constant corresponding to visibleString encoding. + * + */ + public static final int CONTENT_TYPE_VISIBLESTRING = 3; + + /** + * Describe constant
DISPLAY_TEXT_MAXIMUM_SIZE
here.
+ *
+ */
+ public static final int DISPLAY_TEXT_MAXIMUM_SIZE = 200;
+
+ int contentType;
+ ASN1String contents;
+
+ /**
+ * Creates a new DisplayText
instance.
+ *
+ * @param type the desired encoding type for the text.
+ * @param text the text to store. Strings longer than 200
+ * characters are truncated.
+ */
+ public DisplayText(int type, String text)
+ {
+ if (text.length() > DISPLAY_TEXT_MAXIMUM_SIZE)
+ {
+ // RFC3280 limits these strings to 200 chars
+ // truncate the string
+ text = text.substring (0, DISPLAY_TEXT_MAXIMUM_SIZE);
+ }
+
+ contentType = type;
+ switch (type)
+ {
+ case CONTENT_TYPE_IA5STRING:
+ contents = new DERIA5String(text);
+ break;
+ case CONTENT_TYPE_UTF8STRING:
+ contents = new DERUTF8String(text);
+ break;
+ case CONTENT_TYPE_VISIBLESTRING:
+ contents = new DERVisibleString(text);
+ break;
+ case CONTENT_TYPE_BMPSTRING:
+ contents = new DERBMPString(text);
+ break;
+ default:
+ contents = new DERUTF8String(text);
+ break;
+ }
+ }
+
+ /**
+ * Creates a new DisplayText
instance.
+ *
+ * @param text the text to encapsulate. Strings longer than 200
+ * characters are truncated.
+ */
+ public DisplayText(String text)
+ {
+ // by default use UTF8String
+ if (text.length() > DISPLAY_TEXT_MAXIMUM_SIZE)
+ {
+ text = text.substring(0, DISPLAY_TEXT_MAXIMUM_SIZE);
+ }
+
+ contentType = CONTENT_TYPE_UTF8STRING;
+ contents = new DERUTF8String(text);
+ }
+
+ /**
+ * Creates a new DisplayText
instance.
+ * Useful when reading back a DisplayText
class
+ * from it's ASN1Encodable/DEREncodable form.
+ *
+ * @param de a DEREncodable
instance.
+ */
+ private DisplayText(ASN1String de)
+ {
+ contents = de;
+ }
+
+ public static DisplayText getInstance(Object obj)
+ {
+ if (obj instanceof ASN1String)
+ {
+ return new DisplayText((ASN1String)obj);
+ }
+ else if (obj == null || obj instanceof DisplayText)
+ {
+ return (DisplayText)obj;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ public static DisplayText getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return (ASN1Primitive)contents;
+ }
+
+ /**
+ * Returns the stored String
object.
+ *
+ * @return the stored text as a String
.
+ */
+ public String getString()
+ {
+ return contents.getString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java
new file mode 100644
index 000000000..d854e8541
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java
@@ -0,0 +1,158 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * The DistributionPoint object.
+ *
+ * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL + * } + *+ */ +public class DistributionPoint + extends ASN1Object +{ + DistributionPointName distributionPoint; + ReasonFlags reasons; + GeneralNames cRLIssuer; + + public static DistributionPoint getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DistributionPoint getInstance( + Object obj) + { + if(obj == null || obj instanceof DistributionPoint) + { + return (DistributionPoint)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new DistributionPoint((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid DistributionPoint: " + obj.getClass().getName()); + } + + public DistributionPoint( + ASN1Sequence seq) + { + for (int i = 0; i != seq.size(); i++) + { + ASN1TaggedObject t = ASN1TaggedObject.getInstance(seq.getObjectAt(i)); + switch (t.getTagNo()) + { + case 0: + distributionPoint = DistributionPointName.getInstance(t, true); + break; + case 1: + reasons = new ReasonFlags(DERBitString.getInstance(t, false)); + break; + case 2: + cRLIssuer = GeneralNames.getInstance(t, false); + } + } + } + + public DistributionPoint( + DistributionPointName distributionPoint, + ReasonFlags reasons, + GeneralNames cRLIssuer) + { + this.distributionPoint = distributionPoint; + this.reasons = reasons; + this.cRLIssuer = cRLIssuer; + } + + public DistributionPointName getDistributionPoint() + { + return distributionPoint; + } + + public ReasonFlags getReasons() + { + return reasons; + } + + public GeneralNames getCRLIssuer() + { + return cRLIssuer; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (distributionPoint != null) + { + // + // as this is a CHOICE it must be explicitly tagged + // + v.add(new DERTaggedObject(0, distributionPoint)); + } + + if (reasons != null) + { + v.add(new DERTaggedObject(false, 1, reasons)); + } + + if (cRLIssuer != null) + { + v.add(new DERTaggedObject(false, 2, cRLIssuer)); + } + + return new DERSequence(v); + } + + public String toString() + { + String sep = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + buf.append("DistributionPoint: ["); + buf.append(sep); + if (distributionPoint != null) + { + appendObject(buf, sep, "distributionPoint", distributionPoint.toString()); + } + if (reasons != null) + { + appendObject(buf, sep, "reasons", reasons.toString()); + } + if (cRLIssuer != null) + { + appendObject(buf, sep, "cRLIssuer", cRLIssuer.toString()); + } + buf.append("]"); + buf.append(sep); + return buf.toString(); + } + + private void appendObject(StringBuffer buf, String sep, String name, String value) + { + String indent = " "; + + buf.append(indent); + buf.append(name); + buf.append(":"); + buf.append(sep); + buf.append(indent); + buf.append(indent); + buf.append(value); + buf.append(sep); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java new file mode 100644 index 000000000..f0033fb87 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java @@ -0,0 +1,138 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * The DistributionPointName object. + *
+ * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RDN + * } + *+ */ +public class DistributionPointName + extends ASN1Object + implements ASN1Choice +{ + ASN1Encodable name; + int type; + + public static final int FULL_NAME = 0; + public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1; + + public static DistributionPointName getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1TaggedObject.getInstance(obj, true)); + } + + public static DistributionPointName getInstance( + Object obj) + { + if (obj == null || obj instanceof DistributionPointName) + { + return (DistributionPointName)obj; + } + else if (obj instanceof ASN1TaggedObject) + { + return new DistributionPointName((ASN1TaggedObject)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public DistributionPointName( + int type, + ASN1Encodable name) + { + this.type = type; + this.name = name; + } + + public DistributionPointName( + GeneralNames name) + { + this(FULL_NAME, name); + } + + /** + * Return the tag number applying to the underlying choice. + * + * @return the tag number for this point name. + */ + public int getType() + { + return this.type; + } + + /** + * Return the tagged object inside the distribution point name. + * + * @return the underlying choice item. + */ + public ASN1Encodable getName() + { + return (ASN1Encodable)name; + } + + public DistributionPointName( + ASN1TaggedObject obj) + { + this.type = obj.getTagNo(); + + if (type == 0) + { + this.name = GeneralNames.getInstance(obj, false); + } + else + { + this.name = ASN1Set.getInstance(obj, false); + } + } + + public ASN1Primitive toASN1Primitive() + { + return new DERTaggedObject(false, type, name); + } + + public String toString() + { + String sep = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + buf.append("DistributionPointName: ["); + buf.append(sep); + if (type == FULL_NAME) + { + appendObject(buf, sep, "fullName", name.toString()); + } + else + { + appendObject(buf, sep, "nameRelativeToCRLIssuer", name.toString()); + } + buf.append("]"); + buf.append(sep); + return buf.toString(); + } + + private void appendObject(StringBuffer buf, String sep, String name, String value) + { + String indent = " "; + + buf.append(indent); + buf.append(name); + buf.append(":"); + buf.append(sep); + buf.append(indent); + buf.append(indent); + buf.append(value); + buf.append(sep); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java new file mode 100644 index 000000000..c430210f9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java @@ -0,0 +1,192 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +/** + * The extendedKeyUsage object. + *
+ * extendedKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + *+ */ +public class ExtendedKeyUsage + extends ASN1Object +{ + Hashtable usageTable = new Hashtable(); + ASN1Sequence seq; + + /** + * Return an ExtendedKeyUsage from the passed in tagged object. + * + * @param obj the tagged object containing the ExtendedKeyUsage + * @param explicit true if the tagged object should be interpreted as explicitly tagged, false if implicit. + * @return the ExtendedKeyUsage contained. + */ + public static ExtendedKeyUsage getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return an ExtendedKeyUsage from the passed in object. + * + * @param obj an ExtendedKeyUsage, some form or encoding of one, or null. + * @return an ExtendedKeyUsage object, or null if null is passed in. + */ + public static ExtendedKeyUsage getInstance( + Object obj) + { + if (obj instanceof ExtendedKeyUsage) + { + return (ExtendedKeyUsage)obj; + } + else if (obj != null) + { + return new ExtendedKeyUsage(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Retrieve an ExtendedKeyUsage for a passed in Extensions object, if present. + * + * @param extensions the extensions object to be examined. + * @return the ExtendedKeyUsage, null if the extension is not present. + */ + public static ExtendedKeyUsage fromExtensions(Extensions extensions) + { + return ExtendedKeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.extendedKeyUsage)); + } + + /** + * Base constructor, from a single KeyPurposeId. + * + * @param usage the keyPurposeId to be included. + */ + public ExtendedKeyUsage( + KeyPurposeId usage) + { + this.seq = new DERSequence(usage); + + this.usageTable.put(usage, usage); + } + + private ExtendedKeyUsage( + ASN1Sequence seq) + { + this.seq = seq; + + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Encodable o = (ASN1Encodable)e.nextElement(); + if (!(o.toASN1Primitive() instanceof ASN1ObjectIdentifier)) + { + throw new IllegalArgumentException("Only ASN1ObjectIdentifiers allowed in ExtendedKeyUsage."); + } + this.usageTable.put(o, o); + } + } + + /** + * Base constructor, from multiple KeyPurposeIds. + * + * @param usages an array of KeyPurposeIds. + */ + public ExtendedKeyUsage( + KeyPurposeId[] usages) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != usages.length; i++) + { + v.add(usages[i]); + this.usageTable.put(usages[i], usages[i]); + } + + this.seq = new DERSequence(v); + } + + /** + * @deprecated use KeyPurposeId[] constructor. + */ + public ExtendedKeyUsage( + Vector usages) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + Enumeration e = usages.elements(); + + while (e.hasMoreElements()) + { + KeyPurposeId o = KeyPurposeId.getInstance(e.nextElement()); + + v.add(o); + this.usageTable.put(o, o); + } + + this.seq = new DERSequence(v); + } + + /** + * Return true if this ExtendedKeyUsage object contains the passed in keyPurposeId. + * + * @param keyPurposeId the KeyPurposeId of interest. + * @return true if the keyPurposeId is present, false otherwise. + */ + public boolean hasKeyPurposeId( + KeyPurposeId keyPurposeId) + { + return (usageTable.get(keyPurposeId) != null); + } + + /** + * Returns all extended key usages. + * + * @return An array with all key purposes. + */ + public KeyPurposeId[] getUsages() + { + KeyPurposeId[] temp = new KeyPurposeId[seq.size()]; + + int i = 0; + for (Enumeration it = seq.getObjects(); it.hasMoreElements();) + { + temp[i++] = KeyPurposeId.getInstance(it.nextElement()); + } + return temp; + } + + /** + * Return the number of KeyPurposeIds present in this ExtendedKeyUsage. + * + * @return the number of KeyPurposeIds + */ + public int size() + { + return usageTable.size(); + } + + /** + * Return the ASN.1 primitive form of this object. + * + * @return an ASN1Sequence. + */ + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Extension.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Extension.java new file mode 100644 index 000000000..b34f191e6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Extension.java @@ -0,0 +1,321 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +/** + * an object for the elements in the X.509 V3 extension block. + */ +public class Extension + extends ASN1Object +{ + /** + * Subject Directory Attributes + */ + public static final ASN1ObjectIdentifier subjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9"); + + /** + * Subject Key Identifier + */ + public static final ASN1ObjectIdentifier subjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14"); + + /** + * Key Usage + */ + public static final ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15"); + + /** + * Private Key Usage Period + */ + public static final ASN1ObjectIdentifier privateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16"); + + /** + * Subject Alternative Name + */ + public static final ASN1ObjectIdentifier subjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17"); + + /** + * Issuer Alternative Name + */ + public static final ASN1ObjectIdentifier issuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18"); + + /** + * Basic Constraints + */ + public static final ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19"); + + /** + * CRL Number + */ + public static final ASN1ObjectIdentifier cRLNumber = new ASN1ObjectIdentifier("2.5.29.20"); + + /** + * Reason code + */ + public static final ASN1ObjectIdentifier reasonCode = new ASN1ObjectIdentifier("2.5.29.21"); + + /** + * Hold Instruction Code + */ + public static final ASN1ObjectIdentifier instructionCode = new ASN1ObjectIdentifier("2.5.29.23"); + + /** + * Invalidity Date + */ + public static final ASN1ObjectIdentifier invalidityDate = new ASN1ObjectIdentifier("2.5.29.24"); + + /** + * Delta CRL indicator + */ + public static final ASN1ObjectIdentifier deltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27"); + + /** + * Issuing Distribution Point + */ + public static final ASN1ObjectIdentifier issuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28"); + + /** + * Certificate Issuer + */ + public static final ASN1ObjectIdentifier certificateIssuer = new ASN1ObjectIdentifier("2.5.29.29"); + + /** + * Name Constraints + */ + public static final ASN1ObjectIdentifier nameConstraints = new ASN1ObjectIdentifier("2.5.29.30"); + + /** + * CRL Distribution Points + */ + public static final ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31"); + + /** + * Certificate Policies + */ + public static final ASN1ObjectIdentifier certificatePolicies = new ASN1ObjectIdentifier("2.5.29.32"); + + /** + * Policy Mappings + */ + public static final ASN1ObjectIdentifier policyMappings = new ASN1ObjectIdentifier("2.5.29.33"); + + /** + * Authority Key Identifier + */ + public static final ASN1ObjectIdentifier authorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35"); + + /** + * Policy Constraints + */ + public static final ASN1ObjectIdentifier policyConstraints = new ASN1ObjectIdentifier("2.5.29.36"); + + /** + * Extended Key Usage + */ + public static final ASN1ObjectIdentifier extendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37"); + + /** + * Freshest CRL + */ + public static final ASN1ObjectIdentifier freshestCRL = new ASN1ObjectIdentifier("2.5.29.46"); + + /** + * Inhibit Any Policy + */ + public static final ASN1ObjectIdentifier inhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54"); + + /** + * Authority Info Access + */ + public static final ASN1ObjectIdentifier authorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1"); + + /** + * Subject Info Access + */ + public static final ASN1ObjectIdentifier subjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11"); + + /** + * Logo Type + */ + public static final ASN1ObjectIdentifier logoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12"); + + /** + * BiometricInfo + */ + public static final ASN1ObjectIdentifier biometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2"); + + /** + * QCStatements + */ + public static final ASN1ObjectIdentifier qCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3"); + + /** + * Audit identity extension in attribute certificates. + */ + public static final ASN1ObjectIdentifier auditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4"); + + /** + * NoRevAvail extension in attribute certificates. + */ + public static final ASN1ObjectIdentifier noRevAvail = new ASN1ObjectIdentifier("2.5.29.56"); + + /** + * TargetInformation extension in attribute certificates. + */ + public static final ASN1ObjectIdentifier targetInformation = new ASN1ObjectIdentifier("2.5.29.55"); + + private ASN1ObjectIdentifier extnId; + private boolean critical; + private ASN1OctetString value; + + public Extension( + ASN1ObjectIdentifier extnId, + ASN1Boolean critical, + ASN1OctetString value) + { + this(extnId, critical.isTrue(), value); + } + + public Extension( + ASN1ObjectIdentifier extnId, + boolean critical, + byte[] value) + { + this(extnId, critical, new DEROctetString(value)); + } + + public Extension( + ASN1ObjectIdentifier extnId, + boolean critical, + ASN1OctetString value) + { + this.extnId = extnId; + this.critical = critical; + this.value = value; + } + + private Extension(ASN1Sequence seq) + { + if (seq.size() == 2) + { + this.extnId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + this.critical = false; + this.value = ASN1OctetString.getInstance(seq.getObjectAt(1)); + } + else if (seq.size() == 3) + { + this.extnId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + this.critical = ASN1Boolean.getInstance(seq.getObjectAt(1)).isTrue(); + this.value = ASN1OctetString.getInstance(seq.getObjectAt(2)); + } + else + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + } + + public static Extension getInstance(Object obj) + { + if (obj instanceof Extension) + { + return (Extension)obj; + } + else if (obj != null) + { + return new Extension(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1ObjectIdentifier getExtnId() + { + return extnId; + } + + public boolean isCritical() + { + return critical; + } + + public ASN1OctetString getExtnValue() + { + return value; + } + + public ASN1Encodable getParsedValue() + { + return convertValueToObject(this); + } + + public int hashCode() + { + if (this.isCritical()) + { + return this.getExtnValue().hashCode() ^ this.getExtnId().hashCode(); + } + + return ~(this.getExtnValue().hashCode() ^ this.getExtnId().hashCode()); + } + + public boolean equals( + Object o) + { + if (!(o instanceof Extension)) + { + return false; + } + + Extension other = (Extension)o; + + return other.getExtnId().equals(this.getExtnId()) + && other.getExtnValue().equals(this.getExtnValue()) + && (other.isCritical() == this.isCritical()); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(extnId); + + if (critical) + { + v.add(ASN1Boolean.getInstance(true)); + } + + v.add(value); + + return new DERSequence(v); + } + + /** + * Convert the value of the passed in extension to an object + * @param ext the extension to parse + * @return the object the value string contains + * @exception IllegalArgumentException if conversion is not possible + */ + private static ASN1Primitive convertValueToObject( + Extension ext) + throws IllegalArgumentException + { + try + { + return ASN1Primitive.fromByteArray(ext.getExtnValue().getOctets()); + } + catch (IOException e) + { + throw new IllegalArgumentException("can't convert extension: " + e); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Extensions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Extensions.java new file mode 100644 index 000000000..9678240a4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Extensions.java @@ -0,0 +1,221 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class Extensions + extends ASN1Object +{ + private Hashtable extensions = new Hashtable(); + private Vector ordering = new Vector(); + + public static Extensions getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static Extensions getInstance( + Object obj) + { + if (obj instanceof Extensions) + { + return (Extensions)obj; + } + else if (obj != null) + { + return new Extensions(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Constructor from ASN1Sequence. + * + * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString) + */ + private Extensions( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + Extension ext = Extension.getInstance(e.nextElement()); + + extensions.put(ext.getExtnId(), ext); + ordering.addElement(ext.getExtnId()); + } + } + + /** + * Base Constructor + * + * @param extension a single extension. + */ + public Extensions( + Extension extension) + { + this.ordering.addElement(extension.getExtnId()); + this.extensions.put(extension.getExtnId(), extension); + } + + /** + * Base Constructor + * + * @param extensions an array of extensions. + */ + public Extensions( + Extension[] extensions) + { + for (int i = 0; i != extensions.length; i++) + { + Extension ext = extensions[i]; + + this.ordering.addElement(ext.getExtnId()); + this.extensions.put(ext.getExtnId(), ext); + } + } + + /** + * return an Enumeration of the extension field's object ids. + */ + public Enumeration oids() + { + return ordering.elements(); + } + + /** + * return the extension represented by the object identifier + * passed in. + * + * @return the extension if it's present, null otherwise. + */ + public Extension getExtension( + ASN1ObjectIdentifier oid) + { + return (Extension)extensions.get(oid); + } + + /** + * return the parsed value of the extension represented by the object identifier + * passed in. + * + * @return the parsed value of the extension if it's present, null otherwise. + */ + public ASN1Encodable getExtensionParsedValue(ASN1ObjectIdentifier oid) + { + Extension ext = this.getExtension(oid); + + if (ext != null) + { + return ext.getParsedValue(); + } + + return null; + } + + /** + *
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnId EXTENSION.&id ({ExtensionSet}), + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + Enumeration e = ordering.elements(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = (Extension)extensions.get(oid); + + vec.add(ext); + } + + return new DERSequence(vec); + } + + public boolean equivalent( + Extensions other) + { + if (extensions.size() != other.extensions.size()) + { + return false; + } + + Enumeration e1 = extensions.keys(); + + while (e1.hasMoreElements()) + { + Object key = e1.nextElement(); + + if (!extensions.get(key).equals(other.extensions.get(key))) + { + return false; + } + } + + return true; + } + + public ASN1ObjectIdentifier[] getExtensionOIDs() + { + return toOidArray(ordering); + } + + public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs() + { + return getExtensionOIDs(false); + } + + public ASN1ObjectIdentifier[] getCriticalExtensionOIDs() + { + return getExtensionOIDs(true); + } + + private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical) + { + Vector oidVec = new Vector(); + + for (int i = 0; i != ordering.size(); i++) + { + Object oid = ordering.elementAt(i); + + if (((Extension)extensions.get(oid)).isCritical() == isCritical) + { + oidVec.addElement(oid); + } + } + + return toOidArray(oidVec); + } + + private ASN1ObjectIdentifier[] toOidArray(Vector oidVec) + { + ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()]; + + for (int i = 0; i != oids.length; i++) + { + oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i); + } + return oids; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java new file mode 100644 index 000000000..1f2f571db --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java @@ -0,0 +1,94 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DEROctetString; + +/** + * Generator for X.509 extensions + */ +public class ExtensionsGenerator +{ + private Hashtable extensions = new Hashtable(); + private Vector extOrdering = new Vector(); + + /** + * Reset the generator + */ + public void reset() + { + extensions = new Hashtable(); + extOrdering = new Vector(); + } + + /** + * Add an extension with the given oid and the passed in value to be included + * in the OCTET STRING associated with the extension. + * + * @param oid OID for the extension. + * @param critical true if critical, false otherwise. + * @param value the ASN.1 object to be included in the extension. + */ + public void addExtension( + ASN1ObjectIdentifier oid, + boolean critical, + ASN1Encodable value) + throws IOException + { + this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + + /** + * Add an extension with the given oid and the passed in byte array to be wrapped in the + * OCTET STRING associated with the extension. + * + * @param oid OID for the extension. + * @param critical true if critical, false otherwise. + * @param value the byte array to be wrapped. + */ + public void addExtension( + ASN1ObjectIdentifier oid, + boolean critical, + byte[] value) + { + if (extensions.containsKey(oid)) + { + throw new IllegalArgumentException("extension " + oid + " already added"); + } + + extOrdering.addElement(oid); + extensions.put(oid, new Extension(oid, critical, new DEROctetString(value))); + } + + /** + * Return true if there are no extension present in this generator. + * + * @return true if empty, false otherwise + */ + public boolean isEmpty() + { + return extOrdering.isEmpty(); + } + + /** + * Generate an Extensions object based on the current state of the generator. + * + * @return an X09Extensions object. + */ + public Extensions generate() + { + Extension[] exts = new Extension[extOrdering.size()]; + + for (int i = 0; i != extOrdering.size(); i++) + { + exts[i] = (Extension)extensions.get(extOrdering.elementAt(i)); + } + + return new Extensions(exts); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java new file mode 100644 index 000000000..86ad11e5e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java @@ -0,0 +1,439 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; +import java.util.StringTokenizer; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.util.IPAddress; + +/** + * The GeneralName object. + *
+ * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER} + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * + * Name ::= CHOICE { RDNSequence } + *+ */ +public class GeneralName + extends ASN1Object + implements ASN1Choice +{ + public static final int otherName = 0; + public static final int rfc822Name = 1; + public static final int dNSName = 2; + public static final int x400Address = 3; + public static final int directoryName = 4; + public static final int ediPartyName = 5; + public static final int uniformResourceIdentifier = 6; + public static final int iPAddress = 7; + public static final int registeredID = 8; + + private ASN1Encodable obj; + private int tag; + + /** + * @deprecated use X500Name constructor. + * @param dirName + */ + public GeneralName( + X509Name dirName) + { + this.obj = X500Name.getInstance(dirName); + this.tag = 4; + } + + public GeneralName( + X500Name dirName) + { + this.obj = dirName; + this.tag = 4; + } + + /** + * When the subjectAltName extension contains an Internet mail address, + * the address MUST be included as an rfc822Name. The format of an + * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822]. + * + * When the subjectAltName extension contains a domain name service + * label, the domain name MUST be stored in the dNSName (an IA5String). + * The name MUST be in the "preferred name syntax," as specified by RFC + * 1034 [RFC 1034]. + * + * When the subjectAltName extension contains a URI, the name MUST be + * stored in the uniformResourceIdentifier (an IA5String). The name MUST + * be a non-relative URL, and MUST follow the URL syntax and encoding + * rules specified in [RFC 1738]. The name must include both a scheme + * (e.g., "http" or "ftp") and a scheme-specific-part. The scheme- + * specific-part must include a fully qualified domain name or IP + * address as the host. + * + * When the subjectAltName extension contains a iPAddress, the address + * MUST be stored in the octet string in "network byte order," as + * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of + * each octet is the LSB of the corresponding byte in the network + * address. For IP Version 4, as specified in RFC 791, the octet string + * MUST contain exactly four octets. For IP Version 6, as specified in + * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC + * 1883]. + */ + public GeneralName( + int tag, + ASN1Encodable name) + { + this.obj = name; + this.tag = tag; + } + + /** + * Create a GeneralName for the given tag from the passed in String. + *
+ * This constructor can handle: + *
+ * Note: A directory name can be encoded in different ways into a byte + * representation. Be aware of this if the byte representation is used for + * comparing results. + * + * @param tag tag number + * @param name string representation of name + * @throws IllegalArgumentException if the string encoding is not correct or * not supported. + */ + public GeneralName( + int tag, + String name) + { + this.tag = tag; + + if (tag == rfc822Name || tag == dNSName || tag == uniformResourceIdentifier) + { + this.obj = new DERIA5String(name); + } + else if (tag == registeredID) + { + this.obj = new ASN1ObjectIdentifier(name); + } + else if (tag == directoryName) + { + this.obj = new X500Name(name); + } + else if (tag == iPAddress) + { + byte[] enc = toGeneralNameEncoding(name); + if (enc != null) + { + this.obj = new DEROctetString(enc); + } + else + { + throw new IllegalArgumentException("IP Address is invalid"); + } + } + else + { + throw new IllegalArgumentException("can't process String for tag: " + tag); + } + } + + public static GeneralName getInstance( + Object obj) + { + if (obj == null || obj instanceof GeneralName) + { + return (GeneralName)obj; + } + + if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagObj = (ASN1TaggedObject)obj; + int tag = tagObj.getTagNo(); + + switch (tag) + { + case otherName: + return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false)); + case rfc822Name: + return new GeneralName(tag, DERIA5String.getInstance(tagObj, false)); + case dNSName: + return new GeneralName(tag, DERIA5String.getInstance(tagObj, false)); + case x400Address: + throw new IllegalArgumentException("unknown tag: " + tag); + case directoryName: + return new GeneralName(tag, X500Name.getInstance(tagObj, true)); + case ediPartyName: + return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false)); + case uniformResourceIdentifier: + return new GeneralName(tag, DERIA5String.getInstance(tagObj, false)); + case iPAddress: + return new GeneralName(tag, ASN1OctetString.getInstance(tagObj, false)); + case registeredID: + return new GeneralName(tag, ASN1ObjectIdentifier.getInstance(tagObj, false)); + } + } + + if (obj instanceof byte[]) + { + try + { + return getInstance(ASN1Primitive.fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("unable to parse encoded general name"); + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + public static GeneralName getInstance( + ASN1TaggedObject tagObj, + boolean explicit) + { + return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true)); + } + + public int getTagNo() + { + return tag; + } + + public ASN1Encodable getName() + { + return obj; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + + buf.append(tag); + buf.append(": "); + switch (tag) + { + case rfc822Name: + case dNSName: + case uniformResourceIdentifier: + buf.append(DERIA5String.getInstance(obj).getString()); + break; + case directoryName: + buf.append(X500Name.getInstance(obj).toString()); + break; + default: + buf.append(obj.toString()); + } + return buf.toString(); + } + + private byte[] toGeneralNameEncoding(String ip) + { + if (IPAddress.isValidIPv6WithNetmask(ip) || IPAddress.isValidIPv6(ip)) + { + int slashIndex = ip.indexOf('/'); + + if (slashIndex < 0) + { + byte[] addr = new byte[16]; + int[] parsedIp = parseIPv6(ip); + copyInts(parsedIp, addr, 0); + + return addr; + } + else + { + byte[] addr = new byte[32]; + int[] parsedIp = parseIPv6(ip.substring(0, slashIndex)); + copyInts(parsedIp, addr, 0); + String mask = ip.substring(slashIndex + 1); + if (mask.indexOf(':') > 0) + { + parsedIp = parseIPv6(mask); + } + else + { + parsedIp = parseMask(mask); + } + copyInts(parsedIp, addr, 16); + + return addr; + } + } + else if (IPAddress.isValidIPv4WithNetmask(ip) || IPAddress.isValidIPv4(ip)) + { + int slashIndex = ip.indexOf('/'); + + if (slashIndex < 0) + { + byte[] addr = new byte[4]; + + parseIPv4(ip, addr, 0); + + return addr; + } + else + { + byte[] addr = new byte[8]; + + parseIPv4(ip.substring(0, slashIndex), addr, 0); + + String mask = ip.substring(slashIndex + 1); + if (mask.indexOf('.') > 0) + { + parseIPv4(mask, addr, 4); + } + else + { + parseIPv4Mask(mask, addr, 4); + } + + return addr; + } + } + + return null; + } + + private void parseIPv4Mask(String mask, byte[] addr, int offset) + { + int maskVal = Integer.parseInt(mask); + + for (int i = 0; i != maskVal; i++) + { + addr[(i / 8) + offset] |= 1 << (7 - (i % 8)); + } + } + + private void parseIPv4(String ip, byte[] addr, int offset) + { + StringTokenizer sTok = new StringTokenizer(ip, "./"); + int index = 0; + + while (sTok.hasMoreTokens()) + { + addr[offset + index++] = (byte)Integer.parseInt(sTok.nextToken()); + } + } + + private int[] parseMask(String mask) + { + int[] res = new int[8]; + int maskVal = Integer.parseInt(mask); + + for (int i = 0; i != maskVal; i++) + { + res[i / 16] |= 1 << (15 - (i % 16)); + } + return res; + } + + private void copyInts(int[] parsedIp, byte[] addr, int offSet) + { + for (int i = 0; i != parsedIp.length; i++) + { + addr[(i * 2) + offSet] = (byte)(parsedIp[i] >> 8); + addr[(i * 2 + 1) + offSet] = (byte)parsedIp[i]; + } + } + + private int[] parseIPv6(String ip) + { + StringTokenizer sTok = new StringTokenizer(ip, ":", true); + int index = 0; + int[] val = new int[8]; + + if (ip.charAt(0) == ':' && ip.charAt(1) == ':') + { + sTok.nextToken(); // skip the first one + } + + int doubleColon = -1; + + while (sTok.hasMoreTokens()) + { + String e = sTok.nextToken(); + + if (e.equals(":")) + { + doubleColon = index; + val[index++] = 0; + } + else + { + if (e.indexOf('.') < 0) + { + val[index++] = Integer.parseInt(e, 16); + if (sTok.hasMoreTokens()) + { + sTok.nextToken(); + } + } + else + { + StringTokenizer eTok = new StringTokenizer(e, "."); + + val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken()); + val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken()); + } + } + } + + if (index != val.length) + { + System.arraycopy(val, doubleColon, val, val.length - (index - doubleColon), index - doubleColon); + for (int i = doubleColon; i != val.length - (index - doubleColon); i++) + { + val[i] = 0; + } + } + + return val; + } + + public ASN1Primitive toASN1Primitive() + { + if (tag == directoryName) // directoryName is explicitly tagged as it is a CHOICE + { + return new DERTaggedObject(true, tag, obj); + } + else + { + return new DERTaggedObject(false, tag, obj); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java new file mode 100644 index 000000000..bf8c21fa8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java @@ -0,0 +1,108 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class GeneralNames + extends ASN1Object +{ + private final GeneralName[] names; + + public static GeneralNames getInstance( + Object obj) + { + if (obj instanceof GeneralNames) + { + return (GeneralNames)obj; + } + + if (obj != null) + { + return new GeneralNames(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static GeneralNames getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static GeneralNames fromExtensions(Extensions extensions, ASN1ObjectIdentifier extOID) + { + return GeneralNames.getInstance(extensions.getExtensionParsedValue(extOID)); + } + + /** + * Construct a GeneralNames object containing one GeneralName. + * + * @param name the name to be contained. + */ + public GeneralNames( + GeneralName name) + { + this.names = new GeneralName[] { name }; + } + + + public GeneralNames( + GeneralName[] names) + { + this.names = names; + } + + private GeneralNames( + ASN1Sequence seq) + { + this.names = new GeneralName[seq.size()]; + + for (int i = 0; i != seq.size(); i++) + { + names[i] = GeneralName.getInstance(seq.getObjectAt(i)); + } + } + + public GeneralName[] getNames() + { + GeneralName[] tmp = new GeneralName[names.length]; + + System.arraycopy(names, 0, tmp, 0, names.length); + + return tmp; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * GeneralNames ::= SEQUENCE SIZE {1..MAX} OF GeneralName + *+ */ + public ASN1Primitive toASN1Primitive() + { + return new DERSequence(names); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String sep = System.getProperty("line.separator"); + + buf.append("GeneralNames:"); + buf.append(sep); + + for (int i = 0; i != names.length; i++) + { + buf.append(" "); + buf.append(names[i]); + buf.append(sep); + } + return buf.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java new file mode 100644 index 000000000..2d4b94a82 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java @@ -0,0 +1,39 @@ +package org.spongycastle.asn1.x509; + +import java.util.Vector; + +public class GeneralNamesBuilder +{ + private Vector names = new Vector(); + + public GeneralNamesBuilder addNames(GeneralNames names) + { + GeneralName[] n = names.getNames(); + + for (int i = 0; i != n.length; i++) + { + this.names.addElement(n[i]); + } + + return this; + } + + public GeneralNamesBuilder addName(GeneralName name) + { + names.addElement(name); + + return this; + } + + public GeneralNames build() + { + GeneralName[] tmp = new GeneralName[names.size()]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = (GeneralName)names.elementAt(i); + } + + return new GeneralNames(tmp); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java new file mode 100644 index 000000000..82084e556 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java @@ -0,0 +1,218 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * Class for containing a restriction object subtrees in NameConstraints. See + * RFC 3280. + * + *
+ * + * GeneralSubtree ::= SEQUENCE + * { + * base GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL + * } + *+ * + * @see org.spongycastle.asn1.x509.NameConstraints + * + */ +public class GeneralSubtree + extends ASN1Object +{ + private static final BigInteger ZERO = BigInteger.valueOf(0); + + private GeneralName base; + + private ASN1Integer minimum; + + private ASN1Integer maximum; + + private GeneralSubtree( + ASN1Sequence seq) + { + base = GeneralName.getInstance(seq.getObjectAt(0)); + + switch (seq.size()) + { + case 1: + break; + case 2: + ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(1)); + switch (o.getTagNo()) + { + case 0: + minimum = ASN1Integer.getInstance(o, false); + break; + case 1: + maximum = ASN1Integer.getInstance(o, false); + break; + default: + throw new IllegalArgumentException("Bad tag number: " + + o.getTagNo()); + } + break; + case 3: + { + { + ASN1TaggedObject oMin = ASN1TaggedObject.getInstance(seq.getObjectAt(1)); + if (oMin.getTagNo() != 0) + { + throw new IllegalArgumentException("Bad tag number for 'minimum': " + oMin.getTagNo()); + } + minimum = ASN1Integer.getInstance(oMin, false); + } + + { + ASN1TaggedObject oMax = ASN1TaggedObject.getInstance(seq.getObjectAt(2)); + if (oMax.getTagNo() != 1) + { + throw new IllegalArgumentException("Bad tag number for 'maximum': " + oMax.getTagNo()); + } + maximum = ASN1Integer.getInstance(oMax, false); + } + + break; + } + default: + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + } + + /** + * Constructor from a given details. + * + * According RFC 3280, the minimum and maximum fields are not used with any + * name forms, thus minimum MUST be zero, and maximum MUST be absent. + *
+ * If minimum is null
, zero is assumed, if
+ * maximum is null
, maximum is absent.
+ *
+ * @param base
+ * A restriction.
+ * @param minimum
+ * Minimum
+ *
+ * @param maximum
+ * Maximum
+ */
+ public GeneralSubtree(
+ GeneralName base,
+ BigInteger minimum,
+ BigInteger maximum)
+ {
+ this.base = base;
+ if (maximum != null)
+ {
+ this.maximum = new ASN1Integer(maximum);
+ }
+ if (minimum == null)
+ {
+ this.minimum = null;
+ }
+ else
+ {
+ this.minimum = new ASN1Integer(minimum);
+ }
+ }
+
+ public GeneralSubtree(GeneralName base)
+ {
+ this(base, null, null);
+ }
+
+ public static GeneralSubtree getInstance(
+ ASN1TaggedObject o,
+ boolean explicit)
+ {
+ return new GeneralSubtree(ASN1Sequence.getInstance(o, explicit));
+ }
+
+ public static GeneralSubtree getInstance(
+ Object obj)
+ {
+ if (obj == null)
+ {
+ return null;
+ }
+
+ if (obj instanceof GeneralSubtree)
+ {
+ return (GeneralSubtree) obj;
+ }
+
+ return new GeneralSubtree(ASN1Sequence.getInstance(obj));
+ }
+
+ public GeneralName getBase()
+ {
+ return base;
+ }
+
+ public BigInteger getMinimum()
+ {
+ if (minimum == null)
+ {
+ return ZERO;
+ }
+
+ return minimum.getValue();
+ }
+
+ public BigInteger getMaximum()
+ {
+ if (maximum == null)
+ {
+ return null;
+ }
+
+ return maximum.getValue();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ *
+ * GeneralSubtree ::= SEQUENCE + * { + * base GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL + * } + *+ * + * @return a ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(base); + + if (minimum != null && !minimum.getValue().equals(ZERO)) + { + v.add(new DERTaggedObject(false, 0, minimum)); + } + + if (maximum != null) + { + v.add(new DERTaggedObject(false, 1, maximum)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Holder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Holder.java new file mode 100644 index 000000000..e3d89cbda --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Holder.java @@ -0,0 +1,245 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * The Holder object. + *
+ * For an v2 attribute certificate this is: + * + *
+ * Holder ::= SEQUENCE { + * baseCertificateID [0] IssuerSerial OPTIONAL, + * -- the issuer and serial number of + * -- the holder's Public Key Certificate + * entityName [1] GeneralNames OPTIONAL, + * -- the name of the claimant or role + * objectDigestInfo [2] ObjectDigestInfo OPTIONAL + * -- used to directly authenticate the holder, + * -- for example, an executable + * } + *+ * + *
+ * For an v1 attribute certificate this is: + * + *
+ * subject CHOICE { + * baseCertificateID [0] EXPLICIT IssuerSerial, + * -- associated with a Public Key Certificate + * subjectName [1] EXPLICIT GeneralNames }, + * -- associated with a name + *+ */ +public class Holder + extends ASN1Object +{ + public static final int V1_CERTIFICATE_HOLDER = 0; + public static final int V2_CERTIFICATE_HOLDER = 1; + + IssuerSerial baseCertificateID; + + GeneralNames entityName; + + ObjectDigestInfo objectDigestInfo; + + private int version = V2_CERTIFICATE_HOLDER; + + public static Holder getInstance(Object obj) + { + if (obj instanceof Holder) + { + return (Holder)obj; + } + else if (obj instanceof ASN1TaggedObject) + { + return new Holder(ASN1TaggedObject.getInstance(obj)); + } + else if (obj != null) + { + return new Holder(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Constructor for a holder for an V1 attribute certificate. + * + * @param tagObj The ASN.1 tagged holder object. + */ + private Holder(ASN1TaggedObject tagObj) + { + switch (tagObj.getTagNo()) + { + case 0: + baseCertificateID = IssuerSerial.getInstance(tagObj, true); + break; + case 1: + entityName = GeneralNames.getInstance(tagObj, true); + break; + default: + throw new IllegalArgumentException("unknown tag in Holder"); + } + version = 0; + } + + /** + * Constructor for a holder for an V2 attribute certificate. + * + * @param seq The ASN.1 sequence. + */ + private Holder(ASN1Sequence seq) + { + if (seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + for (int i = 0; i != seq.size(); i++) + { + ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(seq + .getObjectAt(i)); + + switch (tObj.getTagNo()) + { + case 0: + baseCertificateID = IssuerSerial.getInstance(tObj, false); + break; + case 1: + entityName = GeneralNames.getInstance(tObj, false); + break; + case 2: + objectDigestInfo = ObjectDigestInfo.getInstance(tObj, false); + break; + default: + throw new IllegalArgumentException("unknown tag in Holder"); + } + } + version = 1; + } + + public Holder(IssuerSerial baseCertificateID) + { + this(baseCertificateID, V2_CERTIFICATE_HOLDER); + } + + /** + * Constructs a holder from a IssuerSerial for a V1 or V2 certificate. + * . + * @param baseCertificateID The IssuerSerial. + * @param version The version of the attribute certificate. + */ + public Holder(IssuerSerial baseCertificateID, int version) + { + this.baseCertificateID = baseCertificateID; + this.version = version; + } + + /** + * Returns 1 for V2 attribute certificates or 0 for V1 attribute + * certificates. + * @return The version of the attribute certificate. + */ + public int getVersion() + { + return version; + } + + /** + * Constructs a holder with an entityName for V2 attribute certificates. + * + * @param entityName The entity or subject name. + */ + public Holder(GeneralNames entityName) + { + this(entityName, V2_CERTIFICATE_HOLDER); + } + + /** + * Constructs a holder with an entityName for V2 attribute certificates or + * with a subjectName for V1 attribute certificates. + * + * @param entityName The entity or subject name. + * @param version The version of the attribute certificate. + */ + public Holder(GeneralNames entityName, int version) + { + this.entityName = entityName; + this.version = version; + } + + /** + * Constructs a holder from an object digest info. + * + * @param objectDigestInfo The object digest info object. + */ + public Holder(ObjectDigestInfo objectDigestInfo) + { + this.objectDigestInfo = objectDigestInfo; + } + + public IssuerSerial getBaseCertificateID() + { + return baseCertificateID; + } + + /** + * Returns the entityName for an V2 attribute certificate or the subjectName + * for an V1 attribute certificate. + * + * @return The entityname or subjectname. + */ + public GeneralNames getEntityName() + { + return entityName; + } + + public ObjectDigestInfo getObjectDigestInfo() + { + return objectDigestInfo; + } + + public ASN1Primitive toASN1Primitive() + { + if (version == 1) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (baseCertificateID != null) + { + v.add(new DERTaggedObject(false, 0, baseCertificateID)); + } + + if (entityName != null) + { + v.add(new DERTaggedObject(false, 1, entityName)); + } + + if (objectDigestInfo != null) + { + v.add(new DERTaggedObject(false, 2, objectDigestInfo)); + } + + return new DERSequence(v); + } + else + { + if (entityName != null) + { + return new DERTaggedObject(true, 1, entityName); + } + else + { + return new DERTaggedObject(true, 0, baseCertificateID); + } + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java new file mode 100644 index 000000000..c8eca193c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java @@ -0,0 +1,189 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.DERUTF8String; + +/** + * Implementation of
IetfAttrSyntax
as specified by RFC3281.
+ */
+public class IetfAttrSyntax
+ extends ASN1Object
+{
+ public static final int VALUE_OCTETS = 1;
+ public static final int VALUE_OID = 2;
+ public static final int VALUE_UTF8 = 3;
+ GeneralNames policyAuthority = null;
+ Vector values = new Vector();
+ int valueChoice = -1;
+
+ public static IetfAttrSyntax getInstance(Object obj)
+ {
+ if (obj instanceof IetfAttrSyntax)
+ {
+ return (IetfAttrSyntax)obj;
+ }
+ if (obj != null)
+ {
+ return new IetfAttrSyntax(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ */
+ private IetfAttrSyntax(ASN1Sequence seq)
+ {
+ int i = 0;
+
+ if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+ {
+ policyAuthority = GeneralNames.getInstance(((ASN1TaggedObject)seq.getObjectAt(0)), false);
+ i++;
+ }
+ else if (seq.size() == 2)
+ { // VOMS fix
+ policyAuthority = GeneralNames.getInstance(seq.getObjectAt(0));
+ i++;
+ }
+
+ if (!(seq.getObjectAt(i) instanceof ASN1Sequence))
+ {
+ throw new IllegalArgumentException("Non-IetfAttrSyntax encoding");
+ }
+
+ seq = (ASN1Sequence)seq.getObjectAt(i);
+
+ for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+ {
+ ASN1Primitive obj = (ASN1Primitive)e.nextElement();
+ int type;
+
+ if (obj instanceof ASN1ObjectIdentifier)
+ {
+ type = VALUE_OID;
+ }
+ else if (obj instanceof DERUTF8String)
+ {
+ type = VALUE_UTF8;
+ }
+ else if (obj instanceof DEROctetString)
+ {
+ type = VALUE_OCTETS;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad value type encoding IetfAttrSyntax");
+ }
+
+ if (valueChoice < 0)
+ {
+ valueChoice = type;
+ }
+
+ if (type != valueChoice)
+ {
+ throw new IllegalArgumentException("Mix of value types in IetfAttrSyntax");
+ }
+
+ values.addElement(obj);
+ }
+ }
+
+ public GeneralNames getPolicyAuthority()
+ {
+ return policyAuthority;
+ }
+
+ public int getValueType()
+ {
+ return valueChoice;
+ }
+
+ public Object[] getValues()
+ {
+ if (this.getValueType() == VALUE_OCTETS)
+ {
+ ASN1OctetString[] tmp = new ASN1OctetString[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1OctetString)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+ else if (this.getValueType() == VALUE_OID)
+ {
+ ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1ObjectIdentifier)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+ else
+ {
+ DERUTF8String[] tmp = new DERUTF8String[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (DERUTF8String)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+ }
+
+ /**
+ *
+ * + * + * IetfAttrSyntax ::= SEQUENCE { + * policyAuthority [0] GeneralNames OPTIONAL, + * values SEQUENCE OF CHOICE { + * octets OCTET STRING, + * oid OBJECT IDENTIFIER, + * string UTF8String + * } + * } + * + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (policyAuthority != null) + { + v.add(new DERTaggedObject(0, policyAuthority)); + } + + ASN1EncodableVector v2 = new ASN1EncodableVector(); + + for (Enumeration i = values.elements(); i.hasMoreElements();) + { + v2.add((ASN1Encodable)i.nextElement()); + } + + v.add(new DERSequence(v2)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java new file mode 100644 index 000000000..0ef8674a7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java @@ -0,0 +1,123 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.X500Name; + +public class IssuerSerial + extends ASN1Object +{ + GeneralNames issuer; + ASN1Integer serial; + DERBitString issuerUID; + + public static IssuerSerial getInstance( + Object obj) + { + if (obj instanceof IssuerSerial) + { + return (IssuerSerial)obj; + } + + if (obj != null) + { + return new IssuerSerial(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static IssuerSerial getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + private IssuerSerial( + ASN1Sequence seq) + { + if (seq.size() != 2 && seq.size() != 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + issuer = GeneralNames.getInstance(seq.getObjectAt(0)); + serial = ASN1Integer.getInstance(seq.getObjectAt(1)); + + if (seq.size() == 3) + { + issuerUID = DERBitString.getInstance(seq.getObjectAt(2)); + } + } + + public IssuerSerial( + X500Name issuer, + BigInteger serial) + { + this(new GeneralNames(new GeneralName(issuer)), new ASN1Integer(serial)); + } + + public IssuerSerial( + GeneralNames issuer, + BigInteger serial) + { + this(issuer, new ASN1Integer(serial)); + } + + public IssuerSerial( + GeneralNames issuer, + ASN1Integer serial) + { + this.issuer = issuer; + this.serial = serial; + } + + public GeneralNames getIssuer() + { + return issuer; + } + + public ASN1Integer getSerial() + { + return serial; + } + + public DERBitString getIssuerUID() + { + return issuerUID; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * IssuerSerial ::= SEQUENCE { + * issuer GeneralNames, + * serial CertificateSerialNumber, + * issuerUID UniqueIdentifier OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(issuer); + v.add(serial); + + if (issuerUID != null) + { + v.add(issuerUID); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java new file mode 100644 index 000000000..dd0358fea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java @@ -0,0 +1,274 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Boolean; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + *
+ * IssuingDistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + * onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, + * onlySomeReasons [3] ReasonFlags OPTIONAL, + * indirectCRL [4] BOOLEAN DEFAULT FALSE, + * onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } + *+ */ +public class IssuingDistributionPoint + extends ASN1Object +{ + private DistributionPointName distributionPoint; + + private boolean onlyContainsUserCerts; + + private boolean onlyContainsCACerts; + + private ReasonFlags onlySomeReasons; + + private boolean indirectCRL; + + private boolean onlyContainsAttributeCerts; + + private ASN1Sequence seq; + + public static IssuingDistributionPoint getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static IssuingDistributionPoint getInstance( + Object obj) + { + if (obj instanceof IssuingDistributionPoint) + { + return (IssuingDistributionPoint)obj; + } + else if (obj != null) + { + return new IssuingDistributionPoint(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Constructor from given details. + * + * @param distributionPoint + * May contain an URI as pointer to most current CRL. + * @param onlyContainsUserCerts Covers revocation information for end certificates. + * @param onlyContainsCACerts Covers revocation information for CA certificates. + * + * @param onlySomeReasons + * Which revocation reasons does this point cover. + * @param indirectCRL + * If
true
then the CRL contains revocation
+ * information about certificates ssued by other CAs.
+ * @param onlyContainsAttributeCerts Covers revocation information for attribute certificates.
+ */
+ public IssuingDistributionPoint(
+ DistributionPointName distributionPoint,
+ boolean onlyContainsUserCerts,
+ boolean onlyContainsCACerts,
+ ReasonFlags onlySomeReasons,
+ boolean indirectCRL,
+ boolean onlyContainsAttributeCerts)
+ {
+ this.distributionPoint = distributionPoint;
+ this.indirectCRL = indirectCRL;
+ this.onlyContainsAttributeCerts = onlyContainsAttributeCerts;
+ this.onlyContainsCACerts = onlyContainsCACerts;
+ this.onlyContainsUserCerts = onlyContainsUserCerts;
+ this.onlySomeReasons = onlySomeReasons;
+
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ if (distributionPoint != null)
+ { // CHOICE item so explicitly tagged
+ vec.add(new DERTaggedObject(true, 0, distributionPoint));
+ }
+ if (onlyContainsUserCerts)
+ {
+ vec.add(new DERTaggedObject(false, 1, ASN1Boolean.getInstance(true)));
+ }
+ if (onlyContainsCACerts)
+ {
+ vec.add(new DERTaggedObject(false, 2, ASN1Boolean.getInstance(true)));
+ }
+ if (onlySomeReasons != null)
+ {
+ vec.add(new DERTaggedObject(false, 3, onlySomeReasons));
+ }
+ if (indirectCRL)
+ {
+ vec.add(new DERTaggedObject(false, 4, ASN1Boolean.getInstance(true)));
+ }
+ if (onlyContainsAttributeCerts)
+ {
+ vec.add(new DERTaggedObject(false, 5, ASN1Boolean.getInstance(true)));
+ }
+
+ seq = new DERSequence(vec);
+ }
+
+ /**
+ * Shorthand Constructor from given details.
+ *
+ * @param distributionPoint
+ * May contain an URI as pointer to most current CRL.
+ * @param indirectCRL
+ * If true
then the CRL contains revocation
+ * information about certificates ssued by other CAs.
+ * @param onlyContainsAttributeCerts Covers revocation information for attribute certificates.
+ */
+ public IssuingDistributionPoint(
+ DistributionPointName distributionPoint,
+ boolean indirectCRL,
+ boolean onlyContainsAttributeCerts)
+ {
+ this(distributionPoint, false, false, null, indirectCRL, onlyContainsAttributeCerts);
+ }
+
+ /**
+ * Constructor from ASN1Sequence
+ */
+ private IssuingDistributionPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ // CHOICE so explicit
+ distributionPoint = DistributionPointName.getInstance(o, true);
+ break;
+ case 1:
+ onlyContainsUserCerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 2:
+ onlyContainsCACerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 3:
+ onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
+ break;
+ case 4:
+ indirectCRL = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 5:
+ onlyContainsAttributeCerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "unknown tag in IssuingDistributionPoint");
+ }
+ }
+ }
+
+ public boolean onlyContainsUserCerts()
+ {
+ return onlyContainsUserCerts;
+ }
+
+ public boolean onlyContainsCACerts()
+ {
+ return onlyContainsCACerts;
+ }
+
+ public boolean isIndirectCRL()
+ {
+ return indirectCRL;
+ }
+
+ public boolean onlyContainsAttributeCerts()
+ {
+ return onlyContainsAttributeCerts;
+ }
+
+ /**
+ * @return Returns the distributionPoint.
+ */
+ public DistributionPointName getDistributionPoint()
+ {
+ return distributionPoint;
+ }
+
+ /**
+ * @return Returns the onlySomeReasons.
+ */
+ public ReasonFlags getOnlySomeReasons()
+ {
+ return onlySomeReasons;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+
+ public String toString()
+ {
+ String sep = System.getProperty("line.separator");
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("IssuingDistributionPoint: [");
+ buf.append(sep);
+ if (distributionPoint != null)
+ {
+ appendObject(buf, sep, "distributionPoint", distributionPoint.toString());
+ }
+ if (onlyContainsUserCerts)
+ {
+ appendObject(buf, sep, "onlyContainsUserCerts", booleanToString(onlyContainsUserCerts));
+ }
+ if (onlyContainsCACerts)
+ {
+ appendObject(buf, sep, "onlyContainsCACerts", booleanToString(onlyContainsCACerts));
+ }
+ if (onlySomeReasons != null)
+ {
+ appendObject(buf, sep, "onlySomeReasons", onlySomeReasons.toString());
+ }
+ if (onlyContainsAttributeCerts)
+ {
+ appendObject(buf, sep, "onlyContainsAttributeCerts", booleanToString(onlyContainsAttributeCerts));
+ }
+ if (indirectCRL)
+ {
+ appendObject(buf, sep, "indirectCRL", booleanToString(indirectCRL));
+ }
+ buf.append("]");
+ buf.append(sep);
+ return buf.toString();
+ }
+
+ private void appendObject(StringBuffer buf, String sep, String name, String value)
+ {
+ String indent = " ";
+
+ buf.append(indent);
+ buf.append(name);
+ buf.append(":");
+ buf.append(sep);
+ buf.append(indent);
+ buf.append(indent);
+ buf.append(value);
+ buf.append(sep);
+ }
+
+ private String booleanToString(boolean value)
+ {
+ return value ? "true" : "false";
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java
new file mode 100644
index 000000000..9308a48ac
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java
@@ -0,0 +1,157 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+
+/**
+ * The KeyPurposeId object.
+ * + * KeyPurposeId ::= OBJECT IDENTIFIER + * + * id-kp ::= OBJECT IDENTIFIER { iso(1) identified-organization(3) + * dod(6) internet(1) security(5) mechanisms(5) pkix(7) 3} + * + *+ * To create a new KeyPurposeId where none of the below suit, use + *
+ * ASN1ObjectIdentifier newKeyPurposeIdOID = new ASN1ObjectIdentifier("1.3.6.1..."); + * + * KeyPurposeId newKeyPurposeId = KeyPurposeId.getInstance(newKeyPurposeIdOID); + *+ */ +public class KeyPurposeId + extends ASN1Object +{ + private static final ASN1ObjectIdentifier id_kp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.3"); + + /** + * { 2 5 29 37 0 } + */ + public static final KeyPurposeId anyExtendedKeyUsage = new KeyPurposeId(Extension.extendedKeyUsage.branch("0")); + + /** + * { id-kp 1 } + */ + public static final KeyPurposeId id_kp_serverAuth = new KeyPurposeId(id_kp.branch("1")); + /** + * { id-kp 2 } + */ + public static final KeyPurposeId id_kp_clientAuth = new KeyPurposeId(id_kp.branch("2")); + /** + * { id-kp 3 } + */ + public static final KeyPurposeId id_kp_codeSigning = new KeyPurposeId(id_kp.branch("3")); + /** + * { id-kp 4 } + */ + public static final KeyPurposeId id_kp_emailProtection = new KeyPurposeId(id_kp.branch("4")); + /** + * Usage deprecated by RFC4945 - was { id-kp 5 } + */ + public static final KeyPurposeId id_kp_ipsecEndSystem = new KeyPurposeId(id_kp.branch("5")); + /** + * Usage deprecated by RFC4945 - was { id-kp 6 } + */ + public static final KeyPurposeId id_kp_ipsecTunnel = new KeyPurposeId(id_kp.branch("6")); + /** + * Usage deprecated by RFC4945 - was { idkp 7 } + */ + public static final KeyPurposeId id_kp_ipsecUser = new KeyPurposeId(id_kp.branch("7")); + /** + * { id-kp 8 } + */ + public static final KeyPurposeId id_kp_timeStamping = new KeyPurposeId(id_kp.branch("8")); + /** + * { id-kp 9 } + */ + public static final KeyPurposeId id_kp_OCSPSigning = new KeyPurposeId(id_kp.branch("9")); + /** + * { id-kp 10 } + */ + public static final KeyPurposeId id_kp_dvcs = new KeyPurposeId(id_kp.branch("10")); + /** + * { id-kp 11 } + */ + public static final KeyPurposeId id_kp_sbgpCertAAServerAuth = new KeyPurposeId(id_kp.branch("11")); + /** + * { id-kp 12 } + */ + public static final KeyPurposeId id_kp_scvp_responder = new KeyPurposeId(id_kp.branch("12")); + /** + * { id-kp 13 } + */ + public static final KeyPurposeId id_kp_eapOverPPP = new KeyPurposeId(id_kp.branch("13")); + /** + * { id-kp 14 } + */ + public static final KeyPurposeId id_kp_eapOverLAN = new KeyPurposeId(id_kp.branch("14")); + /** + * { id-kp 15 } + */ + public static final KeyPurposeId id_kp_scvpServer = new KeyPurposeId(id_kp.branch("15")); + /** + * { id-kp 16 } + */ + public static final KeyPurposeId id_kp_scvpClient = new KeyPurposeId(id_kp.branch("16")); + /** + * { id-kp 17 } + */ + public static final KeyPurposeId id_kp_ipsecIKE = new KeyPurposeId(id_kp.branch("17")); + /** + * { id-kp 18 } + */ + public static final KeyPurposeId id_kp_capwapAC = new KeyPurposeId(id_kp.branch("18")); + /** + * { id-kp 19 } + */ + public static final KeyPurposeId id_kp_capwapWTP = new KeyPurposeId(id_kp.branch("19")); + + // + // microsoft key purpose ids + // + /** + * { 1 3 6 1 4 1 311 20 2 2 } + */ + public static final KeyPurposeId id_kp_smartcardlogon = new KeyPurposeId(new ASN1ObjectIdentifier("1.3.6.1.4.1.311.20.2.2")); + + private ASN1ObjectIdentifier id; + + private KeyPurposeId(ASN1ObjectIdentifier id) + { + this.id = id; + } + + /** + * @deprecated use getInstance and an OID or one of the constants above. + * @param id string representation of an OID. + */ + public KeyPurposeId(String id) + { + this(new ASN1ObjectIdentifier(id)); + } + + public static KeyPurposeId getInstance(Object o) + { + if (o instanceof KeyPurposeId) + { + return (KeyPurposeId)o; + } + else if (o != null) + { + return new KeyPurposeId(ASN1ObjectIdentifier.getInstance(o)); + } + + return null; + } + + public ASN1Primitive toASN1Primitive() + { + return id; + } + + public String getId() + { + return id.getId(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java new file mode 100644 index 000000000..afff05543 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java @@ -0,0 +1,113 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBitString; + +/** + * The KeyUsage object. + *
+ * id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } + * + * KeyUsage ::= BIT STRING { + * digitalSignature (0), + * nonRepudiation (1), + * keyEncipherment (2), + * dataEncipherment (3), + * keyAgreement (4), + * keyCertSign (5), + * cRLSign (6), + * encipherOnly (7), + * decipherOnly (8) } + *+ */ +public class KeyUsage + extends ASN1Object +{ + public static final int digitalSignature = (1 << 7); + public static final int nonRepudiation = (1 << 6); + public static final int keyEncipherment = (1 << 5); + public static final int dataEncipherment = (1 << 4); + public static final int keyAgreement = (1 << 3); + public static final int keyCertSign = (1 << 2); + public static final int cRLSign = (1 << 1); + public static final int encipherOnly = (1 << 0); + public static final int decipherOnly = (1 << 15); + + private DERBitString bitString; + + public static KeyUsage getInstance(Object obj) // needs to be DERBitString for other VMs + { + if (obj instanceof KeyUsage) + { + return (KeyUsage)obj; + } + else if (obj != null) + { + return new KeyUsage(DERBitString.getInstance(obj)); + } + + return null; + } + + public static KeyUsage fromExtensions(Extensions extensions) + { + return KeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.keyUsage)); + } + + /** + * Basic constructor. + * + * @param usage - the bitwise OR of the Key Usage flags giving the + * allowed uses for the key. + * e.g. (KeyUsage.keyEncipherment | KeyUsage.dataEncipherment) + */ + public KeyUsage( + int usage) + { + this.bitString = new DERBitString(usage); + } + + private KeyUsage( + DERBitString bitString) + { + this.bitString = bitString; + } + + /** + * Return true if a given usage bit is set, false otherwise. + * + * @param usages combination of usage flags. + * @return true if all bits are set, false otherwise. + */ + public boolean hasUsages(int usages) + { + return (bitString.intValue() & usages) == usages; + } + + public byte[] getBytes() + { + return bitString.getBytes(); + } + + public int getPadBits() + { + return bitString.getPadBits(); + } + + public String toString() + { + byte[] data = bitString.getBytes(); + + if (data.length == 1) + { + return "KeyUsage: 0x" + Integer.toHexString(data[0] & 0xff); + } + return "KeyUsage: 0x" + Integer.toHexString((data[1] & 0xff) << 8 | (data[0] & 0xff)); + } + + public ASN1Primitive toASN1Primitive() + { + return bitString; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java new file mode 100644 index 000000000..17611bb4b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java @@ -0,0 +1,118 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class NameConstraints + extends ASN1Object +{ + private GeneralSubtree[] permitted, excluded; + + public static NameConstraints getInstance(Object obj) + { + if (obj instanceof NameConstraints) + { + return (NameConstraints)obj; + } + if (obj != null) + { + return new NameConstraints(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private NameConstraints(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + while (e.hasMoreElements()) + { + ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement()); + switch (o.getTagNo()) + { + case 0: + permitted = createArray(ASN1Sequence.getInstance(o, false)); + break; + case 1: + excluded = createArray(ASN1Sequence.getInstance(o, false)); + break; + } + } + } + + /** + * Constructor from a given details. + * + *
+ * permitted and excluded are arrays of GeneralSubtree objects.
+ *
+ * @param permitted
+ * Permitted subtrees
+ * @param excluded
+ * Excludes subtrees
+ */
+ public NameConstraints(
+ GeneralSubtree[] permitted,
+ GeneralSubtree[] excluded)
+ {
+ if (permitted != null)
+ {
+ this.permitted = permitted;
+ }
+
+ if (excluded != null)
+ {
+ this.excluded = excluded;
+ }
+ }
+
+ private GeneralSubtree[] createArray(ASN1Sequence subtree)
+ {
+ GeneralSubtree[] ar = new GeneralSubtree[subtree.size()];
+
+ for (int i = 0; i != ar.length; i++)
+ {
+ ar[i] = GeneralSubtree.getInstance(subtree.getObjectAt(i));
+ }
+
+ return ar;
+ }
+
+ public GeneralSubtree[] getPermittedSubtrees()
+ {
+ return permitted;
+ }
+
+ public GeneralSubtree[] getExcludedSubtrees()
+ {
+ return excluded;
+ }
+
+ /*
+ * NameConstraints ::= SEQUENCE { permittedSubtrees [0] GeneralSubtrees
+ * OPTIONAL, excludedSubtrees [1] GeneralSubtrees OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (permitted != null)
+ {
+ v.add(new DERTaggedObject(false, 0, new DERSequence(permitted)));
+ }
+
+ if (excluded != null)
+ {
+ v.add(new DERTaggedObject(false, 1, new DERSequence(excluded)));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java
new file mode 100644
index 000000000..5c9a78836
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java
@@ -0,0 +1,170 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * NoticeReference
class, used in
+ * CertificatePolicies
X509 V3 extensions
+ * (in policy qualifiers).
+ *
+ *
+ * NoticeReference ::= SEQUENCE { + * organization DisplayText, + * noticeNumbers SEQUENCE OF INTEGER } + * + *+ * + * @see PolicyQualifierInfo + * @see PolicyInformation + */ +public class NoticeReference + extends ASN1Object +{ + private DisplayText organization; + private ASN1Sequence noticeNumbers; + + private static ASN1EncodableVector convertVector(Vector numbers) + { + ASN1EncodableVector av = new ASN1EncodableVector(); + + Enumeration it = numbers.elements(); + + while (it.hasMoreElements()) + { + Object o = it.nextElement(); + ASN1Integer di; + + if (o instanceof BigInteger) + { + di = new ASN1Integer((BigInteger)o); + } + else if (o instanceof Integer) + { + di = new ASN1Integer(((Integer)o).intValue()); + } + else + { + throw new IllegalArgumentException(); + } + + av.add(di); + } + return av; + } + + /** + * Creates a new
NoticeReference
instance.
+ *
+ * @param organization a String
value
+ * @param numbers a Vector
value
+ */
+ public NoticeReference(
+ String organization,
+ Vector numbers)
+ {
+ this(organization, convertVector(numbers));
+ }
+
+ /**
+ * Creates a new NoticeReference
instance.
+ *
+ * @param organization a String
value
+ * @param noticeNumbers an ASN1EncodableVector
value
+ */
+ public NoticeReference(
+ String organization,
+ ASN1EncodableVector noticeNumbers)
+ {
+ this(new DisplayText(organization), noticeNumbers);
+ }
+
+ /**
+ * Creates a new NoticeReference
instance.
+ *
+ * @param organization displayText
+ * @param noticeNumbers an ASN1EncodableVector
value
+ */
+ public NoticeReference(
+ DisplayText organization,
+ ASN1EncodableVector noticeNumbers)
+ {
+ this.organization = organization;
+ this.noticeNumbers = new DERSequence(noticeNumbers);
+ }
+
+ /**
+ * Creates a new NoticeReference
instance.
+ * Useful for reconstructing a NoticeReference
+ * instance from its encodable/encoded form.
+ *
+ * @param as an ASN1Sequence
value obtained from either
+ * calling @{link toASN1Primitive()} for a NoticeReference
+ * instance or from parsing it from a DER-encoded stream.
+ */
+ private NoticeReference(
+ ASN1Sequence as)
+ {
+ if (as.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + as.size());
+ }
+
+ organization = DisplayText.getInstance(as.getObjectAt(0));
+ noticeNumbers = ASN1Sequence.getInstance(as.getObjectAt(1));
+ }
+
+ public static NoticeReference getInstance(
+ Object as)
+ {
+ if (as instanceof NoticeReference)
+ {
+ return (NoticeReference)as;
+ }
+ else if (as != null)
+ {
+ return new NoticeReference(ASN1Sequence.getInstance(as));
+ }
+
+ return null;
+ }
+
+ public DisplayText getOrganization()
+ {
+ return organization;
+ }
+
+ public ASN1Integer[] getNoticeNumbers()
+ {
+ ASN1Integer[] tmp = new ASN1Integer[noticeNumbers.size()];
+
+ for (int i = 0; i != noticeNumbers.size(); i++)
+ {
+ tmp[i] = ASN1Integer.getInstance(noticeNumbers.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+
+ /**
+ * Describe toASN1Object
method here.
+ *
+ * @return a ASN1Primitive
value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+ av.add (organization);
+ av.add (noticeNumbers);
+ return new DERSequence (av);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java
new file mode 100644
index 000000000..1d714cdf1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java
@@ -0,0 +1,190 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Enumerated;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * ObjectDigestInfo ASN.1 structure used in v2 attribute certificates.
+ *
+ *
+ * + * ObjectDigestInfo ::= SEQUENCE { + * digestedObjectType ENUMERATED { + * publicKey (0), + * publicKeyCert (1), + * otherObjectTypes (2) }, + * -- otherObjectTypes MUST NOT + * -- be used in this profile + * otherObjectTypeID OBJECT IDENTIFIER OPTIONAL, + * digestAlgorithm AlgorithmIdentifier, + * objectDigest BIT STRING + * } + * + *+ * + */ +public class ObjectDigestInfo + extends ASN1Object +{ + /** + * The public key is hashed. + */ + public final static int publicKey = 0; + + /** + * The public key certificate is hashed. + */ + public final static int publicKeyCert = 1; + + /** + * An other object is hashed. + */ + public final static int otherObjectDigest = 2; + + ASN1Enumerated digestedObjectType; + + ASN1ObjectIdentifier otherObjectTypeID; + + AlgorithmIdentifier digestAlgorithm; + + DERBitString objectDigest; + + public static ObjectDigestInfo getInstance( + Object obj) + { + if (obj instanceof ObjectDigestInfo) + { + return (ObjectDigestInfo)obj; + } + + if (obj != null) + { + return new ObjectDigestInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static ObjectDigestInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Constructor from given details. + *
+ * If digestedObjectType
is not {@link #publicKeyCert} or
+ * {@link #publicKey} otherObjectTypeID
must be given,
+ * otherwise it is ignored.
+ *
+ * @param digestedObjectType The digest object type.
+ * @param otherObjectTypeID The object type ID for
+ * otherObjectDigest
.
+ * @param digestAlgorithm The algorithm identifier for the hash.
+ * @param objectDigest The hash value.
+ */
+ public ObjectDigestInfo(
+ int digestedObjectType,
+ ASN1ObjectIdentifier otherObjectTypeID,
+ AlgorithmIdentifier digestAlgorithm,
+ byte[] objectDigest)
+ {
+ this.digestedObjectType = new ASN1Enumerated(digestedObjectType);
+ if (digestedObjectType == otherObjectDigest)
+ {
+ this.otherObjectTypeID = otherObjectTypeID;
+ }
+
+ this.digestAlgorithm = digestAlgorithm;
+ this.objectDigest = new DERBitString(objectDigest);
+ }
+
+ private ObjectDigestInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() > 4 || seq.size() < 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ digestedObjectType = ASN1Enumerated.getInstance(seq.getObjectAt(0));
+
+ int offset = 0;
+
+ if (seq.size() == 4)
+ {
+ otherObjectTypeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(1));
+ offset++;
+ }
+
+ digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1 + offset));
+
+ objectDigest = DERBitString.getInstance(seq.getObjectAt(2 + offset));
+ }
+
+ public ASN1Enumerated getDigestedObjectType()
+ {
+ return digestedObjectType;
+ }
+
+ public ASN1ObjectIdentifier getOtherObjectTypeID()
+ {
+ return otherObjectTypeID;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digestAlgorithm;
+ }
+
+ public DERBitString getObjectDigest()
+ {
+ return objectDigest;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ *
+ * + * ObjectDigestInfo ::= SEQUENCE { + * digestedObjectType ENUMERATED { + * publicKey (0), + * publicKeyCert (1), + * otherObjectTypes (2) }, + * -- otherObjectTypes MUST NOT + * -- be used in this profile + * otherObjectTypeID OBJECT IDENTIFIER OPTIONAL, + * digestAlgorithm AlgorithmIdentifier, + * objectDigest BIT STRING + * } + * + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(digestedObjectType); + + if (otherObjectTypeID != null) + { + v.add(otherObjectTypeID); + } + + v.add(digestAlgorithm); + v.add(objectDigest); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java new file mode 100644 index 000000000..540fd0b2c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java @@ -0,0 +1,106 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * PKIX RFC 5280 + *
+ * id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } + * + * PolicyConstraints ::= SEQUENCE { + * requireExplicitPolicy [0] SkipCerts OPTIONAL, + * inhibitPolicyMapping [1] SkipCerts OPTIONAL } + * + * SkipCerts ::= INTEGER (0..MAX) + *+ */ +public class PolicyConstraints + extends ASN1Object +{ + private BigInteger requireExplicitPolicyMapping; + private BigInteger inhibitPolicyMapping; + + public PolicyConstraints(BigInteger requireExplicitPolicyMapping, BigInteger inhibitPolicyMapping) + { + this.requireExplicitPolicyMapping = requireExplicitPolicyMapping; + this.inhibitPolicyMapping = inhibitPolicyMapping; + } + + private PolicyConstraints(ASN1Sequence seq) + { + for (int i = 0; i != seq.size(); i++) + { + ASN1TaggedObject to = ASN1TaggedObject.getInstance(seq.getObjectAt(i)); + + if (to.getTagNo() == 0) + { + requireExplicitPolicyMapping = ASN1Integer.getInstance(to, false).getValue(); + } + else if (to.getTagNo() == 1) + { + inhibitPolicyMapping = ASN1Integer.getInstance(to, false).getValue(); + } + else + { + throw new IllegalArgumentException("Unknown tag encountered."); + } + } + } + + public static PolicyConstraints getInstance( + Object obj) + { + if (obj instanceof PolicyConstraints) + { + return (PolicyConstraints)obj; + } + + if (obj != null) + { + return new PolicyConstraints(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public static PolicyConstraints fromExtensions(Extensions extensions) + { + return PolicyConstraints.getInstance(extensions.getExtensionParsedValue(Extension.policyConstraints)); + } + + public BigInteger getRequireExplicitPolicyMapping() + { + return requireExplicitPolicyMapping; + } + + public BigInteger getInhibitPolicyMapping() + { + return inhibitPolicyMapping; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (requireExplicitPolicyMapping != null) + { + v.add(new DERTaggedObject(0, new ASN1Integer(requireExplicitPolicyMapping))); + } + + if (inhibitPolicyMapping != null) + { + v.add(new DERTaggedObject(1, new ASN1Integer(inhibitPolicyMapping))); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java new file mode 100644 index 000000000..0b5ee0c83 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java @@ -0,0 +1,87 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +public class PolicyInformation + extends ASN1Object +{ + private ASN1ObjectIdentifier policyIdentifier; + private ASN1Sequence policyQualifiers; + + private PolicyInformation( + ASN1Sequence seq) + { + if (seq.size() < 1 || seq.size() > 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + policyIdentifier = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + + if (seq.size() > 1) + { + policyQualifiers = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + } + + public PolicyInformation( + ASN1ObjectIdentifier policyIdentifier) + { + this.policyIdentifier = policyIdentifier; + } + + public PolicyInformation( + ASN1ObjectIdentifier policyIdentifier, + ASN1Sequence policyQualifiers) + { + this.policyIdentifier = policyIdentifier; + this.policyQualifiers = policyQualifiers; + } + + public static PolicyInformation getInstance( + Object obj) + { + if (obj == null || obj instanceof PolicyInformation) + { + return (PolicyInformation)obj; + } + + return new PolicyInformation(ASN1Sequence.getInstance(obj)); + } + + public ASN1ObjectIdentifier getPolicyIdentifier() + { + return policyIdentifier; + } + + public ASN1Sequence getPolicyQualifiers() + { + return policyQualifiers; + } + + /* + * PolicyInformation ::= SEQUENCE { + * policyIdentifier CertPolicyId, + * policyQualifiers SEQUENCE SIZE (1..MAX) OF + * PolicyQualifierInfo OPTIONAL } + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(policyIdentifier); + + if (policyQualifiers != null) + { + v.add(policyQualifiers); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java new file mode 100644 index 000000000..4b01b0d6b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java @@ -0,0 +1,107 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * PolicyMappings V3 extension, described in RFC3280. + *
+ * PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { + * issuerDomainPolicy CertPolicyId, + * subjectDomainPolicy CertPolicyId } + *+ * + * @see RFC 3280, section 4.2.1.6 + */ +public class PolicyMappings + extends ASN1Object +{ + ASN1Sequence seq = null; + + public static PolicyMappings getInstance(Object obj) + { + if (obj instanceof PolicyMappings) + { + return (PolicyMappings)obj; + } + if (obj != null) + { + return new PolicyMappings(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Creates a new
PolicyMappings
instance.
+ *
+ * @param seq an ASN1Sequence
constructed as specified
+ * in RFC 3280
+ */
+ private PolicyMappings(ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ /**
+ * Creates a new PolicyMappings
instance.
+ *
+ * @param mappings a HashMap
value that maps
+ * String
oids
+ * to other String
oids.
+ * @deprecated use CertPolicyId constructors.
+ */
+ public PolicyMappings(Hashtable mappings)
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ Enumeration it = mappings.keys();
+
+ while (it.hasMoreElements())
+ {
+ String idp = (String)it.nextElement();
+ String sdp = (String)mappings.get(idp);
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(new ASN1ObjectIdentifier(idp));
+ dv.add(new ASN1ObjectIdentifier(sdp));
+ dev.add(new DERSequence(dv));
+ }
+
+ seq = new DERSequence(dev);
+ }
+
+ public PolicyMappings(CertPolicyId issuerDomainPolicy, CertPolicyId subjectDomainPolicy)
+ {
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(issuerDomainPolicy);
+ dv.add(subjectDomainPolicy);
+
+ seq = new DERSequence(new DERSequence(dv));
+ }
+
+ public PolicyMappings(CertPolicyId[] issuerDomainPolicy, CertPolicyId[] subjectDomainPolicy)
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+
+ for (int i = 0; i != issuerDomainPolicy.length; i++)
+ {
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(issuerDomainPolicy[i]);
+ dv.add(subjectDomainPolicy[i]);
+ dev.add(new DERSequence(dv));
+ }
+
+ seq = new DERSequence(dev);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java
new file mode 100644
index 000000000..011e52cdb
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java
@@ -0,0 +1,31 @@
+
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * PolicyQualifierId, used in the CertificatePolicies
+ * X509V3 extension.
+ *
+ * + * id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } + * id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } + * id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } + * PolicyQualifierId ::= + * OBJECT IDENTIFIER (id-qt-cps | id-qt-unotice) + *+ */ +public class PolicyQualifierId extends ASN1ObjectIdentifier +{ + private static final String id_qt = "1.3.6.1.5.5.7.2"; + + private PolicyQualifierId(String id) + { + super(id); + } + + public static final PolicyQualifierId id_qt_cps = + new PolicyQualifierId(id_qt + ".1"); + public static final PolicyQualifierId id_qt_unotice = + new PolicyQualifierId(id_qt + ".2"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java new file mode 100644 index 000000000..d7c5a343b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java @@ -0,0 +1,115 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERSequence; + +/** + * Policy qualifiers, used in the X509V3 CertificatePolicies + * extension. + * + *
+ * PolicyQualifierInfo ::= SEQUENCE { + * policyQualifierId PolicyQualifierId, + * qualifier ANY DEFINED BY policyQualifierId } + *+ */ +public class PolicyQualifierInfo + extends ASN1Object +{ + private ASN1ObjectIdentifier policyQualifierId; + private ASN1Encodable qualifier; + + /** + * Creates a new
PolicyQualifierInfo
instance.
+ *
+ * @param policyQualifierId a PolicyQualifierId
value
+ * @param qualifier the qualifier, defined by the above field.
+ */
+ public PolicyQualifierInfo(
+ ASN1ObjectIdentifier policyQualifierId,
+ ASN1Encodable qualifier)
+ {
+ this.policyQualifierId = policyQualifierId;
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * Creates a new PolicyQualifierInfo
containing a
+ * cPSuri qualifier.
+ *
+ * @param cps the CPS (certification practice statement) uri as a
+ * String
.
+ */
+ public PolicyQualifierInfo(
+ String cps)
+ {
+ policyQualifierId = PolicyQualifierId.id_qt_cps;
+ qualifier = new DERIA5String (cps);
+ }
+
+ /**
+ * Creates a new PolicyQualifierInfo
instance.
+ *
+ * @param as PolicyQualifierInfo
X509 structure
+ * encoded as an ASN1Sequence.
+ * @deprecated use PolicyQualifierInfo.getInstance()
+ */
+ public PolicyQualifierInfo(
+ ASN1Sequence as)
+ {
+ if (as.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + as.size());
+ }
+
+ policyQualifierId = ASN1ObjectIdentifier.getInstance(as.getObjectAt(0));
+ qualifier = as.getObjectAt(1);
+ }
+
+ public static PolicyQualifierInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof PolicyQualifierInfo)
+ {
+ return (PolicyQualifierInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new PolicyQualifierInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+
+ public ASN1ObjectIdentifier getPolicyQualifierId()
+ {
+ return policyQualifierId;
+ }
+
+ public ASN1Encodable getQualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Returns a DER-encodable representation of this instance.
+ *
+ * @return a ASN1Primitive
value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ dev.add(policyQualifierId);
+ dev.add(qualifier);
+
+ return new DERSequence(dev);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java
new file mode 100644
index 000000000..f47041469
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java
@@ -0,0 +1,84 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * + * PrivateKeyUsagePeriod ::= SEQUENCE { + * notBefore [0] GeneralizedTime OPTIONAL, + * notAfter [1] GeneralizedTime OPTIONAL } + *+ */ +public class PrivateKeyUsagePeriod + extends ASN1Object +{ + public static PrivateKeyUsagePeriod getInstance(Object obj) + { + if (obj instanceof PrivateKeyUsagePeriod) + { + return (PrivateKeyUsagePeriod)obj; + } + + if (obj != null) + { + return new PrivateKeyUsagePeriod(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private DERGeneralizedTime _notBefore, _notAfter; + + private PrivateKeyUsagePeriod(ASN1Sequence seq) + { + Enumeration en = seq.getObjects(); + while (en.hasMoreElements()) + { + ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement(); + + if (tObj.getTagNo() == 0) + { + _notBefore = DERGeneralizedTime.getInstance(tObj, false); + } + else if (tObj.getTagNo() == 1) + { + _notAfter = DERGeneralizedTime.getInstance(tObj, false); + } + } + } + + public DERGeneralizedTime getNotBefore() + { + return _notBefore; + } + + public DERGeneralizedTime getNotAfter() + { + return _notAfter; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (_notBefore != null) + { + v.add(new DERTaggedObject(false, 0, _notBefore)); + } + if (_notAfter != null) + { + v.add(new DERTaggedObject(false, 1, _notAfter)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java new file mode 100644 index 000000000..174511646 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java @@ -0,0 +1,98 @@ +package org.spongycastle.asn1.x509; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +/** + * @deprecated use org.spongycastle.asn1.pkcs.RSAPublicKey + */ +public class RSAPublicKeyStructure + extends ASN1Object +{ + private BigInteger modulus; + private BigInteger publicExponent; + + public static RSAPublicKeyStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RSAPublicKeyStructure getInstance( + Object obj) + { + if(obj == null || obj instanceof RSAPublicKeyStructure) + { + return (RSAPublicKeyStructure)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new RSAPublicKeyStructure((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid RSAPublicKeyStructure: " + obj.getClass().getName()); + } + + public RSAPublicKeyStructure( + BigInteger modulus, + BigInteger publicExponent) + { + this.modulus = modulus; + this.publicExponent = publicExponent; + } + + public RSAPublicKeyStructure( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + modulus = ASN1Integer.getInstance(e.nextElement()).getPositiveValue(); + publicExponent = ASN1Integer.getInstance(e.nextElement()).getPositiveValue(); + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return publicExponent; + } + + /** + * This outputs the key in PKCS1v2 format. + *
+ * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * } + *+ *
+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(getModulus())); + v.add(new ASN1Integer(getPublicExponent())); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java new file mode 100644 index 000000000..3df0821bf --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java @@ -0,0 +1,85 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.DERBitString; + +/** + * The ReasonFlags object. + *
+ * ReasonFlags ::= BIT STRING { + * unused (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6), + * privilegeWithdrawn (7), + * aACompromise (8) } + *+ */ +public class ReasonFlags + extends DERBitString +{ + /** + * @deprecated use lower case version + */ + public static final int UNUSED = (1 << 7); + /** + * @deprecated use lower case version + */ + public static final int KEY_COMPROMISE = (1 << 6); + /** + * @deprecated use lower case version + */ + public static final int CA_COMPROMISE = (1 << 5); + /** + * @deprecated use lower case version + */ + public static final int AFFILIATION_CHANGED = (1 << 4); + /** + * @deprecated use lower case version + */ + public static final int SUPERSEDED = (1 << 3); + /** + * @deprecated use lower case version + */ + public static final int CESSATION_OF_OPERATION = (1 << 2); + /** + * @deprecated use lower case version + */ + public static final int CERTIFICATE_HOLD = (1 << 1); + /** + * @deprecated use lower case version + */ + public static final int PRIVILEGE_WITHDRAWN = (1 << 0); + /** + * @deprecated use lower case version + */ + public static final int AA_COMPROMISE = (1 << 15); + + public static final int unused = (1 << 7); + public static final int keyCompromise = (1 << 6); + public static final int cACompromise = (1 << 5); + public static final int affiliationChanged = (1 << 4); + public static final int superseded = (1 << 3); + public static final int cessationOfOperation = (1 << 2); + public static final int certificateHold = (1 << 1); + public static final int privilegeWithdrawn = (1 << 0); + public static final int aACompromise = (1 << 15); + + /** + * @param reasons - the bitwise OR of the Key Reason flags giving the + * allowed uses for the key. + */ + public ReasonFlags( + int reasons) + { + super(getBytes(reasons), getPadBits(reasons)); + } + + public ReasonFlags( + DERBitString reasons) + { + super(reasons.getBytes(), reasons.getPadBits()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java new file mode 100644 index 000000000..0a0b3d2b7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java @@ -0,0 +1,237 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * Implementation of the RoleSyntax object as specified by the RFC3281. + * + *
+ * RoleSyntax ::= SEQUENCE { + * roleAuthority [0] GeneralNames OPTIONAL, + * roleName [1] GeneralName + * } + *+ */ +public class RoleSyntax + extends ASN1Object +{ + private GeneralNames roleAuthority; + private GeneralName roleName; + + /** + * RoleSyntax factory method. + * @param obj the object used to construct an instance of
+ * RoleSyntax
. It must be an instance of RoleSyntax
+ *
or ASN1Sequence
.
+ * @return the instance of RoleSyntax
built from the
+ * supplied object.
+ * @throws java.lang.IllegalArgumentException if the object passed
+ * to the factory is not an instance of RoleSyntax
or
+ * ASN1Sequence
.
+ */
+ public static RoleSyntax getInstance(
+ Object obj)
+ {
+
+ if (obj instanceof RoleSyntax)
+ {
+ return (RoleSyntax)obj;
+ }
+ else if (obj != null)
+ {
+ return new RoleSyntax(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor.
+ * @param roleAuthority the role authority of this RoleSyntax.
+ * @param roleName the role name of this RoleSyntax.
+ */
+ public RoleSyntax(
+ GeneralNames roleAuthority,
+ GeneralName roleName)
+ {
+ if(roleName == null ||
+ roleName.getTagNo() != GeneralName.uniformResourceIdentifier ||
+ ((ASN1String)roleName.getName()).getString().equals(""))
+ {
+ throw new IllegalArgumentException("the role name MUST be non empty and MUST " +
+ "use the URI option of GeneralName");
+ }
+ this.roleAuthority = roleAuthority;
+ this.roleName = roleName;
+ }
+
+ /**
+ * Constructor. Invoking this constructor is the same as invoking
+ * new RoleSyntax(null, roleName)
.
+ * @param roleName the role name of this RoleSyntax.
+ */
+ public RoleSyntax(
+ GeneralName roleName)
+ {
+ this(null, roleName);
+ }
+
+ /**
+ * Utility constructor. Takes a String
argument representing
+ * the role name, builds a GeneralName
to hold the role name
+ * and calls the constructor that takes a GeneralName
.
+ * @param roleName
+ */
+ public RoleSyntax(
+ String roleName)
+ {
+ this(new GeneralName(GeneralName.uniformResourceIdentifier,
+ (roleName == null)? "": roleName));
+ }
+
+ /**
+ * Constructor that builds an instance of RoleSyntax
by
+ * extracting the encoded elements from the ASN1Sequence
+ * object supplied.
+ * @param seq an instance of ASN1Sequence
that holds
+ * the encoded elements used to build this RoleSyntax
.
+ */
+ private RoleSyntax(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+ switch (taggedObject.getTagNo())
+ {
+ case 0:
+ roleAuthority = GeneralNames.getInstance(taggedObject, false);
+ break;
+ case 1:
+ roleName = GeneralName.getInstance(taggedObject, true);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown tag in RoleSyntax");
+ }
+ }
+ }
+
+ /**
+ * Gets the role authority of this RoleSyntax.
+ * @return an instance of GeneralNames
holding the
+ * role authority of this RoleSyntax.
+ */
+ public GeneralNames getRoleAuthority()
+ {
+ return this.roleAuthority;
+ }
+
+ /**
+ * Gets the role name of this RoleSyntax.
+ * @return an instance of GeneralName
holding the
+ * role name of this RoleSyntax.
+ */
+ public GeneralName getRoleName()
+ {
+ return this.roleName;
+ }
+
+ /**
+ * Gets the role name as a java.lang.String
object.
+ * @return the role name of this RoleSyntax represented as a
+ * java.lang.String
object.
+ */
+ public String getRoleNameAsString()
+ {
+ ASN1String str = (ASN1String)this.roleName.getName();
+
+ return str.getString();
+ }
+
+ /**
+ * Gets the role authority as a String[]
object.
+ * @return the role authority of this RoleSyntax represented as a
+ * String[]
array.
+ */
+ public String[] getRoleAuthorityAsString()
+ {
+ if(roleAuthority == null)
+ {
+ return new String[0];
+ }
+
+ GeneralName[] names = roleAuthority.getNames();
+ String[] namesString = new String[names.length];
+ for(int i = 0; i < names.length; i++)
+ {
+ ASN1Encodable value = names[i].getName();
+ if(value instanceof ASN1String)
+ {
+ namesString[i] = ((ASN1String)value).getString();
+ }
+ else
+ {
+ namesString[i] = value.toString();
+ }
+ }
+ return namesString;
+ }
+
+ /**
+ * Implementation of the method toASN1Object
as
+ * required by the superclass ASN1Encodable
.
+ *
+ * + * RoleSyntax ::= SEQUENCE { + * roleAuthority [0] GeneralNames OPTIONAL, + * roleName [1] GeneralName + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + if(this.roleAuthority != null) + { + v.add(new DERTaggedObject(false, 0, roleAuthority)); + } + v.add(new DERTaggedObject(true, 1, roleName)); + + return new DERSequence(v); + } + + public String toString() + { + StringBuffer buff = new StringBuffer("Name: " + this.getRoleNameAsString() + + " - Auth: "); + if(this.roleAuthority == null || roleAuthority.getNames().length == 0) + { + buff.append("N/A"); + } + else + { + String[] names = this.getRoleAuthorityAsString(); + buff.append('[').append(names[0]); + for(int i = 1; i < names.length; i++) + { + buff.append(", ").append(names[i]); + } + buff.append(']'); + } + return buff.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java new file mode 100644 index 000000000..13d4d284a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java @@ -0,0 +1,144 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * This extension may contain further X.500 attributes of the subject. See also + * RFC 3039. + * + *
+ * SubjectDirectoryAttributes ::= Attributes + * Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute + * Attribute ::= SEQUENCE + * { + * type AttributeType + * values SET OF AttributeValue + * } + * + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY DEFINED BY AttributeType + *+ * + * @see org.spongycastle.asn1.x500.style.BCStyle for AttributeType ObjectIdentifiers. + */ +public class SubjectDirectoryAttributes + extends ASN1Object +{ + private Vector attributes = new Vector(); + + public static SubjectDirectoryAttributes getInstance( + Object obj) + { + if (obj instanceof SubjectDirectoryAttributes) + { + return (SubjectDirectoryAttributes)obj; + } + + if (obj != null) + { + return new SubjectDirectoryAttributes(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type SubjectDirectoryAttributes: + * + *
+ * SubjectDirectoryAttributes ::= Attributes + * Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute + * Attribute ::= SEQUENCE + * { + * type AttributeType + * values SET OF AttributeValue + * } + * + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY DEFINED BY AttributeType + *+ * + * @param seq + * The ASN.1 sequence. + */ + private SubjectDirectoryAttributes(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement()); + attributes.addElement(Attribute.getInstance(s)); + } + } + + /** + * Constructor from a vector of attributes. + * + * The vector consists of attributes of type {@link Attribute Attribute} + * + * @param attributes + * The attributes. + * + */ + public SubjectDirectoryAttributes(Vector attributes) + { + Enumeration e = attributes.elements(); + + while (e.hasMoreElements()) + { + this.attributes.addElement(e.nextElement()); + } + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * SubjectDirectoryAttributes ::= Attributes + * Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute + * Attribute ::= SEQUENCE + * { + * type AttributeType + * values SET OF AttributeValue + * } + * + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY DEFINED BY AttributeType + *+ * + * @return a ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + Enumeration e = attributes.elements(); + + while (e.hasMoreElements()) + { + + vec.add((Attribute)e.nextElement()); + } + + return new DERSequence(vec); + } + + /** + * @return Returns the attributes. + */ + public Vector getAttributes() + { + return attributes; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java new file mode 100644 index 000000000..96ff98e3e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java @@ -0,0 +1,135 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; + +/** + * The SubjectKeyIdentifier object. + *
+ * SubjectKeyIdentifier::= OCTET STRING + *+ */ +public class SubjectKeyIdentifier + extends ASN1Object +{ + private byte[] keyidentifier; + + public static SubjectKeyIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1OctetString.getInstance(obj, explicit)); + } + + public static SubjectKeyIdentifier getInstance( + Object obj) + { + if (obj instanceof SubjectKeyIdentifier) + { + return (SubjectKeyIdentifier)obj; + } + else if (obj != null) + { + return new SubjectKeyIdentifier(ASN1OctetString.getInstance(obj)); + } + + return null; + } + + public static SubjectKeyIdentifier fromExtensions(Extensions extensions) + { + return SubjectKeyIdentifier.getInstance(extensions.getExtensionParsedValue(Extension.subjectKeyIdentifier)); + } + + public SubjectKeyIdentifier( + byte[] keyid) + { + this.keyidentifier = keyid; + } + + protected SubjectKeyIdentifier( + ASN1OctetString keyid) + { + this.keyidentifier = keyid.getOctets(); + } + + public byte[] getKeyIdentifier() + { + return keyidentifier; + } + + public ASN1Primitive toASN1Primitive() + { + return new DEROctetString(keyidentifier); + } + + + /** + * Calculates the keyidentifier using a SHA1 hash over the BIT STRING + * from SubjectPublicKeyInfo as defined in RFC3280. + * + * @param spki the subject public key info. + * @deprecated + */ + public SubjectKeyIdentifier( + SubjectPublicKeyInfo spki) + { + this.keyidentifier = getDigest(spki); + } + + /** + * Return a RFC 3280 type 1 key identifier. As in: + *
+ * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the + * value of the BIT STRING subjectPublicKey (excluding the tag, + * length, and number of unused bits). + *+ * @param keyInfo the key info object containing the subjectPublicKey field. + * @return the key identifier. + * @deprecated use org.spongycastle.cert.X509ExtensionUtils.createSubjectKeyIdentifier + */ + public static SubjectKeyIdentifier createSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo) + { + return new SubjectKeyIdentifier(keyInfo); + } + + /** + * Return a RFC 3280 type 2 key identifier. As in: + *
+ * (2) The keyIdentifier is composed of a four bit type field with + * the value 0100 followed by the least significant 60 bits of the + * SHA-1 hash of the value of the BIT STRING subjectPublicKey. + *+ * @param keyInfo the key info object containing the subjectPublicKey field. + * @return the key identifier. + * @deprecated use org.spongycastle.cert.X509ExtensionUtils.createTruncatedSubjectKeyIdentifier + */ + public static SubjectKeyIdentifier createTruncatedSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo) + { + byte[] dig = getDigest(keyInfo); + byte[] id = new byte[8]; + + System.arraycopy(dig, dig.length - 8, id, 0, id.length); + + id[0] &= 0x0f; + id[0] |= 0x40; + + return new SubjectKeyIdentifier(id); + } + + private static byte[] getDigest(SubjectPublicKeyInfo spki) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + return resBuf; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java new file mode 100644 index 000000000..68a4c85e8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java @@ -0,0 +1,156 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; + +/** + * The object that contains the public key stored in a certficate. + *
+ * The getEncoded() method in the public keys in the JCE produces a DER + * encoded one of these. + */ +public class SubjectPublicKeyInfo + extends ASN1Object +{ + private AlgorithmIdentifier algId; + private DERBitString keyData; + + public static SubjectPublicKeyInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static SubjectPublicKeyInfo getInstance( + Object obj) + { + if (obj instanceof SubjectPublicKeyInfo) + { + return (SubjectPublicKeyInfo)obj; + } + else if (obj != null) + { + return new SubjectPublicKeyInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public SubjectPublicKeyInfo( + AlgorithmIdentifier algId, + ASN1Encodable publicKey) + throws IOException + { + this.keyData = new DERBitString(publicKey); + this.algId = algId; + } + + public SubjectPublicKeyInfo( + AlgorithmIdentifier algId, + byte[] publicKey) + { + this.keyData = new DERBitString(publicKey); + this.algId = algId; + } + + public SubjectPublicKeyInfo( + ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + this.algId = AlgorithmIdentifier.getInstance(e.nextElement()); + this.keyData = DERBitString.getInstance(e.nextElement()); + } + + public AlgorithmIdentifier getAlgorithm() + { + return algId; + } + + /** + * @deprecated use getAlgorithm() + * @return alg ID. + */ + public AlgorithmIdentifier getAlgorithmId() + { + return algId; + } + + /** + * for when the public key is an encoded object - if the bitstring + * can't be decoded this routine throws an IOException. + * + * @exception IOException - if the bit string doesn't represent a DER + * encoded object. + * @return the public key as an ASN.1 primitive. + */ + public ASN1Primitive parsePublicKey() + throws IOException + { + ASN1InputStream aIn = new ASN1InputStream(keyData.getBytes()); + + return aIn.readObject(); + } + + /** + * for when the public key is an encoded object - if the bitstring + * can't be decoded this routine throws an IOException. + * + * @exception IOException - if the bit string doesn't represent a DER + * encoded object. + * @deprecated use parsePublicKey + * @return the public key as an ASN.1 primitive. + */ + public ASN1Primitive getPublicKey() + throws IOException + { + ASN1InputStream aIn = new ASN1InputStream(keyData.getBytes()); + + return aIn.readObject(); + } + + /** + * for when the public key is raw bits. + * + * @return the public key as the raw bit string... + */ + public DERBitString getPublicKeyData() + { + return keyData; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * publicKey BIT STRING } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(algId); + v.add(keyData); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java new file mode 100644 index 000000000..e21c0bd94 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java @@ -0,0 +1,309 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.DERUTCTime; +import org.spongycastle.asn1.x500.X500Name; + +/** + * PKIX RFC-2459 - TBSCertList object. + *
+ * TBSCertList ::= SEQUENCE { + * version Version OPTIONAL, + * -- if present, shall be v2 + * signature AlgorithmIdentifier, + * issuer Name, + * thisUpdate Time, + * nextUpdate Time OPTIONAL, + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate Time, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, shall be v2 + * } OPTIONAL, + * crlExtensions [0] EXPLICIT Extensions OPTIONAL + * -- if present, shall be v2 + * } + *+ */ +public class TBSCertList + extends ASN1Object +{ + public static class CRLEntry + extends ASN1Object + { + ASN1Sequence seq; + + Extensions crlEntryExtensions; + + private CRLEntry( + ASN1Sequence seq) + { + if (seq.size() < 2 || seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + this.seq = seq; + } + + public static CRLEntry getInstance(Object o) + { + if (o instanceof CRLEntry) + { + return ((CRLEntry)o); + } + else if (o != null) + { + return new CRLEntry(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public ASN1Integer getUserCertificate() + { + return ASN1Integer.getInstance(seq.getObjectAt(0)); + } + + public Time getRevocationDate() + { + return Time.getInstance(seq.getObjectAt(1)); + } + + public Extensions getExtensions() + { + if (crlEntryExtensions == null && seq.size() == 3) + { + crlEntryExtensions = Extensions.getInstance(seq.getObjectAt(2)); + } + + return crlEntryExtensions; + } + + public ASN1Primitive toASN1Primitive() + { + return seq; + } + + public boolean hasExtensions() + { + return seq.size() == 3; + } + } + + private class RevokedCertificatesEnumeration + implements Enumeration + { + private final Enumeration en; + + RevokedCertificatesEnumeration(Enumeration en) + { + this.en = en; + } + + public boolean hasMoreElements() + { + return en.hasMoreElements(); + } + + public Object nextElement() + { + return CRLEntry.getInstance(en.nextElement()); + } + } + + private class EmptyEnumeration + implements Enumeration + { + public boolean hasMoreElements() + { + return false; + } + + public Object nextElement() + { + return null; // TODO: check exception handling + } + } + + ASN1Integer version; + AlgorithmIdentifier signature; + X500Name issuer; + Time thisUpdate; + Time nextUpdate; + ASN1Sequence revokedCertificates; + Extensions crlExtensions; + + public static TBSCertList getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static TBSCertList getInstance( + Object obj) + { + if (obj instanceof TBSCertList) + { + return (TBSCertList)obj; + } + else if (obj != null) + { + return new TBSCertList(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public TBSCertList( + ASN1Sequence seq) + { + if (seq.size() < 3 || seq.size() > 7) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + int seqPos = 0; + + if (seq.getObjectAt(seqPos) instanceof ASN1Integer) + { + version = ASN1Integer.getInstance(seq.getObjectAt(seqPos++)); + } + else + { + version = null; // version is optional + } + + signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqPos++)); + issuer = X500Name.getInstance(seq.getObjectAt(seqPos++)); + thisUpdate = Time.getInstance(seq.getObjectAt(seqPos++)); + + if (seqPos < seq.size() + && (seq.getObjectAt(seqPos) instanceof DERUTCTime + || seq.getObjectAt(seqPos) instanceof DERGeneralizedTime + || seq.getObjectAt(seqPos) instanceof Time)) + { + nextUpdate = Time.getInstance(seq.getObjectAt(seqPos++)); + } + + if (seqPos < seq.size() + && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject)) + { + revokedCertificates = ASN1Sequence.getInstance(seq.getObjectAt(seqPos++)); + } + + if (seqPos < seq.size() + && seq.getObjectAt(seqPos) instanceof DERTaggedObject) + { + crlExtensions = Extensions.getInstance(ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(seqPos), true)); + } + } + + public int getVersionNumber() + { + if (version == null) + { + return 1; + } + return version.getValue().intValue() + 1; + } + + public ASN1Integer getVersion() + { + return version; + } + + public AlgorithmIdentifier getSignature() + { + return signature; + } + + public X500Name getIssuer() + { + return issuer; + } + + public Time getThisUpdate() + { + return thisUpdate; + } + + public Time getNextUpdate() + { + return nextUpdate; + } + + public CRLEntry[] getRevokedCertificates() + { + if (revokedCertificates == null) + { + return new CRLEntry[0]; + } + + CRLEntry[] entries = new CRLEntry[revokedCertificates.size()]; + + for (int i = 0; i < entries.length; i++) + { + entries[i] = CRLEntry.getInstance(revokedCertificates.getObjectAt(i)); + } + + return entries; + } + + public Enumeration getRevokedCertificateEnumeration() + { + if (revokedCertificates == null) + { + return new EmptyEnumeration(); + } + + return new RevokedCertificatesEnumeration(revokedCertificates.getObjects()); + } + + public Extensions getExtensions() + { + return crlExtensions; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != null) + { + v.add(version); + } + v.add(signature); + v.add(issuer); + + v.add(thisUpdate); + if (nextUpdate != null) + { + v.add(nextUpdate); + } + + // Add CRLEntries if they exist + if (revokedCertificates != null) + { + v.add(revokedCertificates); + } + + if (crlExtensions != null) + { + v.add(new DERTaggedObject(0, crlExtensions)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java new file mode 100644 index 000000000..b2ab6f1e4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java @@ -0,0 +1,192 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; + +/** + * The TBSCertificate object. + *
+ * TBSCertificate ::= SEQUENCE { + * version [ 0 ] Version DEFAULT v1(0), + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL, + * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL, + * extensions [ 3 ] Extensions OPTIONAL + * } + *+ *
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class + * will parse them, but you really shouldn't be creating new ones. + */ +public class TBSCertificate + extends ASN1Object +{ + ASN1Sequence seq; + + ASN1Integer version; + ASN1Integer serialNumber; + AlgorithmIdentifier signature; + X500Name issuer; + Time startDate, endDate; + X500Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + DERBitString issuerUniqueId; + DERBitString subjectUniqueId; + Extensions extensions; + + public static TBSCertificate getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static TBSCertificate getInstance( + Object obj) + { + if (obj instanceof TBSCertificate) + { + return (TBSCertificate)obj; + } + else if (obj != null) + { + return new TBSCertificate(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private TBSCertificate( + ASN1Sequence seq) + { + int seqStart = 0; + + this.seq = seq; + + // + // some certficates don't include a version number - we assume v1 + // + if (seq.getObjectAt(0) instanceof DERTaggedObject) + { + version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true); + } + else + { + seqStart = -1; // field 0 is missing! + version = new ASN1Integer(0); + } + + serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1)); + + signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2)); + issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3)); + + // + // before and after dates + // + ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4); + + startDate = Time.getInstance(dates.getObjectAt(0)); + endDate = Time.getInstance(dates.getObjectAt(1)); + + subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5)); + + // + // public key info. + // + subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6)); + + for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--) + { + DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras); + + switch (extra.getTagNo()) + { + case 1: + issuerUniqueId = DERBitString.getInstance(extra, false); + break; + case 2: + subjectUniqueId = DERBitString.getInstance(extra, false); + break; + case 3: + extensions = Extensions.getInstance(ASN1Sequence.getInstance(extra, true)); + } + } + } + + public int getVersionNumber() + { + return version.getValue().intValue() + 1; + } + + public ASN1Integer getVersion() + { + return version; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + public AlgorithmIdentifier getSignature() + { + return signature; + } + + public X500Name getIssuer() + { + return issuer; + } + + public Time getStartDate() + { + return startDate; + } + + public Time getEndDate() + { + return endDate; + } + + public X500Name getSubject() + { + return subject; + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return subjectPublicKeyInfo; + } + + public DERBitString getIssuerUniqueId() + { + return issuerUniqueId; + } + + public DERBitString getSubjectUniqueId() + { + return subjectUniqueId; + } + + public Extensions getExtensions() + { + return extensions; + } + + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java new file mode 100644 index 000000000..d138b47e6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java @@ -0,0 +1,194 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x500.X500Name; + +/** + * The TBSCertificate object. + *
+ * TBSCertificate ::= SEQUENCE { + * version [ 0 ] Version DEFAULT v1(0), + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL, + * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL, + * extensions [ 3 ] Extensions OPTIONAL + * } + *+ *
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class + * will parse them, but you really shouldn't be creating new ones. + */ +public class TBSCertificateStructure + extends ASN1Object + implements X509ObjectIdentifiers, PKCSObjectIdentifiers +{ + ASN1Sequence seq; + + ASN1Integer version; + ASN1Integer serialNumber; + AlgorithmIdentifier signature; + X500Name issuer; + Time startDate, endDate; + X500Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + DERBitString issuerUniqueId; + DERBitString subjectUniqueId; + X509Extensions extensions; + + public static TBSCertificateStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static TBSCertificateStructure getInstance( + Object obj) + { + if (obj instanceof TBSCertificateStructure) + { + return (TBSCertificateStructure)obj; + } + else if (obj != null) + { + return new TBSCertificateStructure(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public TBSCertificateStructure( + ASN1Sequence seq) + { + int seqStart = 0; + + this.seq = seq; + + // + // some certficates don't include a version number - we assume v1 + // + if (seq.getObjectAt(0) instanceof DERTaggedObject) + { + version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true); + } + else + { + seqStart = -1; // field 0 is missing! + version = new ASN1Integer(0); + } + + serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1)); + + signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2)); + issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3)); + + // + // before and after dates + // + ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4); + + startDate = Time.getInstance(dates.getObjectAt(0)); + endDate = Time.getInstance(dates.getObjectAt(1)); + + subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5)); + + // + // public key info. + // + subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6)); + + for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--) + { + DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras); + + switch (extra.getTagNo()) + { + case 1: + issuerUniqueId = DERBitString.getInstance(extra, false); + break; + case 2: + subjectUniqueId = DERBitString.getInstance(extra, false); + break; + case 3: + extensions = X509Extensions.getInstance(extra); + } + } + } + + public int getVersion() + { + return version.getValue().intValue() + 1; + } + + public ASN1Integer getVersionNumber() + { + return version; + } + + public ASN1Integer getSerialNumber() + { + return serialNumber; + } + + public AlgorithmIdentifier getSignature() + { + return signature; + } + + public X500Name getIssuer() + { + return issuer; + } + + public Time getStartDate() + { + return startDate; + } + + public Time getEndDate() + { + return endDate; + } + + public X500Name getSubject() + { + return subject; + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return subjectPublicKeyInfo; + } + + public DERBitString getIssuerUniqueId() + { + return issuerUniqueId; + } + + public DERBitString getSubjectUniqueId() + { + return subjectUniqueId; + } + + public X509Extensions getExtensions() + { + return extensions; + } + + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Target.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Target.java new file mode 100644 index 000000000..c153c260c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Target.java @@ -0,0 +1,138 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * Target structure used in target information extension for attribute + * certificates from RFC 3281. + * + *
+ * Target ::= CHOICE { + * targetName [0] GeneralName, + * targetGroup [1] GeneralName, + * targetCert [2] TargetCert + * } + *+ * + *
+ * The targetCert field is currently not supported and must not be used + * according to RFC 3281. + */ +public class Target + extends ASN1Object + implements ASN1Choice +{ + public static final int targetName = 0; + public static final int targetGroup = 1; + + private GeneralName targName; + private GeneralName targGroup; + + /** + * Creates an instance of a Target from the given object. + *
+ * obj
can be a Target or a {@link ASN1TaggedObject}
+ *
+ * @param obj The object.
+ * @return A Target instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as Target.
+ */
+ public static Target getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof Target)
+ {
+ return (Target) obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Target((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: "
+ + obj.getClass());
+ }
+
+ /**
+ * Constructor from ASN1TaggedObject.
+ *
+ * @param tagObj The tagged object.
+ * @throws IllegalArgumentException if the encoding is wrong.
+ */
+ private Target(ASN1TaggedObject tagObj)
+ {
+ switch (tagObj.getTagNo())
+ {
+ case targetName: // GeneralName is already a choice so explicit
+ targName = GeneralName.getInstance(tagObj, true);
+ break;
+ case targetGroup:
+ targGroup = GeneralName.getInstance(tagObj, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag: " + tagObj.getTagNo());
+ }
+ }
+
+ /**
+ * Constructor from given details.
+ *
+ * Exactly one of the parameters must be not null
.
+ *
+ * @param type the choice type to apply to the name.
+ * @param name the general name.
+ * @throws IllegalArgumentException if type is invalid.
+ */
+ public Target(int type, GeneralName name)
+ {
+ this(new DERTaggedObject(type, name));
+ }
+
+ /**
+ * @return Returns the targetGroup.
+ */
+ public GeneralName getTargetGroup()
+ {
+ return targGroup;
+ }
+
+ /**
+ * @return Returns the targetName.
+ */
+ public GeneralName getTargetName()
+ {
+ return targName;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ *
+ * Target ::= CHOICE { + * targetName [0] GeneralName, + * targetGroup [1] GeneralName, + * targetCert [2] TargetCert + * } + *+ * + * @return a ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + // GeneralName is a choice already so most be explicitly tagged + if (targName != null) + { + return new DERTaggedObject(true, 0, targName); + } + else + { + return new DERTaggedObject(true, 1, targGroup); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java new file mode 100644 index 000000000..0b8c49a56 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java @@ -0,0 +1,120 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * Target information extension for attributes certificates according to RFC + * 3281. + * + *
+ * SEQUENCE OF Targets + *+ * + */ +public class TargetInformation + extends ASN1Object +{ + private ASN1Sequence targets; + + /** + * Creates an instance of a TargetInformation from the given object. + *
+ * obj
can be a TargetInformation or a {@link ASN1Sequence}
+ *
+ * @param obj The object.
+ * @return A TargetInformation instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as TargetInformation.
+ */
+ public static TargetInformation getInstance(Object obj)
+ {
+ if (obj instanceof TargetInformation)
+ {
+ return (TargetInformation)obj;
+ }
+ else if (obj != null)
+ {
+ return new TargetInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from a ASN1Sequence.
+ *
+ * @param seq The ASN1Sequence.
+ * @throws IllegalArgumentException if the sequence does not contain
+ * correctly encoded Targets elements.
+ */
+ private TargetInformation(ASN1Sequence seq)
+ {
+ targets = seq;
+ }
+
+ /**
+ * Returns the targets in this target information extension.
+ *
+ * @return Returns the targets.
+ */
+ public Targets[] getTargetsObjects()
+ {
+ Targets[] copy = new Targets[targets.size()];
+ int count = 0;
+ for (Enumeration e = targets.getObjects(); e.hasMoreElements();)
+ {
+ copy[count++] = Targets.getInstance(e.nextElement());
+ }
+ return copy;
+ }
+
+ /**
+ * Constructs a target information from a single targets element.
+ * According to RFC 3281 only one targets element must be produced.
+ *
+ * @param targets A Targets instance.
+ */
+ public TargetInformation(Targets targets)
+ {
+ this.targets = new DERSequence(targets);
+ }
+
+ /**
+ * According to RFC 3281 only one targets element must be produced. If
+ * multiple targets are given they must be merged in
+ * into one targets element.
+ *
+ * @param targets An array with {@link Targets}.
+ */
+ public TargetInformation(Target[] targets)
+ {
+ this(new Targets(targets));
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ *
+ * SEQUENCE OF Targets + *+ * + *
+ * According to RFC 3281 only one targets element must be produced. If + * multiple targets are given in the constructor they are merged into one + * targets element. If this was produced from a + * {@link org.spongycastle.asn1.ASN1Sequence} the encoding is kept. + * + * @return a ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + return targets; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Targets.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Targets.java new file mode 100644 index 000000000..a67b69840 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Targets.java @@ -0,0 +1,121 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * Targets structure used in target information extension for attribute + * certificates from RFC 3281. + * + *
+ * Targets ::= SEQUENCE OF Target + * + * Target ::= CHOICE { + * targetName [0] GeneralName, + * targetGroup [1] GeneralName, + * targetCert [2] TargetCert + * } + * + * TargetCert ::= SEQUENCE { + * targetCertificate IssuerSerial, + * targetName GeneralName OPTIONAL, + * certDigestInfo ObjectDigestInfo OPTIONAL + * } + *+ * + * @see org.spongycastle.asn1.x509.Target + * @see org.spongycastle.asn1.x509.TargetInformation + */ +public class Targets + extends ASN1Object +{ + private ASN1Sequence targets; + + /** + * Creates an instance of a Targets from the given object. + *
+ * obj
can be a Targets or a {@link ASN1Sequence}
+ *
+ * @param obj The object.
+ * @return A Targets instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as Target.
+ */
+ public static Targets getInstance(Object obj)
+ {
+ if (obj instanceof Targets)
+ {
+ return (Targets)obj;
+ }
+ else if (obj != null)
+ {
+ return new Targets(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ *
+ * @param targets The ASN.1 SEQUENCE.
+ * @throws IllegalArgumentException if the contents of the sequence are
+ * invalid.
+ */
+ private Targets(ASN1Sequence targets)
+ {
+ this.targets = targets;
+ }
+
+ /**
+ * Constructor from given targets.
+ *
+ * The vector is copied.
+ *
+ * @param targets A Vector
of {@link Target}s.
+ * @see Target
+ * @throws IllegalArgumentException if the vector contains not only Targets.
+ */
+ public Targets(Target[] targets)
+ {
+ this.targets = new DERSequence(targets);
+ }
+
+ /**
+ * Returns the targets in a Vector
.
+ *
+ * The vector is cloned before it is returned. + * + * @return Returns the targets. + */ + public Target[] getTargets() + { + Target[] targs = new Target[targets.size()]; + int count = 0; + for (Enumeration e = targets.getObjects(); e.hasMoreElements();) + { + targs[count++] = Target.getInstance(e.nextElement()); + } + return targs; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *
+ * Targets ::= SEQUENCE OF Target + *+ * + * @return a ASN1Primitive + */ + public ASN1Primitive toASN1Primitive() + { + return targets; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Time.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Time.java new file mode 100644 index 000000000..e42d837cc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/Time.java @@ -0,0 +1,133 @@ +package org.spongycastle.asn1.x509; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.SimpleTimeZone; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERUTCTime; + +public class Time + extends ASN1Object + implements ASN1Choice +{ + ASN1Primitive time; + + public static Time getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + public Time( + ASN1Primitive time) + { + if (!(time instanceof DERUTCTime) + && !(time instanceof DERGeneralizedTime)) + { + throw new IllegalArgumentException("unknown object passed to Time"); + } + + this.time = time; + } + + /** + * creates a time object from a given date - if the date is between 1950 + * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime + * is used. + */ + public Time( + Date date) + { + SimpleTimeZone tz = new SimpleTimeZone(0, "Z"); + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss"); + + dateF.setTimeZone(tz); + + String d = dateF.format(date) + "Z"; + int year = Integer.parseInt(d.substring(0, 4)); + + if (year < 1950 || year > 2049) + { + time = new DERGeneralizedTime(d); + } + else + { + time = new DERUTCTime(d.substring(2)); + } + } + + public static Time getInstance( + Object obj) + { + if (obj == null || obj instanceof Time) + { + return (Time)obj; + } + else if (obj instanceof DERUTCTime) + { + return new Time((DERUTCTime)obj); + } + else if (obj instanceof DERGeneralizedTime) + { + return new Time((DERGeneralizedTime)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public String getTime() + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedTime(); + } + else + { + return ((DERGeneralizedTime)time).getTime(); + } + } + + public Date getDate() + { + try + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedDate(); + } + else + { + return ((DERGeneralizedTime)time).getDate(); + } + } + catch (ParseException e) + { // this should never happen + throw new IllegalStateException("invalid date string: " + e.getMessage()); + } + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return time; + } + + public String toString() + { + return getTime(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java new file mode 100644 index 000000000..22c512c7e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java @@ -0,0 +1,132 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + *
UserNotice
class, used in
+ * CertificatePolicies
X509 extensions (in policy
+ * qualifiers).
+ * + * UserNotice ::= SEQUENCE { + * noticeRef NoticeReference OPTIONAL, + * explicitText DisplayText OPTIONAL} + * + *+ * + * @see PolicyQualifierId + * @see PolicyInformation + */ +public class UserNotice + extends ASN1Object +{ + private NoticeReference noticeRef; + private DisplayText explicitText; + + /** + * Creates a new
UserNotice
instance.
+ *
+ * @param noticeRef a NoticeReference
value
+ * @param explicitText a DisplayText
value
+ */
+ public UserNotice(
+ NoticeReference noticeRef,
+ DisplayText explicitText)
+ {
+ this.noticeRef = noticeRef;
+ this.explicitText = explicitText;
+ }
+
+ /**
+ * Creates a new UserNotice
instance.
+ *
+ * @param noticeRef a NoticeReference
value
+ * @param str the explicitText field as a String.
+ */
+ public UserNotice(
+ NoticeReference noticeRef,
+ String str)
+ {
+ this(noticeRef, new DisplayText(str));
+ }
+
+ /**
+ * Creates a new UserNotice
instance.
+ * Useful from reconstructing a UserNotice
instance
+ * from its encodable/encoded form.
+ *
+ * @param as an ASN1Sequence
value obtained from either
+ * calling @{link toASN1Primitive()} for a UserNotice
+ * instance or from parsing it from a DER-encoded stream.
+ */
+ private UserNotice(
+ ASN1Sequence as)
+ {
+ if (as.size() == 2)
+ {
+ noticeRef = NoticeReference.getInstance(as.getObjectAt(0));
+ explicitText = DisplayText.getInstance(as.getObjectAt(1));
+ }
+ else if (as.size() == 1)
+ {
+ if (as.getObjectAt(0).toASN1Primitive() instanceof ASN1Sequence)
+ {
+ noticeRef = NoticeReference.getInstance(as.getObjectAt(0));
+ }
+ else
+ {
+ explicitText = DisplayText.getInstance(as.getObjectAt(0));
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + as.size());
+ }
+ }
+
+ public static UserNotice getInstance(
+ Object obj)
+ {
+ if (obj instanceof UserNotice)
+ {
+ return (UserNotice)obj;
+ }
+
+ if (obj != null)
+ {
+ return new UserNotice(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public NoticeReference getNoticeRef()
+ {
+ return noticeRef;
+ }
+
+ public DisplayText getExplicitText()
+ {
+ return explicitText;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+
+ if (noticeRef != null)
+ {
+ av.add(noticeRef);
+ }
+
+ if (explicitText != null)
+ {
+ av.add(explicitText);
+ }
+
+ return new DERSequence(av);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java
new file mode 100644
index 000000000..415cabc1d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java
@@ -0,0 +1,144 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * Generator for Version 1 TBSCertificateStructures.
+ *
+ * TBSCertificate ::= SEQUENCE { + * version [ 0 ] Version DEFAULT v1(0), + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * } + *+ * + */ +public class V1TBSCertificateGenerator +{ + DERTaggedObject version = new DERTaggedObject(true, 0, new ASN1Integer(0)); + + ASN1Integer serialNumber; + AlgorithmIdentifier signature; + X500Name issuer; + Time startDate, endDate; + X500Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + + public V1TBSCertificateGenerator() + { + } + + public void setSerialNumber( + ASN1Integer serialNumber) + { + this.serialNumber = serialNumber; + } + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + /** + * @deprecated use X500Name method + */ + public void setIssuer( + X509Name issuer) + { + this.issuer = X500Name.getInstance(issuer.toASN1Primitive()); + } + + public void setIssuer( + X500Name issuer) + { + this.issuer = issuer; + } + + public void setStartDate( + Time startDate) + { + this.startDate = startDate; + } + + public void setStartDate( + ASN1UTCTime startDate) + { + this.startDate = new Time(startDate); + } + + public void setEndDate( + Time endDate) + { + this.endDate = endDate; + } + + public void setEndDate( + ASN1UTCTime endDate) + { + this.endDate = new Time(endDate); + } + + /** + * @deprecated use X500Name method + */ + public void setSubject( + X509Name subject) + { + this.subject = X500Name.getInstance(subject.toASN1Primitive()); + } + + public void setSubject( + X500Name subject) + { + this.subject = subject; + } + + public void setSubjectPublicKeyInfo( + SubjectPublicKeyInfo pubKeyInfo) + { + this.subjectPublicKeyInfo = pubKeyInfo; + } + + public TBSCertificate generateTBSCertificate() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (subject == null) || (subjectPublicKeyInfo == null)) + { + throw new IllegalStateException("not all mandatory fields set in V1 TBScertificate generator"); + } + + ASN1EncodableVector seq = new ASN1EncodableVector(); + + // seq.add(version); - not required as default value. + seq.add(serialNumber); + seq.add(signature); + seq.add(issuer); + + // + // before and after dates + // + ASN1EncodableVector validity = new ASN1EncodableVector(); + + validity.add(startDate); + validity.add(endDate); + + seq.add(new DERSequence(validity)); + + seq.add(subject); + + seq.add(subjectPublicKeyInfo); + + return TBSCertificate.getInstance(new DERSequence(seq)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java new file mode 100644 index 000000000..934e1c882 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java @@ -0,0 +1,158 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; + +/** + * Generator for Version 2 AttributeCertificateInfo + *
+ * AttributeCertificateInfo ::= SEQUENCE { + * version AttCertVersion -- version is v2, + * holder Holder, + * issuer AttCertIssuer, + * signature AlgorithmIdentifier, + * serialNumber CertificateSerialNumber, + * attrCertValidityPeriod AttCertValidityPeriod, + * attributes SEQUENCE OF Attribute, + * issuerUniqueID UniqueIdentifier OPTIONAL, + * extensions Extensions OPTIONAL + * } + *+ * + */ +public class V2AttributeCertificateInfoGenerator +{ + private ASN1Integer version; + private Holder holder; + private AttCertIssuer issuer; + private AlgorithmIdentifier signature; + private ASN1Integer serialNumber; + private ASN1EncodableVector attributes; + private DERBitString issuerUniqueID; + private Extensions extensions; + + // Note: validity period start/end dates stored directly + //private AttCertValidityPeriod attrCertValidityPeriod; + private ASN1GeneralizedTime startDate, endDate; + + public V2AttributeCertificateInfoGenerator() + { + this.version = new ASN1Integer(1); + attributes = new ASN1EncodableVector(); + } + + public void setHolder(Holder holder) + { + this.holder = holder; + } + + public void addAttribute(String oid, ASN1Encodable value) + { + attributes.add(new Attribute(new ASN1ObjectIdentifier(oid), new DERSet(value))); + } + + /** + * @param attribute + */ + public void addAttribute(Attribute attribute) + { + attributes.add(attribute); + } + + public void setSerialNumber( + ASN1Integer serialNumber) + { + this.serialNumber = serialNumber; + } + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void setIssuer( + AttCertIssuer issuer) + { + this.issuer = issuer; + } + + public void setStartDate( + ASN1GeneralizedTime startDate) + { + this.startDate = startDate; + } + + public void setEndDate( + ASN1GeneralizedTime endDate) + { + this.endDate = endDate; + } + + public void setIssuerUniqueID( + DERBitString issuerUniqueID) + { + this.issuerUniqueID = issuerUniqueID; + } + + /** + * @deprecated use method taking Extensions + * @param extensions + */ + public void setExtensions( + X509Extensions extensions) + { + this.extensions = Extensions.getInstance(extensions.toASN1Primitive()); + } + + public void setExtensions( + Extensions extensions) + { + this.extensions = extensions; + } + + public AttributeCertificateInfo generateAttributeCertificateInfo() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (holder == null) || (attributes == null)) + { + throw new IllegalStateException("not all mandatory fields set in V2 AttributeCertificateInfo generator"); + } + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(holder); + v.add(issuer); + v.add(signature); + v.add(serialNumber); + + // + // before and after dates => AttCertValidityPeriod + // + AttCertValidityPeriod validity = new AttCertValidityPeriod(startDate, endDate); + v.add(validity); + + // Attributes + v.add(new DERSequence(attributes)); + + if (issuerUniqueID != null) + { + v.add(issuerUniqueID); + } + + if (extensions != null) + { + v.add(extensions); + } + + return AttributeCertificateInfo.getInstance(new DERSequence(v)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2Form.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2Form.java new file mode 100644 index 000000000..a94c4a54e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2Form.java @@ -0,0 +1,157 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +public class V2Form + extends ASN1Object +{ + GeneralNames issuerName; + IssuerSerial baseCertificateID; + ObjectDigestInfo objectDigestInfo; + + public static V2Form getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static V2Form getInstance( + Object obj) + { + if (obj instanceof V2Form) + { + return (V2Form)obj; + } + else if (obj != null) + { + return new V2Form(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public V2Form( + GeneralNames issuerName) + { + this(issuerName, null, null); + } + + public V2Form( + GeneralNames issuerName, + IssuerSerial baseCertificateID) + { + this(issuerName, baseCertificateID, null); + } + + public V2Form( + GeneralNames issuerName, + ObjectDigestInfo objectDigestInfo) + { + this(issuerName, null, objectDigestInfo); + } + + public V2Form( + GeneralNames issuerName, + IssuerSerial baseCertificateID, + ObjectDigestInfo objectDigestInfo) + { + this.issuerName = issuerName; + this.baseCertificateID = baseCertificateID; + this.objectDigestInfo = objectDigestInfo; + } + + /** + * @deprecated use getInstance(). + */ + public V2Form( + ASN1Sequence seq) + { + if (seq.size() > 3) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + int index = 0; + + if (!(seq.getObjectAt(0) instanceof ASN1TaggedObject)) + { + index++; + this.issuerName = GeneralNames.getInstance(seq.getObjectAt(0)); + } + + for (int i = index; i != seq.size(); i++) + { + ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(i)); + if (o.getTagNo() == 0) + { + baseCertificateID = IssuerSerial.getInstance(o, false); + } + else if (o.getTagNo() == 1) + { + objectDigestInfo = ObjectDigestInfo.getInstance(o, false); + } + else + { + throw new IllegalArgumentException("Bad tag number: " + + o.getTagNo()); + } + } + } + + public GeneralNames getIssuerName() + { + return issuerName; + } + + public IssuerSerial getBaseCertificateID() + { + return baseCertificateID; + } + + public ObjectDigestInfo getObjectDigestInfo() + { + return objectDigestInfo; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * V2Form ::= SEQUENCE { + * issuerName GeneralNames OPTIONAL, + * baseCertificateID [0] IssuerSerial OPTIONAL, + * objectDigestInfo [1] ObjectDigestInfo OPTIONAL + * -- issuerName MUST be present in this profile + * -- baseCertificateID and objectDigestInfo MUST NOT + * -- be present in this profile + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (issuerName != null) + { + v.add(issuerName); + } + + if (baseCertificateID != null) + { + v.add(new DERTaggedObject(false, 0, baseCertificateID)); + } + + if (objectDigestInfo != null) + { + v.add(new DERTaggedObject(false, 1, objectDigestInfo)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java new file mode 100644 index 000000000..8419470aa --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java @@ -0,0 +1,281 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1UTCTime; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.X500Name; + +/** + * Generator for Version 2 TBSCertList structures. + *
+ * TBSCertList ::= SEQUENCE { + * version Version OPTIONAL, + * -- if present, shall be v2 + * signature AlgorithmIdentifier, + * issuer Name, + * thisUpdate Time, + * nextUpdate Time OPTIONAL, + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate Time, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, shall be v2 + * } OPTIONAL, + * crlExtensions [0] EXPLICIT Extensions OPTIONAL + * -- if present, shall be v2 + * } + *+ * + * Note: This class may be subject to change + */ +public class V2TBSCertListGenerator +{ + private ASN1Integer version = new ASN1Integer(1); + private AlgorithmIdentifier signature; + private X500Name issuer; + private Time thisUpdate, nextUpdate=null; + private Extensions extensions = null; + private ASN1EncodableVector crlentries = new ASN1EncodableVector(); + + private final static ASN1Sequence[] reasons; + + static + { + reasons = new ASN1Sequence[11]; + + reasons[0] = createReasonExtension(CRLReason.unspecified); + reasons[1] = createReasonExtension(CRLReason.keyCompromise); + reasons[2] = createReasonExtension(CRLReason.cACompromise); + reasons[3] = createReasonExtension(CRLReason.affiliationChanged); + reasons[4] = createReasonExtension(CRLReason.superseded); + reasons[5] = createReasonExtension(CRLReason.cessationOfOperation); + reasons[6] = createReasonExtension(CRLReason.certificateHold); + reasons[7] = createReasonExtension(7); // 7 -> unknown + reasons[8] = createReasonExtension(CRLReason.removeFromCRL); + reasons[9] = createReasonExtension(CRLReason.privilegeWithdrawn); + reasons[10] = createReasonExtension(CRLReason.aACompromise); + } + + public V2TBSCertListGenerator() + { + } + + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + /** + * @deprecated use X500Name method + */ + public void setIssuer( + X509Name issuer) + { + this.issuer = X500Name.getInstance(issuer.toASN1Primitive()); + } + + public void setIssuer(X500Name issuer) + { + this.issuer = issuer; + } + + public void setThisUpdate( + ASN1UTCTime thisUpdate) + { + this.thisUpdate = new Time(thisUpdate); + } + + public void setNextUpdate( + ASN1UTCTime nextUpdate) + { + this.nextUpdate = new Time(nextUpdate); + } + + public void setThisUpdate( + Time thisUpdate) + { + this.thisUpdate = thisUpdate; + } + + public void setNextUpdate( + Time nextUpdate) + { + this.nextUpdate = nextUpdate; + } + + public void addCRLEntry( + ASN1Sequence crlEntry) + { + crlentries.add(crlEntry); + } + + public void addCRLEntry(ASN1Integer userCertificate, ASN1UTCTime revocationDate, int reason) + { + addCRLEntry(userCertificate, new Time(revocationDate), reason); + } + + public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, int reason) + { + addCRLEntry(userCertificate, revocationDate, reason, null); + } + + public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, int reason, ASN1GeneralizedTime invalidityDate) + { + if (reason != 0) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (reason < reasons.length) + { + if (reason < 0) + { + throw new IllegalArgumentException("invalid reason value: " + reason); + } + v.add(reasons[reason]); + } + else + { + v.add(createReasonExtension(reason)); + } + + if (invalidityDate != null) + { + v.add(createInvalidityDateExtension(invalidityDate)); + } + + internalAddCRLEntry(userCertificate, revocationDate, new DERSequence(v)); + } + else if (invalidityDate != null) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(createInvalidityDateExtension(invalidityDate)); + + internalAddCRLEntry(userCertificate, revocationDate, new DERSequence(v)); + } + else + { + addCRLEntry(userCertificate, revocationDate, null); + } + } + + private void internalAddCRLEntry(ASN1Integer userCertificate, Time revocationDate, ASN1Sequence extensions) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(userCertificate); + v.add(revocationDate); + + if (extensions != null) + { + v.add(extensions); + } + + addCRLEntry(new DERSequence(v)); + } + + public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, Extensions extensions) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(userCertificate); + v.add(revocationDate); + + if (extensions != null) + { + v.add(extensions); + } + + addCRLEntry(new DERSequence(v)); + } + + public void setExtensions( + X509Extensions extensions) + { + setExtensions(Extensions.getInstance(extensions)); + } + + public void setExtensions( + Extensions extensions) + { + this.extensions = extensions; + } + + public TBSCertList generateTBSCertList() + { + if ((signature == null) || (issuer == null) || (thisUpdate == null)) + { + throw new IllegalStateException("Not all mandatory fields set in V2 TBSCertList generator."); + } + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(signature); + v.add(issuer); + + v.add(thisUpdate); + if (nextUpdate != null) + { + v.add(nextUpdate); + } + + // Add CRLEntries if they exist + if (crlentries.size() != 0) + { + v.add(new DERSequence(crlentries)); + } + + if (extensions != null) + { + v.add(new DERTaggedObject(0, extensions)); + } + + return new TBSCertList(new DERSequence(v)); + } + + private static ASN1Sequence createReasonExtension(int reasonCode) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + CRLReason crlReason = CRLReason.lookup(reasonCode); + + try + { + v.add(Extension.reasonCode); + v.add(new DEROctetString(crlReason.getEncoded())); + } + catch (IOException e) + { + throw new IllegalArgumentException("error encoding reason: " + e); + } + + return new DERSequence(v); + } + + private static ASN1Sequence createInvalidityDateExtension(ASN1GeneralizedTime invalidityDate) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + try + { + v.add(Extension.invalidityDate); + v.add(new DEROctetString(invalidityDate.getEncoded())); + } + catch (IOException e) + { + throw new IllegalArgumentException("error encoding reason: " + e); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java new file mode 100644 index 000000000..b6a3ee73d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java @@ -0,0 +1,212 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.DERUTCTime; +import org.spongycastle.asn1.x500.X500Name; + +/** + * Generator for Version 3 TBSCertificateStructures. + *
+ * TBSCertificate ::= SEQUENCE { + * version [ 0 ] Version DEFAULT v1(0), + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL, + * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL, + * extensions [ 3 ] Extensions OPTIONAL + * } + *+ * + */ +public class V3TBSCertificateGenerator +{ + DERTaggedObject version = new DERTaggedObject(true, 0, new ASN1Integer(2)); + + ASN1Integer serialNumber; + AlgorithmIdentifier signature; + X500Name issuer; + Time startDate, endDate; + X500Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + Extensions extensions; + + private boolean altNamePresentAndCritical; + private DERBitString issuerUniqueID; + private DERBitString subjectUniqueID; + + public V3TBSCertificateGenerator() + { + } + + public void setSerialNumber( + ASN1Integer serialNumber) + { + this.serialNumber = serialNumber; + } + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + /** + * @deprecated use X500Name method + */ + public void setIssuer( + X509Name issuer) + { + this.issuer = X500Name.getInstance(issuer); + } + + public void setIssuer( + X500Name issuer) + { + this.issuer = issuer; + } + + public void setStartDate( + DERUTCTime startDate) + { + this.startDate = new Time(startDate); + } + + public void setStartDate( + Time startDate) + { + this.startDate = startDate; + } + + public void setEndDate( + DERUTCTime endDate) + { + this.endDate = new Time(endDate); + } + + public void setEndDate( + Time endDate) + { + this.endDate = endDate; + } + + /** + * @deprecated use X500Name method + */ + public void setSubject( + X509Name subject) + { + this.subject = X500Name.getInstance(subject.toASN1Primitive()); + } + + public void setSubject( + X500Name subject) + { + this.subject = subject; + } + + public void setIssuerUniqueID( + DERBitString uniqueID) + { + this.issuerUniqueID = uniqueID; + } + + public void setSubjectUniqueID( + DERBitString uniqueID) + { + this.subjectUniqueID = uniqueID; + } + + public void setSubjectPublicKeyInfo( + SubjectPublicKeyInfo pubKeyInfo) + { + this.subjectPublicKeyInfo = pubKeyInfo; + } + + /** + * @deprecated use method taking Extensions + * @param extensions + */ + public void setExtensions( + X509Extensions extensions) + { + setExtensions(Extensions.getInstance(extensions)); + } + + public void setExtensions( + Extensions extensions) + { + this.extensions = extensions; + if (extensions != null) + { + Extension altName = extensions.getExtension(Extension.subjectAlternativeName); + + if (altName != null && altName.isCritical()) + { + altNamePresentAndCritical = true; + } + } + } + + public TBSCertificate generateTBSCertificate() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null)) + { + throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator"); + } + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(serialNumber); + v.add(signature); + v.add(issuer); + + // + // before and after dates + // + ASN1EncodableVector validity = new ASN1EncodableVector(); + + validity.add(startDate); + validity.add(endDate); + + v.add(new DERSequence(validity)); + + if (subject != null) + { + v.add(subject); + } + else + { + v.add(new DERSequence()); + } + + v.add(subjectPublicKeyInfo); + + if (issuerUniqueID != null) + { + v.add(new DERTaggedObject(false, 1, issuerUniqueID)); + } + + if (subjectUniqueID != null) + { + v.add(new DERTaggedObject(false, 2, subjectUniqueID)); + } + + if (extensions != null) + { + v.add(new DERTaggedObject(true, 3, extensions)); + } + + return TBSCertificate.getInstance(new DERSequence(v)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java new file mode 100644 index 000000000..28669a711 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java @@ -0,0 +1,29 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public interface X509AttributeIdentifiers +{ + /** + * @deprecated use id_at_role + */ + static final ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72"); + + static final ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4"); + static final ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6"); + static final ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10"); + + static final ASN1ObjectIdentifier id_ce_targetInformation= X509ObjectIdentifiers.id_ce.branch("55"); + + static final ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10"); + + static final ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1"); + static final ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2"); + static final ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3"); + static final ASN1ObjectIdentifier id_aca_group = id_aca.branch("4"); + // { id-aca 5 } is reserved + static final ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6"); + + static final ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72"); + static final ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java new file mode 100644 index 000000000..b75117927 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java @@ -0,0 +1,129 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x500.X500Name; + +/** + * an X509Certificate structure. + *
+ * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + *+ * @deprecated use org.spongycastle.asn1.x509.Certificate + */ +public class X509CertificateStructure + extends ASN1Object + implements X509ObjectIdentifiers, PKCSObjectIdentifiers +{ + ASN1Sequence seq; + TBSCertificateStructure tbsCert; + AlgorithmIdentifier sigAlgId; + DERBitString sig; + + public static X509CertificateStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static X509CertificateStructure getInstance( + Object obj) + { + if (obj instanceof X509CertificateStructure) + { + return (X509CertificateStructure)obj; + } + else if (obj != null) + { + return new X509CertificateStructure(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public X509CertificateStructure( + ASN1Sequence seq) + { + this.seq = seq; + + // + // correct x509 certficate + // + if (seq.size() == 3) + { + tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0)); + sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + + sig = DERBitString.getInstance(seq.getObjectAt(2)); + } + else + { + throw new IllegalArgumentException("sequence wrong size for a certificate"); + } + } + + public TBSCertificateStructure getTBSCertificate() + { + return tbsCert; + } + + public int getVersion() + { + return tbsCert.getVersion(); + } + + public ASN1Integer getSerialNumber() + { + return tbsCert.getSerialNumber(); + } + + public X500Name getIssuer() + { + return tbsCert.getIssuer(); + } + + public Time getStartDate() + { + return tbsCert.getStartDate(); + } + + public Time getEndDate() + { + return tbsCert.getEndDate(); + } + + public X500Name getSubject() + { + return tbsCert.getSubject(); + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return tbsCert.getSubjectPublicKeyInfo(); + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return sigAlgId; + } + + public DERBitString getSignature() + { + return sig; + } + + public ASN1Primitive toASN1Primitive() + { + return seq; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java new file mode 100644 index 000000000..38685b413 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java @@ -0,0 +1,65 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERGeneralizedTime; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERUTF8String; + +/** + * The default converter for X509 DN entries when going from their + * string value to ASN.1 strings. + */ +public class X509DefaultEntryConverter + extends X509NameEntryConverter +{ + /** + * Apply default coversion for the given value depending on the oid + * and the character range of the value. + * + * @param oid the object identifier for the DN entry + * @param value the value associated with it + * @return the ASN.1 equivalent for the string value. + */ + public ASN1Primitive getConvertedValue( + ASN1ObjectIdentifier oid, + String value) + { + if (value.length() != 0 && value.charAt(0) == '#') + { + try + { + return convertHexEncoded(value, 1); + } + catch (IOException e) + { + throw new RuntimeException("can't recode value for oid " + oid.getId()); + } + } + else + { + if (value.length() != 0 && value.charAt(0) == '\\') + { + value = value.substring(1); + } + if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC)) + { + return new DERIA5String(value); + } + else if (oid.equals(X509Name.DATE_OF_BIRTH)) // accept time string as well as # (for compatibility) + { + return new DERGeneralizedTime(value); + } + else if (oid.equals(X509Name.C) || oid.equals(X509Name.SN) || oid.equals(X509Name.DN_QUALIFIER) + || oid.equals(X509Name.TELEPHONE_NUMBER)) + { + return new DERPrintableString(value); + } + } + + return new DERUTF8String(value); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java new file mode 100644 index 000000000..8a7defa95 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java @@ -0,0 +1,249 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBoolean; + +/** + * an object for the elements in the X.509 V3 extension block. + * @deprecated use Extension + */ +public class X509Extension +{ + /** + * Subject Directory Attributes + */ + public static final ASN1ObjectIdentifier subjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9"); + + /** + * Subject Key Identifier + */ + public static final ASN1ObjectIdentifier subjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14"); + + /** + * Key Usage + */ + public static final ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15"); + + /** + * Private Key Usage Period + */ + public static final ASN1ObjectIdentifier privateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16"); + + /** + * Subject Alternative Name + */ + public static final ASN1ObjectIdentifier subjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17"); + + /** + * Issuer Alternative Name + */ + public static final ASN1ObjectIdentifier issuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18"); + + /** + * Basic Constraints + */ + public static final ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19"); + + /** + * CRL Number + */ + public static final ASN1ObjectIdentifier cRLNumber = new ASN1ObjectIdentifier("2.5.29.20"); + + /** + * Reason code + */ + public static final ASN1ObjectIdentifier reasonCode = new ASN1ObjectIdentifier("2.5.29.21"); + + /** + * Hold Instruction Code + */ + public static final ASN1ObjectIdentifier instructionCode = new ASN1ObjectIdentifier("2.5.29.23"); + + /** + * Invalidity Date + */ + public static final ASN1ObjectIdentifier invalidityDate = new ASN1ObjectIdentifier("2.5.29.24"); + + /** + * Delta CRL indicator + */ + public static final ASN1ObjectIdentifier deltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27"); + + /** + * Issuing Distribution Point + */ + public static final ASN1ObjectIdentifier issuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28"); + + /** + * Certificate Issuer + */ + public static final ASN1ObjectIdentifier certificateIssuer = new ASN1ObjectIdentifier("2.5.29.29"); + + /** + * Name Constraints + */ + public static final ASN1ObjectIdentifier nameConstraints = new ASN1ObjectIdentifier("2.5.29.30"); + + /** + * CRL Distribution Points + */ + public static final ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31"); + + /** + * Certificate Policies + */ + public static final ASN1ObjectIdentifier certificatePolicies = new ASN1ObjectIdentifier("2.5.29.32"); + + /** + * Policy Mappings + */ + public static final ASN1ObjectIdentifier policyMappings = new ASN1ObjectIdentifier("2.5.29.33"); + + /** + * Authority Key Identifier + */ + public static final ASN1ObjectIdentifier authorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35"); + + /** + * Policy Constraints + */ + public static final ASN1ObjectIdentifier policyConstraints = new ASN1ObjectIdentifier("2.5.29.36"); + + /** + * Extended Key Usage + */ + public static final ASN1ObjectIdentifier extendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37"); + + /** + * Freshest CRL + */ + public static final ASN1ObjectIdentifier freshestCRL = new ASN1ObjectIdentifier("2.5.29.46"); + + /** + * Inhibit Any Policy + */ + public static final ASN1ObjectIdentifier inhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54"); + + /** + * Authority Info Access + */ + public static final ASN1ObjectIdentifier authorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1"); + + /** + * Subject Info Access + */ + public static final ASN1ObjectIdentifier subjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11"); + + /** + * Logo Type + */ + public static final ASN1ObjectIdentifier logoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12"); + + /** + * BiometricInfo + */ + public static final ASN1ObjectIdentifier biometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2"); + + /** + * QCStatements + */ + public static final ASN1ObjectIdentifier qCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3"); + + /** + * Audit identity extension in attribute certificates. + */ + public static final ASN1ObjectIdentifier auditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4"); + + /** + * NoRevAvail extension in attribute certificates. + */ + public static final ASN1ObjectIdentifier noRevAvail = new ASN1ObjectIdentifier("2.5.29.56"); + + /** + * TargetInformation extension in attribute certificates. + */ + public static final ASN1ObjectIdentifier targetInformation = new ASN1ObjectIdentifier("2.5.29.55"); + + boolean critical; + ASN1OctetString value; + + public X509Extension( + DERBoolean critical, + ASN1OctetString value) + { + this.critical = critical.isTrue(); + this.value = value; + } + + public X509Extension( + boolean critical, + ASN1OctetString value) + { + this.critical = critical; + this.value = value; + } + + public boolean isCritical() + { + return critical; + } + + public ASN1OctetString getValue() + { + return value; + } + + public ASN1Encodable getParsedValue() + { + return convertValueToObject(this); + } + + public int hashCode() + { + if (this.isCritical()) + { + return this.getValue().hashCode(); + } + + return ~this.getValue().hashCode(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof X509Extension)) + { + return false; + } + + X509Extension other = (X509Extension)o; + + return other.getValue().equals(this.getValue()) + && (other.isCritical() == this.isCritical()); + } + + /** + * Convert the value of the passed in extension to an object + * @param ext the extension to parse + * @return the object the value string contains + * @exception IllegalArgumentException if conversion is not possible + */ + public static ASN1Primitive convertValueToObject( + X509Extension ext) + throws IllegalArgumentException + { + try + { + return ASN1Primitive.fromByteArray(ext.getValue().getOctets()); + } + catch (IOException e) + { + throw new IllegalArgumentException("can't convert extension: " + e); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java new file mode 100644 index 000000000..edf116014 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java @@ -0,0 +1,489 @@ +package org.spongycastle.asn1.x509; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBoolean; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DERSequence; + +/** + * @deprecated use Extensions + */ +public class X509Extensions + extends ASN1Object +{ + /** + * Subject Directory Attributes + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier SubjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9"); + + /** + * Subject Key Identifier + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier SubjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14"); + + /** + * Key Usage + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier KeyUsage = new ASN1ObjectIdentifier("2.5.29.15"); + + /** + * Private Key Usage Period + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier PrivateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16"); + + /** + * Subject Alternative Name + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier SubjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17"); + + /** + * Issuer Alternative Name + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier IssuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18"); + + /** + * Basic Constraints + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier BasicConstraints = new ASN1ObjectIdentifier("2.5.29.19"); + + /** + * CRL Number + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier CRLNumber = new ASN1ObjectIdentifier("2.5.29.20"); + + /** + * Reason code + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier ReasonCode = new ASN1ObjectIdentifier("2.5.29.21"); + + /** + * Hold Instruction Code + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier InstructionCode = new ASN1ObjectIdentifier("2.5.29.23"); + + /** + * Invalidity Date + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier InvalidityDate = new ASN1ObjectIdentifier("2.5.29.24"); + + /** + * Delta CRL indicator + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier DeltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27"); + + /** + * Issuing Distribution Point + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier IssuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28"); + + /** + * Certificate Issuer + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier CertificateIssuer = new ASN1ObjectIdentifier("2.5.29.29"); + + /** + * Name Constraints + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier NameConstraints = new ASN1ObjectIdentifier("2.5.29.30"); + + /** + * CRL Distribution Points + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier CRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31"); + + /** + * Certificate Policies + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier CertificatePolicies = new ASN1ObjectIdentifier("2.5.29.32"); + + /** + * Policy Mappings + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier PolicyMappings = new ASN1ObjectIdentifier("2.5.29.33"); + + /** + * Authority Key Identifier + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier AuthorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35"); + + /** + * Policy Constraints + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier PolicyConstraints = new ASN1ObjectIdentifier("2.5.29.36"); + + /** + * Extended Key Usage + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier ExtendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37"); + + /** + * Freshest CRL + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier FreshestCRL = new ASN1ObjectIdentifier("2.5.29.46"); + + /** + * Inhibit Any Policy + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier InhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54"); + + /** + * Authority Info Access + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier AuthorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1"); + + /** + * Subject Info Access + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier SubjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11"); + + /** + * Logo Type + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier LogoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12"); + + /** + * BiometricInfo + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier BiometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2"); + + /** + * QCStatements + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier QCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3"); + + /** + * Audit identity extension in attribute certificates. + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier AuditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4"); + + /** + * NoRevAvail extension in attribute certificates. + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier NoRevAvail = new ASN1ObjectIdentifier("2.5.29.56"); + + /** + * TargetInformation extension in attribute certificates. + * @deprecated use X509Extension value. + */ + public static final ASN1ObjectIdentifier TargetInformation = new ASN1ObjectIdentifier("2.5.29.55"); + + private Hashtable extensions = new Hashtable(); + private Vector ordering = new Vector(); + + public static X509Extensions getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static X509Extensions getInstance( + Object obj) + { + if (obj == null || obj instanceof X509Extensions) + { + return (X509Extensions)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new X509Extensions((ASN1Sequence)obj); + } + + if (obj instanceof Extensions) + { + return new X509Extensions((ASN1Sequence)((Extensions)obj).toASN1Primitive()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString) + */ + public X509Extensions( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement()); + + if (s.size() == 3) + { + extensions.put(s.getObjectAt(0), new X509Extension(DERBoolean.getInstance(s.getObjectAt(1)), ASN1OctetString.getInstance(s.getObjectAt(2)))); + } + else if (s.size() == 2) + { + extensions.put(s.getObjectAt(0), new X509Extension(false, ASN1OctetString.getInstance(s.getObjectAt(1)))); + } + else + { + throw new IllegalArgumentException("Bad sequence size: " + s.size()); + } + + ordering.addElement(s.getObjectAt(0)); + } + } + + /** + * constructor from a table of extensions. + *
+ * it's is assumed the table contains OID/String pairs. + */ + public X509Extensions( + Hashtable extensions) + { + this(null, extensions); + } + + /** + * Constructor from a table of extensions with ordering. + *
+ * It's is assumed the table contains OID/String pairs. + * @deprecated use Extensions + */ + public X509Extensions( + Vector ordering, + Hashtable extensions) + { + Enumeration e; + + if (ordering == null) + { + e = extensions.keys(); + } + else + { + e = ordering.elements(); + } + + while (e.hasMoreElements()) + { + this.ordering.addElement(ASN1ObjectIdentifier.getInstance(e.nextElement())); + } + + e = this.ordering.elements(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(e.nextElement()); + X509Extension ext = (X509Extension)extensions.get(oid); + + this.extensions.put(oid, ext); + } + } + + /** + * Constructor from two vectors + * + * @param objectIDs a vector of the object identifiers. + * @param values a vector of the extension values. + * @deprecated use Extensions + */ + public X509Extensions( + Vector objectIDs, + Vector values) + { + Enumeration e = objectIDs.elements(); + + while (e.hasMoreElements()) + { + this.ordering.addElement(e.nextElement()); + } + + int count = 0; + + e = this.ordering.elements(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + X509Extension ext = (X509Extension)values.elementAt(count); + + this.extensions.put(oid, ext); + count++; + } + } + + /** + * return an Enumeration of the extension field's object ids. + */ + public Enumeration oids() + { + return ordering.elements(); + } + + /** + * return the extension represented by the object identifier + * passed in. + * + * @return the extension if it's present, null otherwise. + */ + public X509Extension getExtension( + DERObjectIdentifier oid) + { + return (X509Extension)extensions.get(oid); + } + + /** + * @deprecated + * @param oid + * @return + */ + public X509Extension getExtension( + ASN1ObjectIdentifier oid) + { + return (X509Extension)extensions.get(oid); + } + + /** + *
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnId EXTENSION.&id ({ExtensionSet}), + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + Enumeration e = ordering.elements(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + X509Extension ext = (X509Extension)extensions.get(oid); + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(oid); + + if (ext.isCritical()) + { + v.add(DERBoolean.TRUE); + } + + v.add(ext.getValue()); + + vec.add(new DERSequence(v)); + } + + return new DERSequence(vec); + } + + public boolean equivalent( + X509Extensions other) + { + if (extensions.size() != other.extensions.size()) + { + return false; + } + + Enumeration e1 = extensions.keys(); + + while (e1.hasMoreElements()) + { + Object key = e1.nextElement(); + + if (!extensions.get(key).equals(other.extensions.get(key))) + { + return false; + } + } + + return true; + } + + public ASN1ObjectIdentifier[] getExtensionOIDs() + { + return toOidArray(ordering); + } + + public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs() + { + return getExtensionOIDs(false); + } + + public ASN1ObjectIdentifier[] getCriticalExtensionOIDs() + { + return getExtensionOIDs(true); + } + + private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical) + { + Vector oidVec = new Vector(); + + for (int i = 0; i != ordering.size(); i++) + { + Object oid = ordering.elementAt(i); + + if (((X509Extension)extensions.get(oid)).isCritical() == isCritical) + { + oidVec.addElement(oid); + } + } + + return toOidArray(oidVec); + } + + private ASN1ObjectIdentifier[] toOidArray(Vector oidVec) + { + ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()]; + + for (int i = 0; i != oids.length; i++) + { + oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i); + } + return oids; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java new file mode 100644 index 000000000..16c075274 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java @@ -0,0 +1,117 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DEROctetString; + +/** + * Generator for X.509 extensions + * @deprecated use org.spongycastle.asn1.x509.ExtensionsGenerator + */ +public class X509ExtensionsGenerator +{ + private Hashtable extensions = new Hashtable(); + private Vector extOrdering = new Vector(); + + /** + * Reset the generator + */ + public void reset() + { + extensions = new Hashtable(); + extOrdering = new Vector(); + } + + /** + * @deprecated use ASN1ObjectIdentifier + */ + public void addExtension( + DERObjectIdentifier oid, + boolean critical, + ASN1Encodable value) + { + addExtension(new ASN1ObjectIdentifier(oid.getId()), critical, value); + } + + /** + * @deprecated use ASN1ObjectIdentifier + */ + public void addExtension( + DERObjectIdentifier oid, + boolean critical, + byte[] value) + { + addExtension(new ASN1ObjectIdentifier(oid.getId()), critical, value); + } + + /** + * Add an extension with the given oid and the passed in value to be included + * in the OCTET STRING associated with the extension. + * + * @param oid OID for the extension. + * @param critical true if critical, false otherwise. + * @param value the ASN.1 object to be included in the extension. + */ + public void addExtension( + ASN1ObjectIdentifier oid, + boolean critical, + ASN1Encodable value) + { + try + { + this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + catch (IOException e) + { + throw new IllegalArgumentException("error encoding value: " + e); + } + } + + /** + * Add an extension with the given oid and the passed in byte array to be wrapped in the + * OCTET STRING associated with the extension. + * + * @param oid OID for the extension. + * @param critical true if critical, false otherwise. + * @param value the byte array to be wrapped. + */ + public void addExtension( + ASN1ObjectIdentifier oid, + boolean critical, + byte[] value) + { + if (extensions.containsKey(oid)) + { + throw new IllegalArgumentException("extension " + oid + " already added"); + } + + extOrdering.addElement(oid); + extensions.put(oid, new X509Extension(critical, new DEROctetString(value))); + } + + /** + * Return true if there are no extension present in this generator. + * + * @return true if empty, false otherwise + */ + public boolean isEmpty() + { + return extOrdering.isEmpty(); + } + + /** + * Generate an X509Extensions object based on the current state of the generator. + * + * @return an X09Extensions object. + */ + public X509Extensions generate() + { + return new X509Extensions(extOrdering, extensions); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Name.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Name.java new file mode 100644 index 000000000..7d2fedf88 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509Name.java @@ -0,0 +1,1379 @@ +package org.spongycastle.asn1.x509; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; +import org.spongycastle.asn1.DERUniversalString; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; + +/** + *
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * value ANY } + *+ * @deprecated use org.spongycastle.asn1.x500.X500Name. + */ +public class X509Name + extends ASN1Object +{ + /** + * country code - StringType(SIZE(2)) + * @deprecated use a X500NameStyle + */ + public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6"); + + /** + * organization - StringType(SIZE(1..64)) + * @deprecated use a X500NameStyle + */ + public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10"); + + /** + * organizational unit name - StringType(SIZE(1..64)) + * @deprecated use a X500NameStyle + */ + public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11"); + + /** + * Title + * @deprecated use a X500NameStyle + */ + public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12"); + + /** + * common name - StringType(SIZE(1..64)) + * @deprecated use a X500NameStyle + */ + public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3"); + + /** + * device serial number name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5"); + + /** + * street - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9"); + + /** + * device serial number name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier SERIALNUMBER = SN; + + /** + * locality name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7"); + + /** + * state, or province name - StringType(SIZE(1..64)) + */ + public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8"); + + /** + * Naming attributes of type X520name + */ + public static final ASN1ObjectIdentifier SURNAME = new ASN1ObjectIdentifier("2.5.4.4"); + public static final ASN1ObjectIdentifier GIVENNAME = new ASN1ObjectIdentifier("2.5.4.42"); + public static final ASN1ObjectIdentifier INITIALS = new ASN1ObjectIdentifier("2.5.4.43"); + public static final ASN1ObjectIdentifier GENERATION = new ASN1ObjectIdentifier("2.5.4.44"); + public static final ASN1ObjectIdentifier UNIQUE_IDENTIFIER = new ASN1ObjectIdentifier("2.5.4.45"); + + /** + * businessCategory - DirectoryString(SIZE(1..128) + */ + public static final ASN1ObjectIdentifier BUSINESS_CATEGORY = new ASN1ObjectIdentifier( + "2.5.4.15"); + + /** + * postalCode - DirectoryString(SIZE(1..40) + */ + public static final ASN1ObjectIdentifier POSTAL_CODE = new ASN1ObjectIdentifier( + "2.5.4.17"); + + /** + * dnQualifier - DirectoryString(SIZE(1..64) + */ + public static final ASN1ObjectIdentifier DN_QUALIFIER = new ASN1ObjectIdentifier( + "2.5.4.46"); + + /** + * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64) + */ + public static final ASN1ObjectIdentifier PSEUDONYM = new ASN1ObjectIdentifier( + "2.5.4.65"); + + + /** + * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z + */ + public static final ASN1ObjectIdentifier DATE_OF_BIRTH = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.1"); + + /** + * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128) + */ + public static final ASN1ObjectIdentifier PLACE_OF_BIRTH = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.2"); + + /** + * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f" + */ + public static final ASN1ObjectIdentifier GENDER = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.3"); + + /** + * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166 + * codes only + */ + public static final ASN1ObjectIdentifier COUNTRY_OF_CITIZENSHIP = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.4"); + + /** + * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166 + * codes only + */ + public static final ASN1ObjectIdentifier COUNTRY_OF_RESIDENCE = new ASN1ObjectIdentifier( + "1.3.6.1.5.5.7.9.5"); + + + /** + * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64) + */ + public static final ASN1ObjectIdentifier NAME_AT_BIRTH = new ASN1ObjectIdentifier("1.3.36.8.3.14"); + + /** + * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF + * DirectoryString(SIZE(1..30)) + */ + public static final ASN1ObjectIdentifier POSTAL_ADDRESS = new ASN1ObjectIdentifier("2.5.4.16"); + + /** + * RFC 2256 dmdName + */ + public static final ASN1ObjectIdentifier DMD_NAME = new ASN1ObjectIdentifier("2.5.4.54"); + + /** + * id-at-telephoneNumber + */ + public static final ASN1ObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber; + + /** + * id-at-name + */ + public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name; + + /** + * Email address (RSA PKCS#9 extension) - IA5String. + *
Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here. + * @deprecated use a X500NameStyle + */ + public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress; + + /** + * more from PKCS#9 + */ + public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName; + public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress; + + /** + * email address in Verisign certificates + */ + public static final ASN1ObjectIdentifier E = EmailAddress; + + /* + * others... + */ + public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25"); + + /** + * LDAP User id. + */ + public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1"); + + /** + * determines whether or not strings should be processed and printed + * from back to front. + */ + public static boolean DefaultReverse = false; + + /** + * default look up table translating OID values into their common symbols following + * the convention in RFC 2253 with a few extras + */ + public static final Hashtable DefaultSymbols = new Hashtable(); + + /** + * look up table translating OID values into their common symbols following the convention in RFC 2253 + * + */ + public static final Hashtable RFC2253Symbols = new Hashtable(); + + /** + * look up table translating OID values into their common symbols following the convention in RFC 1779 + * + */ + public static final Hashtable RFC1779Symbols = new Hashtable(); + + /** + * look up table translating common symbols into their OIDS. + */ + public static final Hashtable DefaultLookUp = new Hashtable(); + + /** + * look up table translating OID values into their common symbols + * @deprecated use DefaultSymbols + */ + public static final Hashtable OIDLookUp = DefaultSymbols; + + /** + * look up table translating string values into their OIDS - + * @deprecated use DefaultLookUp + */ + public static final Hashtable SymbolLookUp = DefaultLookUp; + + private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility + private static final Boolean FALSE = new Boolean(false); + + static + { + DefaultSymbols.put(C, "C"); + DefaultSymbols.put(O, "O"); + DefaultSymbols.put(T, "T"); + DefaultSymbols.put(OU, "OU"); + DefaultSymbols.put(CN, "CN"); + DefaultSymbols.put(L, "L"); + DefaultSymbols.put(ST, "ST"); + DefaultSymbols.put(SN, "SERIALNUMBER"); + DefaultSymbols.put(EmailAddress, "E"); + DefaultSymbols.put(DC, "DC"); + DefaultSymbols.put(UID, "UID"); + DefaultSymbols.put(STREET, "STREET"); + DefaultSymbols.put(SURNAME, "SURNAME"); + DefaultSymbols.put(GIVENNAME, "GIVENNAME"); + DefaultSymbols.put(INITIALS, "INITIALS"); + DefaultSymbols.put(GENERATION, "GENERATION"); + DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress"); + DefaultSymbols.put(UnstructuredName, "unstructuredName"); + DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier"); + DefaultSymbols.put(DN_QUALIFIER, "DN"); + DefaultSymbols.put(PSEUDONYM, "Pseudonym"); + DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress"); + DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth"); + DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship"); + DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence"); + DefaultSymbols.put(GENDER, "Gender"); + DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth"); + DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth"); + DefaultSymbols.put(POSTAL_CODE, "PostalCode"); + DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory"); + DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber"); + DefaultSymbols.put(NAME, "Name"); + + RFC2253Symbols.put(C, "C"); + RFC2253Symbols.put(O, "O"); + RFC2253Symbols.put(OU, "OU"); + RFC2253Symbols.put(CN, "CN"); + RFC2253Symbols.put(L, "L"); + RFC2253Symbols.put(ST, "ST"); + RFC2253Symbols.put(STREET, "STREET"); + RFC2253Symbols.put(DC, "DC"); + RFC2253Symbols.put(UID, "UID"); + + RFC1779Symbols.put(C, "C"); + RFC1779Symbols.put(O, "O"); + RFC1779Symbols.put(OU, "OU"); + RFC1779Symbols.put(CN, "CN"); + RFC1779Symbols.put(L, "L"); + RFC1779Symbols.put(ST, "ST"); + RFC1779Symbols.put(STREET, "STREET"); + + DefaultLookUp.put("c", C); + DefaultLookUp.put("o", O); + DefaultLookUp.put("t", T); + DefaultLookUp.put("ou", OU); + DefaultLookUp.put("cn", CN); + DefaultLookUp.put("l", L); + DefaultLookUp.put("st", ST); + DefaultLookUp.put("sn", SN); + DefaultLookUp.put("serialnumber", SN); + DefaultLookUp.put("street", STREET); + DefaultLookUp.put("emailaddress", E); + DefaultLookUp.put("dc", DC); + DefaultLookUp.put("e", E); + DefaultLookUp.put("uid", UID); + DefaultLookUp.put("surname", SURNAME); + DefaultLookUp.put("givenname", GIVENNAME); + DefaultLookUp.put("initials", INITIALS); + DefaultLookUp.put("generation", GENERATION); + DefaultLookUp.put("unstructuredaddress", UnstructuredAddress); + DefaultLookUp.put("unstructuredname", UnstructuredName); + DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER); + DefaultLookUp.put("dn", DN_QUALIFIER); + DefaultLookUp.put("pseudonym", PSEUDONYM); + DefaultLookUp.put("postaladdress", POSTAL_ADDRESS); + DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH); + DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP); + DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE); + DefaultLookUp.put("gender", GENDER); + DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH); + DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH); + DefaultLookUp.put("postalcode", POSTAL_CODE); + DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY); + DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER); + DefaultLookUp.put("name", NAME); + } + + private X509NameEntryConverter converter = null; + private Vector ordering = new Vector(); + private Vector values = new Vector(); + private Vector added = new Vector(); + + private ASN1Sequence seq; + + private boolean isHashCodeCalculated; + private int hashCodeValue; + + /** + * Return a X509Name based on the passed in tagged object. + * + * @param obj tag object holding name. + * @param explicit true if explicitly tagged false otherwise. + * @return the X509Name + */ + public static X509Name getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static X509Name getInstance( + Object obj) + { + if (obj == null || obj instanceof X509Name) + { + return (X509Name)obj; + } + else if (obj instanceof X500Name) + { + return new X509Name(ASN1Sequence.getInstance(((X500Name)obj).toASN1Primitive())); + } + else if (obj != null) + { + return new X509Name(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + protected X509Name() + { + // constructure use by new X500 Name class + } + /** + * Constructor from ASN1Sequence + * + * the principal will be a list of constructed sets, each containing an (OID, String) pair. + * @deprecated use X500Name.getInstance() + */ + public X509Name( + ASN1Sequence seq) + { + this.seq = seq; + + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Set set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive()); + + for (int i = 0; i < set.size(); i++) + { + ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive()); + + if (s.size() != 2) + { + throw new IllegalArgumentException("badly sized pair"); + } + + ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0))); + + ASN1Encodable value = s.getObjectAt(1); + if (value instanceof ASN1String && !(value instanceof DERUniversalString)) + { + String v = ((ASN1String)value).getString(); + if (v.length() > 0 && v.charAt(0) == '#') + { + values.addElement("\\" + v); + } + else + { + values.addElement(v); + } + } + else + { + try + { + values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)))); + } + catch (IOException e1) + { + throw new IllegalArgumentException("cannot encode value"); + } + } + added.addElement((i != 0) ? TRUE : FALSE); // to allow earlier JDK compatibility + } + } + } + + /** + * constructor from a table of attributes. + *
+ * it's is assumed the table contains OID/String pairs, and the contents + * of the table are copied into an internal table as part of the + * construction process. + *
+ * Note: if the name you are trying to generate should be + * following a specific ordering, you should use the constructor + * with the ordering specified below. + * @deprecated use an ordered constructor! The hashtable ordering is rarely correct + */ + public X509Name( + Hashtable attributes) + { + this(null, attributes); + } + + /** + * Constructor from a table of attributes with ordering. + *
+ * it's is assumed the table contains OID/String pairs, and the contents + * of the table are copied into an internal table as part of the + * construction process. The ordering vector should contain the OIDs + * in the order they are meant to be encoded or printed in toString. + */ + public X509Name( + Vector ordering, + Hashtable attributes) + { + this(ordering, attributes, new X509DefaultEntryConverter()); + } + + /** + * Constructor from a table of attributes with ordering. + *
+ * it's is assumed the table contains OID/String pairs, and the contents + * of the table are copied into an internal table as part of the + * construction process. The ordering vector should contain the OIDs + * in the order they are meant to be encoded or printed in toString. + *
+ * The passed in converter will be used to convert the strings into their + * ASN.1 counterparts. + * @deprecated use X500Name, X500NameBuilder + */ + public X509Name( + Vector ordering, + Hashtable attributes, + X509NameEntryConverter converter) + { + this.converter = converter; + + if (ordering != null) + { + for (int i = 0; i != ordering.size(); i++) + { + this.ordering.addElement(ordering.elementAt(i)); + this.added.addElement(FALSE); + } + } + else + { + Enumeration e = attributes.keys(); + + while (e.hasMoreElements()) + { + this.ordering.addElement(e.nextElement()); + this.added.addElement(FALSE); + } + } + + for (int i = 0; i != this.ordering.size(); i++) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)this.ordering.elementAt(i); + + if (attributes.get(oid) == null) + { + throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name"); + } + + this.values.addElement(attributes.get(oid)); // copy the hash table + } + } + + /** + * Takes two vectors one of the oids and the other of the values. + * @deprecated use X500Name, X500NameBuilder + */ + public X509Name( + Vector oids, + Vector values) + { + this(oids, values, new X509DefaultEntryConverter()); + } + + /** + * Takes two vectors one of the oids and the other of the values. + *
+ * The passed in converter will be used to convert the strings into their
+ * ASN.1 counterparts.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector oids,
+ Vector values,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+
+ if (oids.size() != values.size())
+ {
+ throw new IllegalArgumentException("oids vector must be same length as values.");
+ }
+
+ for (int i = 0; i < oids.size(); i++)
+ {
+ this.ordering.addElement(oids.elementAt(i));
+ this.values.addElement(values.elementAt(i));
+ this.added.addElement(FALSE);
+ }
+ }
+
+// private Boolean isEncoded(String s)
+// {
+// if (s.charAt(0) == '#')
+// {
+// return TRUE;
+// }
+//
+// return FALSE;
+// }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ String dirName)
+ {
+ this(DefaultReverse, DefaultLookUp, dirName);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes with each
+ * string value being converted to its associated ASN.1 type using the passed
+ * in converter.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this(DefaultReverse, DefaultLookUp, dirName, converter);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes. If reverse
+ * is true, create the encoded version of the sequence starting from the
+ * last element in the string.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ String dirName)
+ {
+ this(reverse, DefaultLookUp, dirName);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes with each
+ * string value being converted to its associated ASN.1 type using the passed
+ * in converter. If reverse is true the ASN.1 sequence representing the DN will
+ * be built by starting at the end of the string, rather than the start.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this(reverse, DefaultLookUp, dirName, converter);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes. lookUp
+ * should provide a table of lookups, indexed by lowercase only strings and
+ * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
+ * will be processed automatically.
+ *
+ * If reverse is true, create the encoded version of the sequence
+ * starting from the last element in the string.
+ * @param reverse true if we should start scanning from the end (RFC 2553).
+ * @param lookUp table of names and their oids.
+ * @param dirName the X.500 string to be parsed.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ Hashtable lookUp,
+ String dirName)
+ {
+ this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
+ }
+
+ private ASN1ObjectIdentifier decodeOID(
+ String name,
+ Hashtable lookUp)
+ {
+ name = name.trim();
+ if (Strings.toUpperCase(name).startsWith("OID."))
+ {
+ return new ASN1ObjectIdentifier(name.substring(4));
+ }
+ else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
+ {
+ return new ASN1ObjectIdentifier(name);
+ }
+
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
+ if (oid == null)
+ {
+ throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
+ }
+
+ return oid;
+ }
+
+ private String unescape(String elt)
+ {
+ if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+ {
+ return elt.trim();
+ }
+
+ char[] elts = elt.toCharArray();
+ boolean escaped = false;
+ boolean quoted = false;
+ StringBuffer buf = new StringBuffer(elt.length());
+ int start = 0;
+
+ // if it's an escaped hash string and not an actual encoding in string form
+ // we need to leave it escaped.
+ if (elts[0] == '\\')
+ {
+ if (elts[1] == '#')
+ {
+ start = 2;
+ buf.append("\\#");
+ }
+ }
+
+ boolean nonWhiteSpaceEncountered = false;
+ int lastEscaped = 0;
+
+ for (int i = start; i != elts.length; i++)
+ {
+ char c = elts[i];
+
+ if (c != ' ')
+ {
+ nonWhiteSpaceEncountered = true;
+ }
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ escaped = false;
+ }
+ else if (c == '\\' && !(escaped || quoted))
+ {
+ escaped = true;
+ lastEscaped = buf.length();
+ }
+ else
+ {
+ if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
+ {
+ continue;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ }
+
+ if (buf.length() > 0)
+ {
+ while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
+ {
+ buf.setLength(buf.length() - 1);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes. lookUp
+ * should provide a table of lookups, indexed by lowercase only strings and
+ * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
+ * will be processed automatically. The passed in converter is used to convert the
+ * string values to the right of each equals sign to their ASN.1 counterparts.
+ *
+ * @param reverse true if we should start scanning from the end, false otherwise.
+ * @param lookUp table of names and oids.
+ * @param dirName the string dirName
+ * @param converter the converter to convert string values into their ASN.1 equivalents
+ */
+ public X509Name(
+ boolean reverse,
+ Hashtable lookUp,
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+ X509NameTokenizer nTok = new X509NameTokenizer(dirName);
+
+ while (nTok.hasMoreTokens())
+ {
+ String token = nTok.nextToken();
+
+ if (token.indexOf('+') > 0)
+ {
+ X509NameTokenizer pTok = new X509NameTokenizer(token, '+');
+
+ addEntry(lookUp, pTok.nextToken(), FALSE);
+
+ while (pTok.hasMoreTokens())
+ {
+ addEntry(lookUp, pTok.nextToken(), TRUE);
+ }
+ }
+ else
+ {
+ addEntry(lookUp, token, FALSE);
+ }
+ }
+
+ if (reverse)
+ {
+ Vector o = new Vector();
+ Vector v = new Vector();
+ Vector a = new Vector();
+
+ int count = 1;
+
+ for (int i = 0; i < this.ordering.size(); i++)
+ {
+ if (((Boolean)this.added.elementAt(i)).booleanValue())
+ {
+ o.insertElementAt(this.ordering.elementAt(i), count);
+ v.insertElementAt(this.values.elementAt(i), count);
+ a.insertElementAt(this.added.elementAt(i), count);
+ count++;
+ }
+ else
+ {
+ o.insertElementAt(this.ordering.elementAt(i), 0);
+ v.insertElementAt(this.values.elementAt(i), 0);
+ a.insertElementAt(this.added.elementAt(i), 0);
+ count = 1;
+ }
+ }
+
+ this.ordering = o;
+ this.values = v;
+ this.added = a;
+ }
+ }
+
+ private void addEntry(Hashtable lookUp, String token, Boolean isAdded)
+ {
+ X509NameTokenizer vTok;
+ String name;
+ String value;ASN1ObjectIdentifier oid;
+ vTok = new X509NameTokenizer(token, '=');
+
+ name = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ value = vTok.nextToken();
+
+ oid = decodeOID(name, lookUp);
+
+ this.ordering.addElement(oid);
+ this.values.addElement(unescape(value));
+ this.added.addElement(isAdded);
+ }
+
+ /**
+ * return a vector of the oids in the name, in the order they were found.
+ */
+ public Vector getOIDs()
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ v.addElement(ordering.elementAt(i));
+ }
+
+ return v;
+ }
+
+ /**
+ * return a vector of the values found in the name, in the order they
+ * were found.
+ */
+ public Vector getValues()
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != values.size(); i++)
+ {
+ v.addElement(values.elementAt(i));
+ }
+
+ return v;
+ }
+
+ /**
+ * return a vector of the values found in the name, in the order they
+ * were found, with the DN label corresponding to passed in oid.
+ */
+ public Vector getValues(
+ ASN1ObjectIdentifier oid)
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != values.size(); i++)
+ {
+ if (ordering.elementAt(i).equals(oid))
+ {
+ String val = (String)values.elementAt(i);
+
+ if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
+ {
+ v.addElement(val.substring(1));
+ }
+ else
+ {
+ v.addElement(val);
+ }
+ }
+ }
+
+ return v;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (seq == null)
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ ASN1EncodableVector sVec = new ASN1EncodableVector();
+ ASN1ObjectIdentifier lstOid = null;
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+
+ v.add(oid);
+
+ String str = (String)values.elementAt(i);
+
+ v.add(converter.getConvertedValue(oid, str));
+
+ if (lstOid == null
+ || ((Boolean)this.added.elementAt(i)).booleanValue())
+ {
+ sVec.add(new DERSequence(v));
+ }
+ else
+ {
+ vec.add(new DERSet(sVec));
+ sVec = new ASN1EncodableVector();
+
+ sVec.add(new DERSequence(v));
+ }
+
+ lstOid = oid;
+ }
+
+ vec.add(new DERSet(sVec));
+
+ seq = new DERSequence(vec);
+ }
+
+ return seq;
+ }
+
+ /**
+ * @param inOrder if true the order of both X509 names must be the same,
+ * as well as the values associated with each element.
+ */
+ public boolean equals(Object obj, boolean inOrder)
+ {
+ if (!inOrder)
+ {
+ return this.equals(obj);
+ }
+
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ X509Name other;
+
+ try
+ {
+ other = X509Name.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+
+ int orderingSize = ordering.size();
+
+ if (orderingSize != other.ordering.size())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < orderingSize; i++)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+ ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
+
+ if (oid.equals(oOid))
+ {
+ String value = (String)values.elementAt(i);
+ String oValue = (String)other.values.elementAt(i);
+
+ if (!equivalentStrings(value, oValue))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ if (isHashCodeCalculated)
+ {
+ return hashCodeValue;
+ }
+
+ isHashCodeCalculated = true;
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != ordering.size(); i += 1)
+ {
+ String value = (String)values.elementAt(i);
+
+ value = canonicalize(value);
+ value = stripInternalSpaces(value);
+
+ hashCodeValue ^= ordering.elementAt(i).hashCode();
+ hashCodeValue ^= value.hashCode();
+ }
+
+ return hashCodeValue;
+ }
+
+ /**
+ * test for equality - note: case is ignored.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ X509Name other;
+
+ try
+ {
+ other = X509Name.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+
+ int orderingSize = ordering.size();
+
+ if (orderingSize != other.ordering.size())
+ {
+ return false;
+ }
+
+ boolean[] indexes = new boolean[orderingSize];
+ int start, end, delta;
+
+ if (ordering.elementAt(0).equals(other.ordering.elementAt(0))) // guess forward
+ {
+ start = 0;
+ end = orderingSize;
+ delta = 1;
+ }
+ else // guess reversed - most common problem
+ {
+ start = orderingSize - 1;
+ end = -1;
+ delta = -1;
+ }
+
+ for (int i = start; i != end; i += delta)
+ {
+ boolean found = false;
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+ String value = (String)values.elementAt(i);
+
+ for (int j = 0; j < orderingSize; j++)
+ {
+ if (indexes[j])
+ {
+ continue;
+ }
+
+ ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
+
+ if (oid.equals(oOid))
+ {
+ String oValue = (String)other.values.elementAt(j);
+
+ if (equivalentStrings(value, oValue))
+ {
+ indexes[j] = true;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean equivalentStrings(String s1, String s2)
+ {
+ String value = canonicalize(s1);
+ String oValue = canonicalize(s2);
+
+ if (!value.equals(oValue))
+ {
+ value = stripInternalSpaces(value);
+ oValue = stripInternalSpaces(oValue);
+
+ if (!value.equals(oValue))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private String canonicalize(String s)
+ {
+ String value = Strings.toLowerCase(s.trim());
+
+ if (value.length() > 0 && value.charAt(0) == '#')
+ {
+ ASN1Primitive obj = decodeObject(value);
+
+ if (obj instanceof ASN1String)
+ {
+ value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
+ }
+ }
+
+ return value;
+ }
+
+ private ASN1Primitive decodeObject(String oValue)
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unknown encoding in name: " + e);
+ }
+ }
+
+ private String stripInternalSpaces(
+ String str)
+ {
+ StringBuffer res = new StringBuffer();
+
+ if (str.length() != 0)
+ {
+ char c1 = str.charAt(0);
+
+ res.append(c1);
+
+ for (int k = 1; k < str.length(); k++)
+ {
+ char c2 = str.charAt(k);
+ if (!(c1 == ' ' && c2 == ' '))
+ {
+ res.append(c2);
+ }
+ c1 = c2;
+ }
+ }
+
+ return res.toString();
+ }
+
+ private void appendValue(
+ StringBuffer buf,
+ Hashtable oidSymbols,
+ ASN1ObjectIdentifier oid,
+ String value)
+ {
+ String sym = (String)oidSymbols.get(oid);
+
+ if (sym != null)
+ {
+ buf.append(sym);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ }
+
+ buf.append('=');
+
+ int start = buf.length();
+ buf.append(value);
+ int end = buf.length();
+
+ if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
+ {
+ start += 2;
+ }
+
+ while (start < end && buf.charAt(start) == ' ')
+ {
+ buf.insert(start, "\\");
+ start += 2;
+ ++end;
+ }
+
+ while (--end > start && buf.charAt(end) == ' ')
+ {
+ buf.insert(end, '\\');
+ }
+
+ while (start <= end)
+ {
+ switch (buf.charAt(start))
+ {
+ case ',':
+ case '"':
+ case '\\':
+ case '+':
+ case '=':
+ case '<':
+ case '>':
+ case ';':
+ buf.insert(start, "\\");
+ start += 2;
+ ++end;
+ break;
+ default:
+ ++start;
+ break;
+ }
+ }
+ }
+
+ /**
+ * convert the structure to a string - if reverse is true the
+ * oids and values are listed out starting with the last element
+ * in the sequence (ala RFC 2253), otherwise the string will begin
+ * with the first element of the structure. If no string definition
+ * for the oid is found in oidSymbols the string value of the oid is
+ * added. Two standard symbol tables are provided DefaultSymbols, and
+ * RFC2253Symbols as part of this class.
+ *
+ * @param reverse if true start at the end of the sequence and work back.
+ * @param oidSymbols look up table strings for oids.
+ */
+ public String toString(
+ boolean reverse,
+ Hashtable oidSymbols)
+ {
+ StringBuffer buf = new StringBuffer();
+ Vector components = new Vector();
+ boolean first = true;
+
+ StringBuffer ava = null;
+
+ for (int i = 0; i < ordering.size(); i++)
+ {
+ if (((Boolean)added.elementAt(i)).booleanValue())
+ {
+ ava.append('+');
+ appendValue(ava, oidSymbols,
+ (ASN1ObjectIdentifier)ordering.elementAt(i),
+ (String)values.elementAt(i));
+ }
+ else
+ {
+ ava = new StringBuffer();
+ appendValue(ava, oidSymbols,
+ (ASN1ObjectIdentifier)ordering.elementAt(i),
+ (String)values.elementAt(i));
+ components.addElement(ava);
+ }
+ }
+
+ if (reverse)
+ {
+ for (int i = components.size() - 1; i >= 0; i--)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ buf.append(components.elementAt(i).toString());
+ }
+ }
+ else
+ {
+ for (int i = 0; i < components.size(); i++)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ buf.append(components.elementAt(i).toString());
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private String bytesToString(
+ byte[] data)
+ {
+ char[] cs = new char[data.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(data[i] & 0xff);
+ }
+
+ return new String(cs);
+ }
+
+ public String toString()
+ {
+ return toString(DefaultReverse, DefaultSymbols);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java
new file mode 100644
index 000000000..f831582d2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java
@@ -0,0 +1,113 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.util.Strings;
+
+/**
+ * It turns out that the number of standard ways the fields in a DN should be
+ * encoded into their ASN.1 counterparts is rapidly approaching the
+ * number of machines on the internet. By default the X509Name class
+ * will produce UTF8Strings in line with the current recommendations (RFC 3280).
+ *
+ * An example of an encoder look like below: + *
+ * public class X509DirEntryConverter + * extends X509NameEntryConverter + * { + * public ASN1Primitive getConvertedValue( + * ASN1ObjectIdentifier oid, + * String value) + * { + * if (str.length() != 0 && str.charAt(0) == '#') + * { + * return convertHexEncoded(str, 1); + * } + * if (oid.equals(EmailAddress)) + * { + * return new DERIA5String(str); + * } + * else if (canBePrintable(str)) + * { + * return new DERPrintableString(str); + * } + * else if (canBeUTF8(str)) + * { + * return new DERUTF8String(str); + * } + * else + * { + * return new DERBMPString(str); + * } + * } + * } + */ +public abstract class X509NameEntryConverter +{ + /** + * Convert an inline encoded hex string rendition of an ASN.1 + * object back into its corresponding ASN.1 object. + * + * @param str the hex encoded object + * @param off the index at which the encoding starts + * @return the decoded object + */ + protected ASN1Primitive convertHexEncoded( + String str, + int off) + throws IOException + { + str = Strings.toLowerCase(str); + byte[] data = new byte[(str.length() - off) / 2]; + for (int index = 0; index != data.length; index++) + { + char left = str.charAt((index * 2) + off); + char right = str.charAt((index * 2) + off + 1); + + if (left < 'a') + { + data[index] = (byte)((left - '0') << 4); + } + else + { + data[index] = (byte)((left - 'a' + 10) << 4); + } + if (right < 'a') + { + data[index] |= (byte)(right - '0'); + } + else + { + data[index] |= (byte)(right - 'a' + 10); + } + } + + ASN1InputStream aIn = new ASN1InputStream(data); + + return aIn.readObject(); + } + + /** + * return true if the passed in String can be represented without + * loss as a PrintableString, false otherwise. + */ + protected boolean canBePrintable( + String str) + { + return DERPrintableString.isPrintableString(str); + } + + /** + * Convert the passed in String value into the appropriate ASN.1 + * encoded object. + * + * @param oid the oid associated with the value in the DN. + * @param value the value of the particular DN component. + * @return the ASN.1 equivalent for the value. + */ + public abstract ASN1Primitive getConvertedValue(ASN1ObjectIdentifier oid, String value); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java new file mode 100644 index 000000000..864f0e81c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java @@ -0,0 +1,91 @@ +package org.spongycastle.asn1.x509; + +/** + * class for breaking up an X500 Name into it's component tokens, ala + * java.util.StringTokenizer. We need this class as some of the + * lightweight Java environment don't support classes like + * StringTokenizer. + * @deprecated use X500NameTokenizer + */ +public class X509NameTokenizer +{ + private String value; + private int index; + private char separator; + private StringBuffer buf = new StringBuffer(); + + public X509NameTokenizer( + String oid) + { + this(oid, ','); + } + + public X509NameTokenizer( + String oid, + char separator) + { + this.value = oid; + this.index = -1; + this.separator = separator; + } + + public boolean hasMoreTokens() + { + return (index != value.length()); + } + + public String nextToken() + { + if (index == value.length()) + { + return null; + } + + int end = index + 1; + boolean quoted = false; + boolean escaped = false; + + buf.setLength(0); + + while (end != value.length()) + { + char c = value.charAt(end); + + if (c == '"') + { + if (!escaped) + { + quoted = !quoted; + } + buf.append(c); + escaped = false; + } + else + { + if (escaped || quoted) + { + buf.append(c); + escaped = false; + } + else if (c == '\\') + { + buf.append(c); + escaped = true; + } + else if (c == separator) + { + break; + } + else + { + buf.append(c); + } + } + end++; + } + + index = end; + + return buf.toString(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java new file mode 100644 index 000000000..a760411bd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java @@ -0,0 +1,81 @@ +package org.spongycastle.asn1.x509; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public interface X509ObjectIdentifiers +{ + + /** Subject RDN components: commonName = 2.5.4.3 */ + static final ASN1ObjectIdentifier commonName = new ASN1ObjectIdentifier("2.5.4.3"); + /** Subject RDN components: countryName = 2.5.4.6 */ + static final ASN1ObjectIdentifier countryName = new ASN1ObjectIdentifier("2.5.4.6"); + /** Subject RDN components: localityName = 2.5.4.7 */ + static final ASN1ObjectIdentifier localityName = new ASN1ObjectIdentifier("2.5.4.7"); + /** Subject RDN components: stateOrProvinceName = 2.5.4.8 */ + static final ASN1ObjectIdentifier stateOrProvinceName = new ASN1ObjectIdentifier("2.5.4.8"); + /** Subject RDN components: organization = 2.5.4.10 */ + static final ASN1ObjectIdentifier organization = new ASN1ObjectIdentifier("2.5.4.10"); + /** Subject RDN components: organizationalUnitName = 2.5.4.11 */ + static final ASN1ObjectIdentifier organizationalUnitName = new ASN1ObjectIdentifier("2.5.4.11"); + + /** Subject RDN components: telephone_number = 2.5.4.20 */ + static final ASN1ObjectIdentifier id_at_telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20"); + /** Subject RDN components: name = 2.5.4.41 */ + static final ASN1ObjectIdentifier id_at_name = new ASN1ObjectIdentifier("2.5.4.41"); + + /** + * id-SHA1 OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } + *+ * OID: 1.3.14.3.2.27 + */ + static final ASN1ObjectIdentifier id_SHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.26"); + + /** + * ripemd160 OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)} + *
+ * OID: 1.3.36.3.2.1 + */ + static final ASN1ObjectIdentifier ripemd160 = new ASN1ObjectIdentifier("1.3.36.3.2.1"); + + /** + * ripemd160WithRSAEncryption OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) } + *
+ * OID: 1.3.36.3.3.1.2 + */ + static final ASN1ObjectIdentifier ripemd160WithRSAEncryption = new ASN1ObjectIdentifier("1.3.36.3.3.1.2"); + + + /** OID: 2.5.8.1.1 */ + static final ASN1ObjectIdentifier id_ea_rsa = new ASN1ObjectIdentifier("2.5.8.1.1"); + + /** id-pkix OID: 1.3.6.1.5.5.7 + */ + static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7"); + + /** + * private internet extensions; OID = 1.3.6.1.5.5.7.1 + */ + static final ASN1ObjectIdentifier id_pe = id_pkix.branch("1"); + + /** + * ISO ARC for standard certificate and CRL extensions + *
+ * OID: 2.5.29 + */ + static final ASN1ObjectIdentifier id_ce = new ASN1ObjectIdentifier("2.5.29"); + + /** id-pkix OID: 1.3.6.1.5.5.7.48 */ + static final ASN1ObjectIdentifier id_ad = id_pkix.branch("48"); + /** id-ad-caIssuers OID: 1.3.6.1.5.5.7.48.2 */ + static final ASN1ObjectIdentifier id_ad_caIssuers = id_ad.branch("2"); + /** id-ad-ocsp OID: 1.3.6.1.5.5.7.48.1 */ + static final ASN1ObjectIdentifier id_ad_ocsp = id_ad.branch("1"); + + /** OID for ocsp uri in AuthorityInformationAccess extension */ + static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp; + /** OID for crl uri in AuthorityInformationAccess extension */ + static final ASN1ObjectIdentifier crlAccessMethod = id_ad_caIssuers; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java new file mode 100644 index 000000000..6175572cd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java @@ -0,0 +1,122 @@ +package org.spongycastle.asn1.x509.qualified; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * The BiometricData object. + *
+ * BiometricData ::= SEQUENCE { + * typeOfBiometricData TypeOfBiometricData, + * hashAlgorithm AlgorithmIdentifier, + * biometricDataHash OCTET STRING, + * sourceDataUri IA5String OPTIONAL } + *+ */ +public class BiometricData + extends ASN1Object +{ + private TypeOfBiometricData typeOfBiometricData; + private AlgorithmIdentifier hashAlgorithm; + private ASN1OctetString biometricDataHash; + private DERIA5String sourceDataUri; + + public static BiometricData getInstance( + Object obj) + { + if (obj instanceof BiometricData) + { + return (BiometricData)obj; + } + + if (obj != null) + { + return new BiometricData(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private BiometricData(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + // typeOfBiometricData + typeOfBiometricData = TypeOfBiometricData.getInstance(e.nextElement()); + // hashAlgorithm + hashAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement()); + // biometricDataHash + biometricDataHash = ASN1OctetString.getInstance(e.nextElement()); + // sourceDataUri + if (e.hasMoreElements()) + { + sourceDataUri = DERIA5String.getInstance(e.nextElement()); + } + } + + public BiometricData( + TypeOfBiometricData typeOfBiometricData, + AlgorithmIdentifier hashAlgorithm, + ASN1OctetString biometricDataHash, + DERIA5String sourceDataUri) + { + this.typeOfBiometricData = typeOfBiometricData; + this.hashAlgorithm = hashAlgorithm; + this.biometricDataHash = biometricDataHash; + this.sourceDataUri = sourceDataUri; + } + + public BiometricData( + TypeOfBiometricData typeOfBiometricData, + AlgorithmIdentifier hashAlgorithm, + ASN1OctetString biometricDataHash) + { + this.typeOfBiometricData = typeOfBiometricData; + this.hashAlgorithm = hashAlgorithm; + this.biometricDataHash = biometricDataHash; + this.sourceDataUri = null; + } + + public TypeOfBiometricData getTypeOfBiometricData() + { + return typeOfBiometricData; + } + + public AlgorithmIdentifier getHashAlgorithm() + { + return hashAlgorithm; + } + + public ASN1OctetString getBiometricDataHash() + { + return biometricDataHash; + } + + public DERIA5String getSourceDataUri() + { + return sourceDataUri; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + seq.add(typeOfBiometricData); + seq.add(hashAlgorithm); + seq.add(biometricDataHash); + + if (sourceDataUri != null) + { + seq.add(sourceDataUri); + } + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java new file mode 100644 index 000000000..501456699 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java @@ -0,0 +1,11 @@ +package org.spongycastle.asn1.x509.qualified; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public interface ETSIQCObjectIdentifiers +{ + static final ASN1ObjectIdentifier id_etsi_qcs_QcCompliance = new ASN1ObjectIdentifier("0.4.0.1862.1.1"); + static final ASN1ObjectIdentifier id_etsi_qcs_LimiteValue = new ASN1ObjectIdentifier("0.4.0.1862.1.2"); + static final ASN1ObjectIdentifier id_etsi_qcs_RetentionPeriod = new ASN1ObjectIdentifier("0.4.0.1862.1.3"); + static final ASN1ObjectIdentifier id_etsi_qcs_QcSSCD = new ASN1ObjectIdentifier("0.4.0.1862.1.4"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java new file mode 100644 index 000000000..3e791b47a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java @@ -0,0 +1,93 @@ +package org.spongycastle.asn1.x509.qualified; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERPrintableString; + +/** + * The Iso4217CurrencyCode object. + *+ * Iso4217CurrencyCode ::= CHOICE { + * alphabetic PrintableString (SIZE 3), --Recommended + * numeric INTEGER (1..999) } + * -- Alphabetic or numeric currency code as defined in ISO 4217 + * -- It is recommended that the Alphabetic form is used + *+ */ +public class Iso4217CurrencyCode + extends ASN1Object + implements ASN1Choice +{ + final int ALPHABETIC_MAXSIZE = 3; + final int NUMERIC_MINSIZE = 1; + final int NUMERIC_MAXSIZE = 999; + + ASN1Encodable obj; + int numeric; + + public static Iso4217CurrencyCode getInstance( + Object obj) + { + if (obj == null || obj instanceof Iso4217CurrencyCode) + { + return (Iso4217CurrencyCode)obj; + } + + if (obj instanceof ASN1Integer) + { + ASN1Integer numericobj = ASN1Integer.getInstance(obj); + int numeric = numericobj.getValue().intValue(); + return new Iso4217CurrencyCode(numeric); + } + else + if (obj instanceof DERPrintableString) + { + DERPrintableString alphabetic = DERPrintableString.getInstance(obj); + return new Iso4217CurrencyCode(alphabetic.getString()); + } + throw new IllegalArgumentException("unknown object in getInstance"); + } + + public Iso4217CurrencyCode( + int numeric) + { + if (numeric > NUMERIC_MAXSIZE || numeric < NUMERIC_MINSIZE) + { + throw new IllegalArgumentException("wrong size in numeric code : not in (" +NUMERIC_MINSIZE +".."+ NUMERIC_MAXSIZE +")"); + } + obj = new ASN1Integer(numeric); + } + + public Iso4217CurrencyCode( + String alphabetic) + { + if (alphabetic.length() > ALPHABETIC_MAXSIZE) + { + throw new IllegalArgumentException("wrong size in alphabetic code : max size is " + ALPHABETIC_MAXSIZE); + } + obj = new DERPrintableString(alphabetic); + } + + public boolean isAlphabetic() + { + return obj instanceof DERPrintableString; + } + + public String getAlphabetic() + { + return ((DERPrintableString)obj).getString(); + } + + public int getNumeric() + { + return ((ASN1Integer)obj).getValue().intValue(); + } + + public ASN1Primitive toASN1Primitive() + { + return obj.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java new file mode 100644 index 000000000..edf1897a1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java @@ -0,0 +1,92 @@ +package org.spongycastle.asn1.x509.qualified; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * The MonetaryValue object. + *+ * MonetaryValue ::= SEQUENCE { + * currency Iso4217CurrencyCode, + * amount INTEGER, + * exponent INTEGER } + * -- value = amount * 10^exponent + *+ */ +public class MonetaryValue + extends ASN1Object +{ + private Iso4217CurrencyCode currency; + private ASN1Integer amount; + private ASN1Integer exponent; + + public static MonetaryValue getInstance( + Object obj) + { + if (obj instanceof MonetaryValue) + { + return (MonetaryValue)obj; + } + + if (obj != null) + { + return new MonetaryValue(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private MonetaryValue( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + // currency + currency = Iso4217CurrencyCode.getInstance(e.nextElement()); + // hashAlgorithm + amount = ASN1Integer.getInstance(e.nextElement()); + // exponent + exponent = ASN1Integer.getInstance(e.nextElement()); + } + + public MonetaryValue( + Iso4217CurrencyCode currency, + int amount, + int exponent) + { + this.currency = currency; + this.amount = new ASN1Integer(amount); + this.exponent = new ASN1Integer(exponent); + } + + public Iso4217CurrencyCode getCurrency() + { + return currency; + } + + public BigInteger getAmount() + { + return amount.getValue(); + } + + public BigInteger getExponent() + { + return exponent.getValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + seq.add(currency); + seq.add(amount); + seq.add(exponent); + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java new file mode 100644 index 000000000..f66f5113d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java @@ -0,0 +1,95 @@ +package org.spongycastle.asn1.x509.qualified; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * The QCStatement object. + *+ * QCStatement ::= SEQUENCE { + * statementId OBJECT IDENTIFIER, + * statementInfo ANY DEFINED BY statementId OPTIONAL} + *+ */ + +public class QCStatement + extends ASN1Object + implements ETSIQCObjectIdentifiers, RFC3739QCObjectIdentifiers +{ + ASN1ObjectIdentifier qcStatementId; + ASN1Encodable qcStatementInfo; + + public static QCStatement getInstance( + Object obj) + { + if (obj instanceof QCStatement) + { + return (QCStatement)obj; + } + if (obj != null) + { + return new QCStatement(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private QCStatement( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + // qcStatementId + qcStatementId = ASN1ObjectIdentifier.getInstance(e.nextElement()); + // qcstatementInfo + if (e.hasMoreElements()) + { + qcStatementInfo = (ASN1Encodable) e.nextElement(); + } + } + + public QCStatement( + ASN1ObjectIdentifier qcStatementId) + { + this.qcStatementId = qcStatementId; + this.qcStatementInfo = null; + } + + public QCStatement( + ASN1ObjectIdentifier qcStatementId, + ASN1Encodable qcStatementInfo) + { + this.qcStatementId = qcStatementId; + this.qcStatementInfo = qcStatementInfo; + } + + public ASN1ObjectIdentifier getStatementId() + { + return qcStatementId; + } + + public ASN1Encodable getStatementInfo() + { + return qcStatementInfo; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + seq.add(qcStatementId); + + if (qcStatementInfo != null) + { + seq.add(qcStatementInfo); + } + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java new file mode 100644 index 000000000..d3663b0e9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java @@ -0,0 +1,11 @@ +package org.spongycastle.asn1.x509.qualified; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public interface RFC3739QCObjectIdentifiers +{ + /** OID: 1.3.6.1.5.5.7.11.1 */ + static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v1 = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11.1"); + /** OID: 1.3.6.1.5.5.7.11.2 */ + static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v2 = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11.2"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java new file mode 100644 index 000000000..2a52a7d9e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java @@ -0,0 +1,131 @@ +package org.spongycastle.asn1.x509.qualified; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.GeneralName; + +/** + * The SemanticsInformation object. + *+ * SemanticsInformation ::= SEQUENCE { + * semanticsIdentifier OBJECT IDENTIFIER OPTIONAL, + * nameRegistrationAuthorities NameRegistrationAuthorities + * OPTIONAL } + * (WITH COMPONENTS {..., semanticsIdentifier PRESENT}| + * WITH COMPONENTS {..., nameRegistrationAuthorities PRESENT}) + * + * NameRegistrationAuthorities ::= SEQUENCE SIZE (1..MAX) OF + * GeneralName + *+ */ +public class SemanticsInformation + extends ASN1Object +{ + private ASN1ObjectIdentifier semanticsIdentifier; + private GeneralName[] nameRegistrationAuthorities; + + public static SemanticsInformation getInstance(Object obj) + { + if (obj instanceof SemanticsInformation) + { + return (SemanticsInformation)obj; + } + + if (obj != null) + { + return new SemanticsInformation(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private SemanticsInformation(ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + if (seq.size() < 1) + { + throw new IllegalArgumentException("no objects in SemanticsInformation"); + } + + Object object = e.nextElement(); + if (object instanceof ASN1ObjectIdentifier) + { + semanticsIdentifier = ASN1ObjectIdentifier.getInstance(object); + if (e.hasMoreElements()) + { + object = e.nextElement(); + } + else + { + object = null; + } + } + + if (object != null) + { + ASN1Sequence generalNameSeq = ASN1Sequence.getInstance(object); + nameRegistrationAuthorities = new GeneralName[generalNameSeq.size()]; + for (int i= 0; i < generalNameSeq.size(); i++) + { + nameRegistrationAuthorities[i] = GeneralName.getInstance(generalNameSeq.getObjectAt(i)); + } + } + } + + public SemanticsInformation( + ASN1ObjectIdentifier semanticsIdentifier, + GeneralName[] generalNames) + { + this.semanticsIdentifier = semanticsIdentifier; + this.nameRegistrationAuthorities = generalNames; + } + + public SemanticsInformation(ASN1ObjectIdentifier semanticsIdentifier) + { + this.semanticsIdentifier = semanticsIdentifier; + this.nameRegistrationAuthorities = null; + } + + public SemanticsInformation(GeneralName[] generalNames) + { + this.semanticsIdentifier = null; + this.nameRegistrationAuthorities = generalNames; + } + + public ASN1ObjectIdentifier getSemanticsIdentifier() + { + return semanticsIdentifier; + } + + public GeneralName[] getNameRegistrationAuthorities() + { + return nameRegistrationAuthorities; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector seq = new ASN1EncodableVector(); + + if (this.semanticsIdentifier != null) + { + seq.add(semanticsIdentifier); + } + if (this.nameRegistrationAuthorities != null) + { + ASN1EncodableVector seqname = new ASN1EncodableVector(); + for (int i = 0; i < nameRegistrationAuthorities.length; i++) + { + seqname.add(nameRegistrationAuthorities[i]); + } + seq.add(new DERSequence(seqname)); + } + + return new DERSequence(seq); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java new file mode 100644 index 000000000..e7285d260 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java @@ -0,0 +1,90 @@ +package org.spongycastle.asn1.x509.qualified; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; + +/** + * The TypeOfBiometricData object. + *+ * TypeOfBiometricData ::= CHOICE { + * predefinedBiometricType PredefinedBiometricType, + * biometricDataOid OBJECT IDENTIFIER } + * + * PredefinedBiometricType ::= INTEGER { + * picture(0),handwritten-signature(1)} + * (picture|handwritten-signature) + *+ */ +public class TypeOfBiometricData + extends ASN1Object + implements ASN1Choice +{ + public static final int PICTURE = 0; + public static final int HANDWRITTEN_SIGNATURE = 1; + + ASN1Encodable obj; + + public static TypeOfBiometricData getInstance(Object obj) + { + if (obj == null || obj instanceof TypeOfBiometricData) + { + return (TypeOfBiometricData)obj; + } + + if (obj instanceof ASN1Integer) + { + ASN1Integer predefinedBiometricTypeObj = ASN1Integer.getInstance(obj); + int predefinedBiometricType = predefinedBiometricTypeObj.getValue().intValue(); + + return new TypeOfBiometricData(predefinedBiometricType); + } + else if (obj instanceof ASN1ObjectIdentifier) + { + ASN1ObjectIdentifier BiometricDataID = ASN1ObjectIdentifier.getInstance(obj); + return new TypeOfBiometricData(BiometricDataID); + } + + throw new IllegalArgumentException("unknown object in getInstance"); + } + + public TypeOfBiometricData(int predefinedBiometricType) + { + if (predefinedBiometricType == PICTURE || predefinedBiometricType == HANDWRITTEN_SIGNATURE) + { + obj = new ASN1Integer(predefinedBiometricType); + } + else + { + throw new IllegalArgumentException("unknow PredefinedBiometricType : " + predefinedBiometricType); + } + } + + public TypeOfBiometricData(ASN1ObjectIdentifier BiometricDataID) + { + obj = BiometricDataID; + } + + public boolean isPredefined() + { + return obj instanceof ASN1Integer; + } + + public int getPredefinedBiometricType() + { + return ((ASN1Integer)obj).getValue().intValue(); + } + + public ASN1ObjectIdentifier getBiometricDataOid() + { + return (ASN1ObjectIdentifier)obj; + } + + public ASN1Primitive toASN1Primitive() + { + return obj.toASN1Primitive(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java new file mode 100644 index 000000000..f5fbc4aa7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java @@ -0,0 +1,191 @@ +package org.spongycastle.asn1.x509.sigi; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Structure for a name or pseudonym. + * + *+ * NameOrPseudonym ::= CHOICE { + * surAndGivenName SEQUENCE { + * surName DirectoryString, + * givenName SEQUENCE OF DirectoryString + * }, + * pseudonym DirectoryString + * } + *+ * + * @see org.spongycastle.asn1.x509.sigi.PersonalData + * + */ +public class NameOrPseudonym + extends ASN1Object + implements ASN1Choice +{ + private DirectoryString pseudonym; + + private DirectoryString surname; + + private ASN1Sequence givenName; + + public static NameOrPseudonym getInstance(Object obj) + { + if (obj == null || obj instanceof NameOrPseudonym) + { + return (NameOrPseudonym)obj; + } + + if (obj instanceof ASN1String) + { + return new NameOrPseudonym(DirectoryString.getInstance(obj)); + } + + if (obj instanceof ASN1Sequence) + { + return new NameOrPseudonym((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * Constructor from DirectoryString. + * + * The sequence is of type NameOrPseudonym: + * + *+ * NameOrPseudonym ::= CHOICE { + * surAndGivenName SEQUENCE { + * surName DirectoryString, + * givenName SEQUENCE OF DirectoryString + * }, + * pseudonym DirectoryString + * } + *+ * @param pseudonym pseudonym value to use. + */ + public NameOrPseudonym(DirectoryString pseudonym) + { + this.pseudonym = pseudonym; + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type NameOrPseudonym: + * + *+ * NameOrPseudonym ::= CHOICE { + * surAndGivenName SEQUENCE { + * surName DirectoryString, + * givenName SEQUENCE OF DirectoryString + * }, + * pseudonym DirectoryString + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private NameOrPseudonym(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + if (!(seq.getObjectAt(0) instanceof ASN1String)) + { + throw new IllegalArgumentException("Bad object encountered: " + + seq.getObjectAt(0).getClass()); + } + + surname = DirectoryString.getInstance(seq.getObjectAt(0)); + givenName = ASN1Sequence.getInstance(seq.getObjectAt(1)); + } + + /** + * Constructor from a given details. + * + * @param pseudonym The pseudonym. + */ + public NameOrPseudonym(String pseudonym) + { + this(new DirectoryString(pseudonym)); + } + + /** + * Constructor from a given details. + * + * @param surname The surname. + * @param givenName A sequence of directory strings making up the givenName + */ + public NameOrPseudonym(DirectoryString surname, ASN1Sequence givenName) + { + this.surname = surname; + this.givenName = givenName; + } + + public DirectoryString getPseudonym() + { + return pseudonym; + } + + public DirectoryString getSurname() + { + return surname; + } + + public DirectoryString[] getGivenName() + { + DirectoryString[] items = new DirectoryString[givenName.size()]; + int count = 0; + for (Enumeration e = givenName.getObjects(); e.hasMoreElements();) + { + items[count++] = DirectoryString.getInstance(e.nextElement()); + } + return items; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *+ * NameOrPseudonym ::= CHOICE { + * surAndGivenName SEQUENCE { + * surName DirectoryString, + * givenName SEQUENCE OF DirectoryString + * }, + * pseudonym DirectoryString + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + if (pseudonym != null) + { + return pseudonym.toASN1Primitive(); + } + else + { + ASN1EncodableVector vec1 = new ASN1EncodableVector(); + vec1.add(surname); + vec1.add(givenName); + return new DERSequence(vec1); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java new file mode 100644 index 000000000..ca34b4f57 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java @@ -0,0 +1,214 @@ +package org.spongycastle.asn1.x509.sigi; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERPrintableString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x500.DirectoryString; + +/** + * Contains personal data for the otherName field in the subjectAltNames + * extension. + * + *+ * PersonalData ::= SEQUENCE { + * nameOrPseudonym NameOrPseudonym, + * nameDistinguisher [0] INTEGER OPTIONAL, + * dateOfBirth [1] GeneralizedTime OPTIONAL, + * placeOfBirth [2] DirectoryString OPTIONAL, + * gender [3] PrintableString OPTIONAL, + * postalAddress [4] DirectoryString OPTIONAL + * } + *+ * + * @see org.spongycastle.asn1.x509.sigi.NameOrPseudonym + * @see org.spongycastle.asn1.x509.sigi.SigIObjectIdentifiers + */ +public class PersonalData + extends ASN1Object +{ + private NameOrPseudonym nameOrPseudonym; + private BigInteger nameDistinguisher; + private ASN1GeneralizedTime dateOfBirth; + private DirectoryString placeOfBirth; + private String gender; + private DirectoryString postalAddress; + + public static PersonalData getInstance(Object obj) + { + if (obj == null || obj instanceof PersonalData) + { + return (PersonalData)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new PersonalData((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * Constructor from ASN1Sequence. + * + * The sequence is of type NameOrPseudonym: + * + *+ * PersonalData ::= SEQUENCE { + * nameOrPseudonym NameOrPseudonym, + * nameDistinguisher [0] INTEGER OPTIONAL, + * dateOfBirth [1] GeneralizedTime OPTIONAL, + * placeOfBirth [2] DirectoryString OPTIONAL, + * gender [3] PrintableString OPTIONAL, + * postalAddress [4] DirectoryString OPTIONAL + * } + *+ * + * @param seq The ASN.1 sequence. + */ + private PersonalData(ASN1Sequence seq) + { + if (seq.size() < 1) + { + throw new IllegalArgumentException("Bad sequence size: " + + seq.size()); + } + + Enumeration e = seq.getObjects(); + + nameOrPseudonym = NameOrPseudonym.getInstance(e.nextElement()); + + while (e.hasMoreElements()) + { + ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement()); + int tag = o.getTagNo(); + switch (tag) + { + case 0: + nameDistinguisher = ASN1Integer.getInstance(o, false).getValue(); + break; + case 1: + dateOfBirth = ASN1GeneralizedTime.getInstance(o, false); + break; + case 2: + placeOfBirth = DirectoryString.getInstance(o, true); + break; + case 3: + gender = DERPrintableString.getInstance(o, false).getString(); + break; + case 4: + postalAddress = DirectoryString.getInstance(o, true); + break; + default: + throw new IllegalArgumentException("Bad tag number: " + o.getTagNo()); + } + } + } + + /** + * Constructor from a given details. + * + * @param nameOrPseudonym Name or pseudonym. + * @param nameDistinguisher Name distinguisher. + * @param dateOfBirth Date of birth. + * @param placeOfBirth Place of birth. + * @param gender Gender. + * @param postalAddress Postal Address. + */ + public PersonalData(NameOrPseudonym nameOrPseudonym, + BigInteger nameDistinguisher, ASN1GeneralizedTime dateOfBirth, + DirectoryString placeOfBirth, String gender, DirectoryString postalAddress) + { + this.nameOrPseudonym = nameOrPseudonym; + this.dateOfBirth = dateOfBirth; + this.gender = gender; + this.nameDistinguisher = nameDistinguisher; + this.postalAddress = postalAddress; + this.placeOfBirth = placeOfBirth; + } + + public NameOrPseudonym getNameOrPseudonym() + { + return nameOrPseudonym; + } + + public BigInteger getNameDistinguisher() + { + return nameDistinguisher; + } + + public ASN1GeneralizedTime getDateOfBirth() + { + return dateOfBirth; + } + + public DirectoryString getPlaceOfBirth() + { + return placeOfBirth; + } + + public String getGender() + { + return gender; + } + + public DirectoryString getPostalAddress() + { + return postalAddress; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * + * Returns: + * + *+ * PersonalData ::= SEQUENCE { + * nameOrPseudonym NameOrPseudonym, + * nameDistinguisher [0] INTEGER OPTIONAL, + * dateOfBirth [1] GeneralizedTime OPTIONAL, + * placeOfBirth [2] DirectoryString OPTIONAL, + * gender [3] PrintableString OPTIONAL, + * postalAddress [4] DirectoryString OPTIONAL + * } + *+ * + * @return a DERObject + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector vec = new ASN1EncodableVector(); + vec.add(nameOrPseudonym); + if (nameDistinguisher != null) + { + vec.add(new DERTaggedObject(false, 0, new ASN1Integer(nameDistinguisher))); + } + if (dateOfBirth != null) + { + vec.add(new DERTaggedObject(false, 1, dateOfBirth)); + } + if (placeOfBirth != null) + { + vec.add(new DERTaggedObject(true, 2, placeOfBirth)); + } + if (gender != null) + { + vec.add(new DERTaggedObject(false, 3, new DERPrintableString(gender, true))); + } + if (postalAddress != null) + { + vec.add(new DERTaggedObject(true, 4, postalAddress)); + } + return new DERSequence(vec); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java new file mode 100644 index 000000000..5d9b25b3e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java @@ -0,0 +1,60 @@ +package org.spongycastle.asn1.x509.sigi; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * Object Identifiers of SigI specifciation (German Signature Law + * Interoperability specification). + */ +public interface SigIObjectIdentifiers +{ + /** + * OID: 1.3.36.8 + */ + public final static ASN1ObjectIdentifier id_sigi = new ASN1ObjectIdentifier("1.3.36.8"); + + /** + * Key purpose IDs for German SigI (Signature Interoperability + * Specification) + *+ * OID: 1.3.36.8.2 + */ + public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier("1.3.36.8.2"); + + /** + * Certificate policy IDs for German SigI (Signature Interoperability + * Specification) + *
+ * OID: 1.3.36.8.1 + */ + public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier("1.3.36.8.1"); + + /** + * Other Name IDs for German SigI (Signature Interoperability Specification) + *
+ * OID: 1.3.36.8.4 + */ + public final static ASN1ObjectIdentifier id_sigi_on = new ASN1ObjectIdentifier("1.3.36.8.4"); + + /** + * To be used for for the generation of directory service certificates. + *
+ * OID: 1.3.36.8.2.1 + */ + public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier("1.3.36.8.2.1"); + + /** + * ID for PersonalData + *
+ * OID: 1.3.36.8.4.1 + */ + public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier("1.3.36.8.4.1"); + + /** + * Certificate is conformant to german signature law. + *
+ * OID: 1.3.36.8.1.1 + */ + public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier("1.3.36.8.1.1"); + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHDomainParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHDomainParameters.java new file mode 100644 index 000000000..2d63bf4c7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHDomainParameters.java @@ -0,0 +1,139 @@ +package org.spongycastle.asn1.x9; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERSequence; + +public class DHDomainParameters + extends ASN1Object +{ + private ASN1Integer p, g, q, j; + private DHValidationParms validationParms; + + public static DHDomainParameters getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DHDomainParameters getInstance(Object obj) + { + if (obj == null || obj instanceof DHDomainParameters) + { + return (DHDomainParameters)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new DHDomainParameters((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid DHDomainParameters: " + + obj.getClass().getName()); + } + + public DHDomainParameters(ASN1Integer p, ASN1Integer g, ASN1Integer q, ASN1Integer j, + DHValidationParms validationParms) + { + if (p == null) + { + throw new IllegalArgumentException("'p' cannot be null"); + } + if (g == null) + { + throw new IllegalArgumentException("'g' cannot be null"); + } + if (q == null) + { + throw new IllegalArgumentException("'q' cannot be null"); + } + + this.p = p; + this.g = g; + this.q = q; + this.j = j; + this.validationParms = validationParms; + } + + private DHDomainParameters(ASN1Sequence seq) + { + if (seq.size() < 3 || seq.size() > 5) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + Enumeration e = seq.getObjects(); + this.p = ASN1Integer.getInstance(e.nextElement()); + this.g = ASN1Integer.getInstance(e.nextElement()); + this.q = ASN1Integer.getInstance(e.nextElement()); + + ASN1Encodable next = getNext(e); + + if (next != null && next instanceof ASN1Integer) + { + this.j = ASN1Integer.getInstance(next); + next = getNext(e); + } + + if (next != null) + { + this.validationParms = DHValidationParms.getInstance(next.toASN1Primitive()); + } + } + + private static ASN1Encodable getNext(Enumeration e) + { + return e.hasMoreElements() ? (ASN1Encodable)e.nextElement() : null; + } + + public ASN1Integer getP() + { + return this.p; + } + + public ASN1Integer getG() + { + return this.g; + } + + public ASN1Integer getQ() + { + return this.q; + } + + public ASN1Integer getJ() + { + return this.j; + } + + public DHValidationParms getValidationParms() + { + return this.validationParms; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.p); + v.add(this.g); + v.add(this.q); + + if (this.j != null) + { + v.add(this.j); + } + + if (this.validationParms != null) + { + v.add(this.validationParms); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHPublicKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHPublicKey.java new file mode 100644 index 000000000..b84a42b48 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHPublicKey.java @@ -0,0 +1,52 @@ +package org.spongycastle.asn1.x9; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; + +public class DHPublicKey + extends ASN1Object +{ + private ASN1Integer y; + + public static DHPublicKey getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Integer.getInstance(obj, explicit)); + } + + public static DHPublicKey getInstance(Object obj) + { + if (obj == null || obj instanceof DHPublicKey) + { + return (DHPublicKey)obj; + } + + if (obj instanceof ASN1Integer) + { + return new DHPublicKey((ASN1Integer)obj); + } + + throw new IllegalArgumentException("Invalid DHPublicKey: " + obj.getClass().getName()); + } + + public DHPublicKey(ASN1Integer y) + { + if (y == null) + { + throw new IllegalArgumentException("'y' cannot be null"); + } + + this.y = y; + } + + public ASN1Integer getY() + { + return this.y; + } + + public ASN1Primitive toASN1Primitive() + { + return this.y; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHValidationParms.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHValidationParms.java new file mode 100644 index 000000000..8f5ece200 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/DHValidationParms.java @@ -0,0 +1,80 @@ +package org.spongycastle.asn1.x9; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; + +public class DHValidationParms extends ASN1Object +{ + private DERBitString seed; + private ASN1Integer pgenCounter; + + public static DHValidationParms getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DHValidationParms getInstance(Object obj) + { + if (obj == null || obj instanceof DHDomainParameters) + { + return (DHValidationParms)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new DHValidationParms((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid DHValidationParms: " + obj.getClass().getName()); + } + + public DHValidationParms(DERBitString seed, ASN1Integer pgenCounter) + { + if (seed == null) + { + throw new IllegalArgumentException("'seed' cannot be null"); + } + if (pgenCounter == null) + { + throw new IllegalArgumentException("'pgenCounter' cannot be null"); + } + + this.seed = seed; + this.pgenCounter = pgenCounter; + } + + private DHValidationParms(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + this.seed = DERBitString.getInstance(seq.getObjectAt(0)); + this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1)); + } + + public DERBitString getSeed() + { + return this.seed; + } + + public ASN1Integer getPgenCounter() + { + return this.pgenCounter; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.seed); + v.add(this.pgenCounter); + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/ECNamedCurveTable.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/ECNamedCurveTable.java new file mode 100644 index 000000000..e60629d9d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/ECNamedCurveTable.java @@ -0,0 +1,99 @@ +package org.spongycastle.asn1.x9; + +import java.util.Enumeration; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; + +/** + * A general class that reads all X9.62 style EC curve tables. + */ +public class ECNamedCurveTable +{ + /** + * return a X9ECParameters object representing the passed in named + * curve. The routine returns null if the curve is not present. + * + * @param name the name of the curve requested + * @return an X9ECParameters object or null if the curve is not available. + */ + public static X9ECParameters getByName( + String name) + { + X9ECParameters ecP = X962NamedCurves.getByName(name); + + if (ecP == null) + { + ecP = SECNamedCurves.getByName(name); + } + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.getByName(name); + } + + if (ecP == null) + { + ecP = NISTNamedCurves.getByName(name); + } + + return ecP; + } + + /** + * return a X9ECParameters object representing the passed in named + * curve. + * + * @param oid the object id of the curve requested + * @return an X9ECParameters object or null if the curve is not available. + */ + public static X9ECParameters getByOID( + ASN1ObjectIdentifier oid) + { + X9ECParameters ecP = X962NamedCurves.getByOID(oid); + + if (ecP == null) + { + ecP = SECNamedCurves.getByOID(oid); + } + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.getByOID(oid); + } + + // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup + + return ecP; + } + + /** + * return an enumeration of the names of the available curves. + * + * @return an enumeration of the names of the available curves. + */ + public static Enumeration getNames() + { + Vector v = new Vector(); + + addEnumeration(v, X962NamedCurves.getNames()); + addEnumeration(v, SECNamedCurves.getNames()); + addEnumeration(v, NISTNamedCurves.getNames()); + addEnumeration(v, TeleTrusTNamedCurves.getNames()); + + return v.elements(); + } + + private static void addEnumeration( + Vector v, + Enumeration e) + { + while (e.hasMoreElements()) + { + v.addElement(e.nextElement()); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/KeySpecificInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/KeySpecificInfo.java new file mode 100644 index 000000000..3a7f95173 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/KeySpecificInfo.java @@ -0,0 +1,68 @@ +package org.spongycastle.asn1.x9; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * ASN.1 def for Diffie-Hellman key exchange KeySpecificInfo structure. See + * RFC 2631, or X9.42, for further details. + */ +public class KeySpecificInfo + extends ASN1Object +{ + private ASN1ObjectIdentifier algorithm; + private ASN1OctetString counter; + + public KeySpecificInfo( + ASN1ObjectIdentifier algorithm, + ASN1OctetString counter) + { + this.algorithm = algorithm; + this.counter = counter; + } + + public KeySpecificInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + algorithm = (ASN1ObjectIdentifier)e.nextElement(); + counter = (ASN1OctetString)e.nextElement(); + } + + public ASN1ObjectIdentifier getAlgorithm() + { + return algorithm; + } + + public ASN1OctetString getCounter() + { + return counter; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * KeySpecificInfo ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * counter OCTET STRING SIZE (4..4) + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(algorithm); + v.add(counter); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/OtherInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/OtherInfo.java new file mode 100644 index 000000000..3506c8686 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/OtherInfo.java @@ -0,0 +1,96 @@ +package org.spongycastle.asn1.x9; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * ANS.1 def for Diffie-Hellman key exchange OtherInfo structure. See + * RFC 2631, or X9.42, for further details. + */ +public class OtherInfo + extends ASN1Object +{ + private KeySpecificInfo keyInfo; + private ASN1OctetString partyAInfo; + private ASN1OctetString suppPubInfo; + + public OtherInfo( + KeySpecificInfo keyInfo, + ASN1OctetString partyAInfo, + ASN1OctetString suppPubInfo) + { + this.keyInfo = keyInfo; + this.partyAInfo = partyAInfo; + this.suppPubInfo = suppPubInfo; + } + + public OtherInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + keyInfo = new KeySpecificInfo((ASN1Sequence)e.nextElement()); + + while (e.hasMoreElements()) + { + DERTaggedObject o = (DERTaggedObject)e.nextElement(); + + if (o.getTagNo() == 0) + { + partyAInfo = (ASN1OctetString)o.getObject(); + } + else if (o.getTagNo() == 2) + { + suppPubInfo = (ASN1OctetString)o.getObject(); + } + } + } + + public KeySpecificInfo getKeyInfo() + { + return keyInfo; + } + + public ASN1OctetString getPartyAInfo() + { + return partyAInfo; + } + + public ASN1OctetString getSuppPubInfo() + { + return suppPubInfo; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *+ * OtherInfo ::= SEQUENCE { + * keyInfo KeySpecificInfo, + * partyAInfo [0] OCTET STRING OPTIONAL, + * suppPubInfo [2] OCTET STRING + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(keyInfo); + + if (partyAInfo != null) + { + v.add(new DERTaggedObject(0, partyAInfo)); + } + + v.add(new DERTaggedObject(2, suppPubInfo)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X962NamedCurves.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X962NamedCurves.java new file mode 100644 index 000000000..aea4c032a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X962NamedCurves.java @@ -0,0 +1,621 @@ +package org.spongycastle.asn1.x9; + +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; + + +/** + * table of the current named curves defined in X.962 EC-DSA. + */ +public class X962NamedCurves +{ + static X9ECParametersHolder prime192v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp192v1 = new ECCurve.Fp( + new BigInteger("6277101735386680763835789423207666416083908700390324961279"), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), + new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); + + return new X9ECParameters( + cFp192v1, + cFp192v1.decodePoint( + Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), + new BigInteger("ffffffffffffffffffffffff99def836146bc9b1b4d22831", 16), + BigInteger.valueOf(1), + Hex.decode("3045AE6FC8422f64ED579528D38120EAE12196D5")); + } + }; + + static X9ECParametersHolder prime192v2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp192v2 = new ECCurve.Fp( + new BigInteger("6277101735386680763835789423207666416083908700390324961279"), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), + new BigInteger("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953", 16)); + + return new X9ECParameters( + cFp192v2, + cFp192v2.decodePoint( + Hex.decode("03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a")), + new BigInteger("fffffffffffffffffffffffe5fb1a724dc80418648d8dd31", 16), + BigInteger.valueOf(1), + Hex.decode("31a92ee2029fd10d901b113e990710f0d21ac6b6")); + } + }; + + static X9ECParametersHolder prime192v3 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp192v3 = new ECCurve.Fp( + new BigInteger("6277101735386680763835789423207666416083908700390324961279"), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), + new BigInteger("22123dc2395a05caa7423daeccc94760a7d462256bd56916", 16)); + + return new X9ECParameters( + cFp192v3, + cFp192v3.decodePoint( + Hex.decode("027d29778100c65a1da1783716588dce2b8b4aee8e228f1896")), + new BigInteger("ffffffffffffffffffffffff7a62d031c83f4294f640ec13", 16), + BigInteger.valueOf(1), + Hex.decode("c469684435deb378c4b65ca9591e2a5763059a2e")); + } + }; + + static X9ECParametersHolder prime239v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp239v1 = new ECCurve.Fp( + new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), + new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), + new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); + + return new X9ECParameters( + cFp239v1, + cFp239v1.decodePoint( + Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), + new BigInteger("7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b", 16), + BigInteger.valueOf(1), + Hex.decode("e43bb460f0b80cc0c0b075798e948060f8321b7d")); + } + }; + + static X9ECParametersHolder prime239v2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp239v2 = new ECCurve.Fp( + new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), + new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), + new BigInteger("617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c", 16)); + + return new X9ECParameters( + cFp239v2, + cFp239v2.decodePoint( + Hex.decode("0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7")), + new BigInteger("7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063", 16), + BigInteger.valueOf(1), + Hex.decode("e8b4011604095303ca3b8099982be09fcb9ae616")); + } + }; + + static X9ECParametersHolder prime239v3 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp239v3 = new ECCurve.Fp( + new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), + new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), + new BigInteger("255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e", 16)); + + return new X9ECParameters( + cFp239v3, + cFp239v3.decodePoint( + Hex.decode("036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a")), + new BigInteger("7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551", 16), + BigInteger.valueOf(1), + Hex.decode("7d7374168ffe3471b60a857686a19475d3bfa2ff")); + } + }; + + static X9ECParametersHolder prime256v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + ECCurve cFp256v1 = new ECCurve.Fp( + new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)); + + return new X9ECParameters( + cFp256v1, + cFp256v1.decodePoint( + Hex.decode("036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + BigInteger.valueOf(1), + Hex.decode("c49d360886e704936a6678e1139d26b7819f7e90")); + } + }; + + /* + * F2m Curves + */ + static X9ECParametersHolder c2pnb163v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m163v1n = new BigInteger("0400000000000000000001E60FC8821CC74DAEAFC1", 16); + BigInteger c2m163v1h = BigInteger.valueOf(2); + + ECCurve c2m163v1 = new ECCurve.F2m( + 163, + 1, 2, 8, + new BigInteger("072546B5435234A422E0789675F432C89435DE5242", 16), + new BigInteger("00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9", 16), + c2m163v1n, c2m163v1h); + + return new X9ECParameters( + c2m163v1, + c2m163v1.decodePoint( + Hex.decode("0307AF69989546103D79329FCC3D74880F33BBE803CB")), + c2m163v1n, c2m163v1h, + Hex.decode("D2C0FB15760860DEF1EEF4D696E6768756151754")); + } + }; + + static X9ECParametersHolder c2pnb163v2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m163v2n = new BigInteger("03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7", 16); + BigInteger c2m163v2h = BigInteger.valueOf(2); + + ECCurve c2m163v2 = new ECCurve.F2m( + 163, + 1, 2, 8, + new BigInteger("0108B39E77C4B108BED981ED0E890E117C511CF072", 16), + new BigInteger("0667ACEB38AF4E488C407433FFAE4F1C811638DF20", 16), + c2m163v2n, c2m163v2h); + + return new X9ECParameters( + c2m163v2, + c2m163v2.decodePoint( + Hex.decode("030024266E4EB5106D0A964D92C4860E2671DB9B6CC5")), + c2m163v2n, c2m163v2h, + null); + } + }; + + static X9ECParametersHolder c2pnb163v3 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m163v3n = new BigInteger("03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309", 16); + BigInteger c2m163v3h = BigInteger.valueOf(2); + + ECCurve c2m163v3 = new ECCurve.F2m( + 163, + 1, 2, 8, + new BigInteger("07A526C63D3E25A256A007699F5447E32AE456B50E", 16), + new BigInteger("03F7061798EB99E238FD6F1BF95B48FEEB4854252B", 16), + c2m163v3n, c2m163v3h); + + return new X9ECParameters( + c2m163v3, + c2m163v3.decodePoint( + Hex.decode("0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB")), + c2m163v3n, c2m163v3h, + null); + } + }; + + static X9ECParametersHolder c2pnb176w1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m176w1n = new BigInteger("010092537397ECA4F6145799D62B0A19CE06FE26AD", 16); + BigInteger c2m176w1h = BigInteger.valueOf(0xFF6E); + + ECCurve c2m176w1 = new ECCurve.F2m( + 176, + 1, 2, 43, + new BigInteger("00E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B", 16), + new BigInteger("005DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2", 16), + c2m176w1n, c2m176w1h); + + return new X9ECParameters( + c2m176w1, + c2m176w1.decodePoint( + Hex.decode("038D16C2866798B600F9F08BB4A8E860F3298CE04A5798")), + c2m176w1n, c2m176w1h, + null); + } + }; + + static X9ECParametersHolder c2tnb191v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m191v1n = new BigInteger("40000000000000000000000004A20E90C39067C893BBB9A5", 16); + BigInteger c2m191v1h = BigInteger.valueOf(2); + + ECCurve c2m191v1 = new ECCurve.F2m( + 191, + 9, + new BigInteger("2866537B676752636A68F56554E12640276B649EF7526267", 16), + new BigInteger("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", 16), + c2m191v1n, c2m191v1h); + + return new X9ECParameters( + c2m191v1, + c2m191v1.decodePoint( + Hex.decode("0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D")), + c2m191v1n, c2m191v1h, + Hex.decode("4E13CA542744D696E67687561517552F279A8C84")); + } + }; + + static X9ECParametersHolder c2tnb191v2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m191v2n = new BigInteger("20000000000000000000000050508CB89F652824E06B8173", 16); + BigInteger c2m191v2h = BigInteger.valueOf(4); + + ECCurve c2m191v2 = new ECCurve.F2m( + 191, + 9, + new BigInteger("401028774D7777C7B7666D1366EA432071274F89FF01E718", 16), + new BigInteger("0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01", 16), + c2m191v2n, c2m191v2h); + + return new X9ECParameters( + c2m191v2, + c2m191v2.decodePoint( + Hex.decode("023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10")), + c2m191v2n, c2m191v2h, + null); + } + }; + + static X9ECParametersHolder c2tnb191v3 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m191v3n = new BigInteger("155555555555555555555555610C0B196812BFB6288A3EA3", 16); + BigInteger c2m191v3h = BigInteger.valueOf(6); + + ECCurve c2m191v3 = new ECCurve.F2m( + 191, + 9, + new BigInteger("6C01074756099122221056911C77D77E77A777E7E7E77FCB", 16), + new BigInteger("71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8", 16), + c2m191v3n, c2m191v3h); + + return new X9ECParameters( + c2m191v3, + c2m191v3.decodePoint( + Hex.decode("03375D4CE24FDE434489DE8746E71786015009E66E38A926DD")), + c2m191v3n, c2m191v3h, + null); + } + }; + + static X9ECParametersHolder c2pnb208w1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m208w1n = new BigInteger("0101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D", 16); + BigInteger c2m208w1h = BigInteger.valueOf(0xFE48); + + ECCurve c2m208w1 = new ECCurve.F2m( + 208, + 1, 2, 83, + new BigInteger("0", 16), + new BigInteger("00C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E", 16), + c2m208w1n, c2m208w1h); + + return new X9ECParameters( + c2m208w1, + c2m208w1.decodePoint( + Hex.decode("0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A")), + c2m208w1n, c2m208w1h, + null); + } + }; + + static X9ECParametersHolder c2tnb239v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m239v1n = new BigInteger("2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447", 16); + BigInteger c2m239v1h = BigInteger.valueOf(4); + + ECCurve c2m239v1 = new ECCurve.F2m( + 239, + 36, + new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), + new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16), + c2m239v1n, c2m239v1h); + + return new X9ECParameters( + c2m239v1, + c2m239v1.decodePoint( + Hex.decode("0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D")), + c2m239v1n, c2m239v1h, + null); + } + }; + + static X9ECParametersHolder c2tnb239v2 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m239v2n = new BigInteger("1555555555555555555555555555553C6F2885259C31E3FCDF154624522D", 16); + BigInteger c2m239v2h = BigInteger.valueOf(6); + + ECCurve c2m239v2 = new ECCurve.F2m( + 239, + 36, + new BigInteger("4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F", 16), + new BigInteger("5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B", 16), + c2m239v2n, c2m239v2h); + + return new X9ECParameters( + c2m239v2, + c2m239v2.decodePoint( + Hex.decode("0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205")), + c2m239v2n, c2m239v2h, + null); + } + }; + + static X9ECParametersHolder c2tnb239v3 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m239v3n = new BigInteger("0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF", 16); + BigInteger c2m239v3h = BigInteger.valueOf(10); + + ECCurve c2m239v3 = new ECCurve.F2m( + 239, + 36, + new BigInteger("01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F", 16), + new BigInteger("6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40", 16), + c2m239v3n, c2m239v3h); + + return new X9ECParameters( + c2m239v3, + c2m239v3.decodePoint( + Hex.decode("0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92")), + c2m239v3n, c2m239v3h, + null); + } + }; + + static X9ECParametersHolder c2pnb272w1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m272w1n = new BigInteger("0100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521", 16); + BigInteger c2m272w1h = BigInteger.valueOf(0xFF06); + + ECCurve c2m272w1 = new ECCurve.F2m( + 272, + 1, 3, 56, + new BigInteger("0091A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20", 16), + new BigInteger("7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7", 16), + c2m272w1n, c2m272w1h); + + return new X9ECParameters( + c2m272w1, + c2m272w1.decodePoint( + Hex.decode("026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D")), + c2m272w1n, c2m272w1h, + null); + } + }; + + static X9ECParametersHolder c2pnb304w1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m304w1n = new BigInteger("0101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D", 16); + BigInteger c2m304w1h = BigInteger.valueOf(0xFE2E); + + ECCurve c2m304w1 = new ECCurve.F2m( + 304, + 1, 2, 11, + new BigInteger("00FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681", 16), + new BigInteger("00BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE", 16), + c2m304w1n, c2m304w1h); + + return new X9ECParameters( + c2m304w1, + c2m304w1.decodePoint( + Hex.decode("02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614")), + c2m304w1n, c2m304w1h, + null); + } + }; + + static X9ECParametersHolder c2tnb359v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m359v1n = new BigInteger("01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B", 16); + BigInteger c2m359v1h = BigInteger.valueOf(0x4C); + + ECCurve c2m359v1 = new ECCurve.F2m( + 359, + 68, + new BigInteger("5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557", 16), + new BigInteger("2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988", 16), + c2m359v1n, c2m359v1h); + + return new X9ECParameters( + c2m359v1, + c2m359v1.decodePoint( + Hex.decode("033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097")), + c2m359v1n, c2m359v1h, + null); + } + }; + + static X9ECParametersHolder c2pnb368w1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m368w1n = new BigInteger("010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967", 16); + BigInteger c2m368w1h = BigInteger.valueOf(0xFF70); + + ECCurve c2m368w1 = new ECCurve.F2m( + 368, + 1, 2, 85, + new BigInteger("00E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D", 16), + new BigInteger("00FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A", 16), + c2m368w1n, c2m368w1h); + + return new X9ECParameters( + c2m368w1, + c2m368w1.decodePoint( + Hex.decode("021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F")), + c2m368w1n, c2m368w1h, + null); + } + }; + + static X9ECParametersHolder c2tnb431r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger c2m431r1n = new BigInteger("0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91", 16); + BigInteger c2m431r1h = BigInteger.valueOf(0x2760); + + ECCurve c2m431r1 = new ECCurve.F2m( + 431, + 120, + new BigInteger("1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F", 16), + new BigInteger("10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618", 16), + c2m431r1n, c2m431r1h); + + return new X9ECParameters( + c2m431r1, + c2m431r1.decodePoint( + Hex.decode("02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7")), + c2m431r1n, c2m431r1h, + null); + } + }; + + static final Hashtable objIds = new Hashtable(); + static final Hashtable curves = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder) + { + objIds.put(name, oid); + names.put(oid, name); + curves.put(oid, holder); + } + + static + { + defineCurve("prime192v1", X9ObjectIdentifiers.prime192v1, prime192v1); + defineCurve("prime192v2", X9ObjectIdentifiers.prime192v2, prime192v2); + defineCurve("prime192v3", X9ObjectIdentifiers.prime192v3, prime192v3); + defineCurve("prime239v1", X9ObjectIdentifiers.prime239v1, prime239v1); + defineCurve("prime239v2", X9ObjectIdentifiers.prime239v2, prime239v2); + defineCurve("prime239v3", X9ObjectIdentifiers.prime239v3, prime239v3); + defineCurve("prime256v1", X9ObjectIdentifiers.prime256v1, prime256v1); + defineCurve("c2pnb163v1", X9ObjectIdentifiers.c2pnb163v1, c2pnb163v1); + defineCurve("c2pnb163v2", X9ObjectIdentifiers.c2pnb163v2, c2pnb163v2); + defineCurve("c2pnb163v3", X9ObjectIdentifiers.c2pnb163v3, c2pnb163v3); + defineCurve("c2pnb176w1", X9ObjectIdentifiers.c2pnb176w1, c2pnb176w1); + defineCurve("c2tnb191v1", X9ObjectIdentifiers.c2tnb191v1, c2tnb191v1); + defineCurve("c2tnb191v2", X9ObjectIdentifiers.c2tnb191v2, c2tnb191v2); + defineCurve("c2tnb191v3", X9ObjectIdentifiers.c2tnb191v3, c2tnb191v3); + defineCurve("c2pnb208w1", X9ObjectIdentifiers.c2pnb208w1, c2pnb208w1); + defineCurve("c2tnb239v1", X9ObjectIdentifiers.c2tnb239v1, c2tnb239v1); + defineCurve("c2tnb239v2", X9ObjectIdentifiers.c2tnb239v2, c2tnb239v2); + defineCurve("c2tnb239v3", X9ObjectIdentifiers.c2tnb239v3, c2tnb239v3); + defineCurve("c2pnb272w1", X9ObjectIdentifiers.c2pnb272w1, c2pnb272w1); + defineCurve("c2pnb304w1", X9ObjectIdentifiers.c2pnb304w1, c2pnb304w1); + defineCurve("c2tnb359v1", X9ObjectIdentifiers.c2tnb359v1, c2tnb359v1); + defineCurve("c2pnb368w1", X9ObjectIdentifiers.c2pnb368w1, c2pnb368w1); + defineCurve("c2tnb431r1", X9ObjectIdentifiers.c2tnb431r1, c2tnb431r1); + } + + public static X9ECParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + + if (oid != null) + { + return getByOID(oid); + } + + return null; + } + + /** + * return the X9ECParameters object for the named curve represented by + * the passed in object identifier. Null if the curve isn't present. + * + * @param oid an object identifier representing a named curve, if present. + */ + public static X9ECParameters getByOID( + ASN1ObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid); + + if (holder != null) + { + return holder.getParameters(); + } + + return null; + } + + /** + * return the object identifier signified by the passed in name. Null + * if there is no object identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static ASN1ObjectIdentifier getOID( + String name) + { + return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName( + ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X962Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X962Parameters.java new file mode 100644 index 000000000..49e73a160 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X962Parameters.java @@ -0,0 +1,86 @@ +package org.spongycastle.asn1.x9; + +import org.spongycastle.asn1.ASN1Choice; +import org.spongycastle.asn1.ASN1Null; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1TaggedObject; + +public class X962Parameters + extends ASN1Object + implements ASN1Choice +{ + private ASN1Primitive params = null; + + public static X962Parameters getInstance( + Object obj) + { + if (obj == null || obj instanceof X962Parameters) + { + return (X962Parameters)obj; + } + + if (obj instanceof ASN1Primitive) + { + return new X962Parameters((ASN1Primitive)obj); + } + + throw new IllegalArgumentException("unknown object in getInstance()"); + } + + public static X962Parameters getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); // must be explicitly tagged + } + + public X962Parameters( + X9ECParameters ecParameters) + { + this.params = ecParameters.toASN1Primitive(); + } + + public X962Parameters( + ASN1ObjectIdentifier namedCurve) + { + this.params = namedCurve; + } + + public X962Parameters( + ASN1Primitive obj) + { + this.params = obj; + } + + public boolean isNamedCurve() + { + return (params instanceof ASN1ObjectIdentifier); + } + + public boolean isImplicitlyCA() + { + return (params instanceof ASN1Null); + } + + public ASN1Primitive getParameters() + { + return params; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *+ * Parameters ::= CHOICE { + * ecParameters ECParameters, + * namedCurve CURVES.&id({CurveNames}), + * implicitlyCA NULL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + return (ASN1Primitive)params; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9Curve.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9Curve.java new file mode 100644 index 000000000..f9673c2f3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9Curve.java @@ -0,0 +1,161 @@ +package org.spongycastle.asn1.x9; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.math.ec.ECCurve; + +/** + * ASN.1 def for Elliptic-Curve Curve structure. See + * X9.62, for further details. + */ +public class X9Curve + extends ASN1Object + implements X9ObjectIdentifiers +{ + private ECCurve curve; + private byte[] seed; + private ASN1ObjectIdentifier fieldIdentifier = null; + + public X9Curve( + ECCurve curve) + { + this.curve = curve; + this.seed = null; + setFieldIdentifier(); + } + + public X9Curve( + ECCurve curve, + byte[] seed) + { + this.curve = curve; + this.seed = seed; + setFieldIdentifier(); + } + + public X9Curve( + X9FieldID fieldID, + ASN1Sequence seq) + { + fieldIdentifier = fieldID.getIdentifier(); + if (fieldIdentifier.equals(prime_field)) + { + BigInteger p = ((ASN1Integer)fieldID.getParameters()).getValue(); + X9FieldElement x9A = new X9FieldElement(p, (ASN1OctetString)seq.getObjectAt(0)); + X9FieldElement x9B = new X9FieldElement(p, (ASN1OctetString)seq.getObjectAt(1)); + curve = new ECCurve.Fp(p, x9A.getValue().toBigInteger(), x9B.getValue().toBigInteger()); + } + else if (fieldIdentifier.equals(characteristic_two_field)) + { + // Characteristic two field + ASN1Sequence parameters = ASN1Sequence.getInstance(fieldID.getParameters()); + int m = ((ASN1Integer)parameters.getObjectAt(0)).getValue(). + intValue(); + ASN1ObjectIdentifier representation + = (ASN1ObjectIdentifier)parameters.getObjectAt(1); + + int k1 = 0; + int k2 = 0; + int k3 = 0; + + if (representation.equals(tpBasis)) + { + // Trinomial basis representation + k1 = ASN1Integer.getInstance(parameters.getObjectAt(2)).getValue().intValue(); + } + else if (representation.equals(ppBasis)) + { + // Pentanomial basis representation + ASN1Sequence pentanomial = ASN1Sequence.getInstance(parameters.getObjectAt(2)); + k1 = ASN1Integer.getInstance(pentanomial.getObjectAt(0)).getValue().intValue(); + k2 = ASN1Integer.getInstance(pentanomial.getObjectAt(1)).getValue().intValue(); + k3 = ASN1Integer.getInstance(pentanomial.getObjectAt(2)).getValue().intValue(); + } + else + { + throw new IllegalArgumentException("This type of EC basis is not implemented"); + } + X9FieldElement x9A = new X9FieldElement(m, k1, k2, k3, (ASN1OctetString)seq.getObjectAt(0)); + X9FieldElement x9B = new X9FieldElement(m, k1, k2, k3, (ASN1OctetString)seq.getObjectAt(1)); + // TODO Is it possible to get the order (n) and cofactor(h) too? + curve = new ECCurve.F2m(m, k1, k2, k3, x9A.getValue().toBigInteger(), x9B.getValue().toBigInteger()); + } + else + { + throw new IllegalArgumentException("This type of ECCurve is not implemented"); + } + + if (seq.size() == 3) + { + seed = ((DERBitString)seq.getObjectAt(2)).getBytes(); + } + } + + private void setFieldIdentifier() + { + if (curve instanceof ECCurve.Fp) + { + fieldIdentifier = prime_field; + } + else if (curve instanceof ECCurve.F2m) + { + fieldIdentifier = characteristic_two_field; + } + else + { + throw new IllegalArgumentException("This type of ECCurve is not implemented"); + } + } + + public ECCurve getCurve() + { + return curve; + } + + public byte[] getSeed() + { + return seed; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *+ * Curve ::= SEQUENCE { + * a FieldElement, + * b FieldElement, + * seed BIT STRING OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (fieldIdentifier.equals(prime_field)) + { + v.add(new X9FieldElement(curve.getA()).toASN1Primitive()); + v.add(new X9FieldElement(curve.getB()).toASN1Primitive()); + } + else if (fieldIdentifier.equals(characteristic_two_field)) + { + v.add(new X9FieldElement(curve.getA()).toASN1Primitive()); + v.add(new X9FieldElement(curve.getB()).toASN1Primitive()); + } + + if (seed != null) + { + v.add(new DERBitString(seed)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECParameters.java new file mode 100644 index 000000000..80eb35fe2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECParameters.java @@ -0,0 +1,186 @@ +package org.spongycastle.asn1.x9; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +/** + * ASN.1 def for Elliptic-Curve ECParameters structure. See + * X9.62, for further details. + */ +public class X9ECParameters + extends ASN1Object + implements X9ObjectIdentifiers +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + + private X9FieldID fieldID; + private ECCurve curve; + private ECPoint g; + private BigInteger n; + private BigInteger h; + private byte[] seed; + + private X9ECParameters( + ASN1Sequence seq) + { + if (!(seq.getObjectAt(0) instanceof ASN1Integer) + || !((ASN1Integer)seq.getObjectAt(0)).getValue().equals(ONE)) + { + throw new IllegalArgumentException("bad version in X9ECParameters"); + } + + X9Curve x9c = new X9Curve( + X9FieldID.getInstance(seq.getObjectAt(1)), + ASN1Sequence.getInstance(seq.getObjectAt(2))); + + this.curve = x9c.getCurve(); + Object p = seq.getObjectAt(3); + + if (p instanceof X9ECPoint) + { + this.g = ((X9ECPoint)p).getPoint(); + } + else + { + this.g = new X9ECPoint(curve, (ASN1OctetString)p).getPoint(); + } + + this.n = ((ASN1Integer)seq.getObjectAt(4)).getValue(); + this.seed = x9c.getSeed(); + + if (seq.size() == 6) + { + this.h = ((ASN1Integer)seq.getObjectAt(5)).getValue(); + } + } + + public static X9ECParameters getInstance(Object obj) + { + if (obj instanceof X9ECParameters) + { + return (X9ECParameters)obj; + } + + if (obj != null) + { + return new X9ECParameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public X9ECParameters( + ECCurve curve, + ECPoint g, + BigInteger n) + { + this(curve, g, n, ONE, null); + } + + public X9ECParameters( + ECCurve curve, + ECPoint g, + BigInteger n, + BigInteger h) + { + this(curve, g, n, h, null); + } + + public X9ECParameters( + ECCurve curve, + ECPoint g, + BigInteger n, + BigInteger h, + byte[] seed) + { + this.curve = curve; + this.g = g.normalize(); + this.n = n; + this.h = h; + this.seed = seed; + + if (curve instanceof ECCurve.Fp) + { + this.fieldID = new X9FieldID(((ECCurve.Fp)curve).getQ()); + } + else + { + if (curve instanceof ECCurve.F2m) + { + ECCurve.F2m curveF2m = (ECCurve.F2m)curve; + this.fieldID = new X9FieldID(curveF2m.getM(), curveF2m.getK1(), + curveF2m.getK2(), curveF2m.getK3()); + } + } + } + + public ECCurve getCurve() + { + return curve; + } + + public ECPoint getG() + { + return g; + } + + public BigInteger getN() + { + return n; + } + + public BigInteger getH() + { + if (h == null) + { + return ONE; // TODO - this should be calculated, it will cause issues with custom curves. + } + + return h; + } + + public byte[] getSeed() + { + return seed; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *+ * ECParameters ::= SEQUENCE { + * version INTEGER { ecpVer1(1) } (ecpVer1), + * fieldID FieldID {{FieldTypes}}, + * curve X9Curve, + * base X9ECPoint, + * order INTEGER, + * cofactor INTEGER OPTIONAL + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(1)); + v.add(fieldID); + v.add(new X9Curve(curve, seed)); + v.add(new X9ECPoint(g)); + v.add(new ASN1Integer(n)); + + if (h != null) + { + v.add(new ASN1Integer(h)); + } + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECParametersHolder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECParametersHolder.java new file mode 100644 index 000000000..50e4f1e1e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECParametersHolder.java @@ -0,0 +1,18 @@ +package org.spongycastle.asn1.x9; + +public abstract class X9ECParametersHolder +{ + private X9ECParameters params; + + public X9ECParameters getParameters() + { + if (params == null) + { + params = createParameters(); + } + + return params; + } + + protected abstract X9ECParameters createParameters(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECPoint.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECPoint.java new file mode 100644 index 000000000..d984bcd43 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ECPoint.java @@ -0,0 +1,48 @@ +package org.spongycastle.asn1.x9; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +/** + * class for describing an ECPoint as a DER object. + */ +public class X9ECPoint + extends ASN1Object +{ + ECPoint p; + + public X9ECPoint( + ECPoint p) + { + this.p = p.normalize(); + } + + public X9ECPoint( + ECCurve c, + ASN1OctetString s) + { + this.p = c.decodePoint(s.getOctets()); + } + + public ECPoint getPoint() + { + return p; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *+ * ECPoint ::= OCTET STRING + *+ *+ * Octet string produced using ECPoint.getEncoded(). + */ + public ASN1Primitive toASN1Primitive() + { + return new DEROctetString(p.getEncoded()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9FieldElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9FieldElement.java new file mode 100644 index 000000000..dd5b4dffe --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9FieldElement.java @@ -0,0 +1,64 @@ +package org.spongycastle.asn1.x9; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.math.ec.ECFieldElement; + +/** + * class for processing an FieldElement as a DER object. + */ +public class X9FieldElement + extends ASN1Object +{ + protected ECFieldElement f; + + private static X9IntegerConverter converter = new X9IntegerConverter(); + + public X9FieldElement(ECFieldElement f) + { + this.f = f; + } + + public X9FieldElement(BigInteger p, ASN1OctetString s) + { + this(new ECFieldElement.Fp(p, new BigInteger(1, s.getOctets()))); + } + + public X9FieldElement(int m, int k1, int k2, int k3, ASN1OctetString s) + { + this(new ECFieldElement.F2m(m, k1, k2, k3, new BigInteger(1, s.getOctets()))); + } + + public ECFieldElement getValue() + { + return f; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+ * FieldElement ::= OCTET STRING + *+ *+ *
+ *
+ */ + public ASN1Primitive toASN1Primitive() + { + int byteCount = converter.getByteLength(f); + byte[] paddedBigInteger = converter.integerToBytes(f.toBigInteger(), byteCount); + + return new DEROctetString(paddedBigInteger); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9FieldID.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9FieldID.java new file mode 100644 index 000000000..e73fad726 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9FieldID.java @@ -0,0 +1,124 @@ +package org.spongycastle.asn1.x9; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; + +/** + * ASN.1 def for Elliptic-Curve Field ID structure. See + * X9.62, for further details. + */ +public class X9FieldID + extends ASN1Object + implements X9ObjectIdentifiers +{ + private ASN1ObjectIdentifier id; + private ASN1Primitive parameters; + + /** + * Constructor for elliptic curves over prime fields + *- if q is an odd prime then the field element is + * processed as an Integer and converted to an octet string + * according to x 9.62 4.3.1.
+ *- if q is 2m then the bit string + * contained in the field element is converted into an octet + * string with the same ordering padded at the front if necessary. + *
+ *F2
. + * @param primeP The primep
defining the prime field. + */ + public X9FieldID(BigInteger primeP) + { + this.id = prime_field; + this.parameters = new ASN1Integer(primeP); + } + + /** + * Constructor for elliptic curves over binary fields + *F2m
. + * @param m The exponentm
of + *F2m
. + * @param k1 The integerk1
wherexm + + * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomialf(z)
. + * @param k2 The integerk2
wherexm + + * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomialf(z)
. + * @param k3 The integerk3
wherexm + + * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomialf(z)
.. + */ + public X9FieldID(int m, int k1, int k2, int k3) + { + this.id = characteristic_two_field; + ASN1EncodableVector fieldIdParams = new ASN1EncodableVector(); + fieldIdParams.add(new ASN1Integer(m)); + + if (k2 == 0) + { + fieldIdParams.add(tpBasis); + fieldIdParams.add(new ASN1Integer(k1)); + } + else + { + fieldIdParams.add(ppBasis); + ASN1EncodableVector pentanomialParams = new ASN1EncodableVector(); + pentanomialParams.add(new ASN1Integer(k1)); + pentanomialParams.add(new ASN1Integer(k2)); + pentanomialParams.add(new ASN1Integer(k3)); + fieldIdParams.add(new DERSequence(pentanomialParams)); + } + + this.parameters = new DERSequence(fieldIdParams); + } + + private X9FieldID( + ASN1Sequence seq) + { + this.id = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); + this.parameters = seq.getObjectAt(1).toASN1Primitive(); + } + + public static X9FieldID getInstance(Object obj) + { + if (obj instanceof X9FieldID) + { + return (X9FieldID)obj; + } + + if (obj != null) + { + return new X9FieldID(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ASN1ObjectIdentifier getIdentifier() + { + return id; + } + + public ASN1Primitive getParameters() + { + return parameters; + } + + /** + * Produce a DER encoding of the following structure. + *+ * FieldID ::= SEQUENCE { + * fieldType FIELD-ID.&id({IOSet}), + * parameters FIELD-ID.&Type({IOSet}{@fieldType}) + * } + *+ */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(this.id); + v.add(this.parameters); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9IntegerConverter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9IntegerConverter.java new file mode 100644 index 000000000..66dcc59b1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9IntegerConverter.java @@ -0,0 +1,47 @@ +package org.spongycastle.asn1.x9; + +import java.math.BigInteger; + +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECFieldElement; + +public class X9IntegerConverter +{ + public int getByteLength( + ECCurve c) + { + return (c.getFieldSize() + 7) / 8; + } + + public int getByteLength( + ECFieldElement fe) + { + return (fe.getFieldSize() + 7) / 8; + } + + public byte[] integerToBytes( + BigInteger s, + int qLength) + { + byte[] bytes = s.toByteArray(); + + if (qLength < bytes.length) + { + byte[] tmp = new byte[qLength]; + + System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length); + + return tmp; + } + else if (qLength > bytes.length) + { + byte[] tmp = new byte[qLength]; + + System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length); + + return tmp; + } + + return bytes; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ObjectIdentifiers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ObjectIdentifiers.java new file mode 100644 index 000000000..1b67cb711 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/x9/X9ObjectIdentifiers.java @@ -0,0 +1,207 @@ +package org.spongycastle.asn1.x9; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +/** + * + * X9.62 + *+ * ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + * us(840) ansi-x962(10045) } + *+ */ +public interface X9ObjectIdentifiers +{ + /** Base OID: 1.2.840.10045 */ + static final ASN1ObjectIdentifier ansi_X9_62 = new ASN1ObjectIdentifier("1.2.840.10045"); + + /** OID: 1.2.840.10045.1 */ + static final ASN1ObjectIdentifier id_fieldType = ansi_X9_62.branch("1"); + + /** OID: 1.2.840.10045.1.1 */ + static final ASN1ObjectIdentifier prime_field = id_fieldType.branch("1"); + + /** OID: 1.2.840.10045.1.2 */ + static final ASN1ObjectIdentifier characteristic_two_field = id_fieldType.branch("2"); + + /** OID: 1.2.840.10045.1.2.3.1 */ + static final ASN1ObjectIdentifier gnBasis = characteristic_two_field.branch("3.1"); + + /** OID: 1.2.840.10045.1.2.3.2 */ + static final ASN1ObjectIdentifier tpBasis = characteristic_two_field.branch("3.2"); + + /** OID: 1.2.840.10045.1.2.3.3 */ + static final ASN1ObjectIdentifier ppBasis = characteristic_two_field.branch("3.3"); + + /** OID: 1.2.840.10045.4 */ + static final ASN1ObjectIdentifier id_ecSigType = ansi_X9_62.branch("4"); + + /** OID: 1.2.840.10045.4.1 */ + static final ASN1ObjectIdentifier ecdsa_with_SHA1 = id_ecSigType.branch("1"); + + /** OID: 1.2.840.10045.2 */ + static final ASN1ObjectIdentifier id_publicKeyType = ansi_X9_62.branch("2"); + + /** OID: 1.2.840.10045.2.1 */ + static final ASN1ObjectIdentifier id_ecPublicKey = id_publicKeyType.branch("1"); + + /** OID: 1.2.840.10045.4.3 */ + static final ASN1ObjectIdentifier ecdsa_with_SHA2 = id_ecSigType.branch("3"); + + /** OID: 1.2.840.10045.4.3.1 */ + static final ASN1ObjectIdentifier ecdsa_with_SHA224 = ecdsa_with_SHA2.branch("1"); + + /** OID: 1.2.840.10045.4.3.2 */ + static final ASN1ObjectIdentifier ecdsa_with_SHA256 = ecdsa_with_SHA2.branch("2"); + + /** OID: 1.2.840.10045.4.3.3 */ + static final ASN1ObjectIdentifier ecdsa_with_SHA384 = ecdsa_with_SHA2.branch("3"); + + /** OID: 1.2.840.10045.4.3.4 */ + static final ASN1ObjectIdentifier ecdsa_with_SHA512 = ecdsa_with_SHA2.branch("4"); + + /** + * Named curves base + *+ * OID: 1.2.840.10045.1 + */ + static final ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3"); + + /** + * Two Curves + *
+ * OID: 1.2.840.10045.1.0 + */ + static final ASN1ObjectIdentifier cTwoCurve = ellipticCurve.branch("0"); + + /** Two Curve c2pnb163v1, OID: 1.2.840.10045.1.0.1 */ + static final ASN1ObjectIdentifier c2pnb163v1 = cTwoCurve.branch("1"); + /** Two Curve c2pnb163v2, OID: 1.2.840.10045.1.0.2 */ + static final ASN1ObjectIdentifier c2pnb163v2 = cTwoCurve.branch("2"); + /** Two Curve c2pnb163v3, OID: 1.2.840.10045.1.0.3 */ + static final ASN1ObjectIdentifier c2pnb163v3 = cTwoCurve.branch("3"); + /** Two Curve c2pnb176w1, OID: 1.2.840.10045.1.0.4 */ + static final ASN1ObjectIdentifier c2pnb176w1 = cTwoCurve.branch("4"); + /** Two Curve c2tnb191v1, OID: 1.2.840.10045.1.0.5 */ + static final ASN1ObjectIdentifier c2tnb191v1 = cTwoCurve.branch("5"); + /** Two Curve c2tnb191v2, OID: 1.2.840.10045.1.0.6 */ + static final ASN1ObjectIdentifier c2tnb191v2 = cTwoCurve.branch("6"); + /** Two Curve c2tnb191v3, OID: 1.2.840.10045.1.0.7 */ + static final ASN1ObjectIdentifier c2tnb191v3 = cTwoCurve.branch("7"); + /** Two Curve c2onb191v4, OID: 1.2.840.10045.1.0.8 */ + static final ASN1ObjectIdentifier c2onb191v4 = cTwoCurve.branch("8"); + /** Two Curve c2onb191v5, OID: 1.2.840.10045.1.0.9 */ + static final ASN1ObjectIdentifier c2onb191v5 = cTwoCurve.branch("9"); + /** Two Curve c2pnb208w1, OID: 1.2.840.10045.1.0.10 */ + static final ASN1ObjectIdentifier c2pnb208w1 = cTwoCurve.branch("10"); + /** Two Curve c2tnb239v1, OID: 1.2.840.10045.1.0.11 */ + static final ASN1ObjectIdentifier c2tnb239v1 = cTwoCurve.branch("11"); + /** Two Curve c2tnb239v2, OID: 1.2.840.10045.1.0.12 */ + static final ASN1ObjectIdentifier c2tnb239v2 = cTwoCurve.branch("12"); + /** Two Curve c2tnb239v3, OID: 1.2.840.10045.1.0.13 */ + static final ASN1ObjectIdentifier c2tnb239v3 = cTwoCurve.branch("13"); + /** Two Curve c2onb239v4, OID: 1.2.840.10045.1.0.14 */ + static final ASN1ObjectIdentifier c2onb239v4 = cTwoCurve.branch("14"); + /** Two Curve c2onb239v5, OID: 1.2.840.10045.1.0.15 */ + static final ASN1ObjectIdentifier c2onb239v5 = cTwoCurve.branch("15"); + /** Two Curve c2pnb272w1, OID: 1.2.840.10045.1.0.16 */ + static final ASN1ObjectIdentifier c2pnb272w1 = cTwoCurve.branch("16"); + /** Two Curve c2pnb304w1, OID: 1.2.840.10045.1.0.17 */ + static final ASN1ObjectIdentifier c2pnb304w1 = cTwoCurve.branch("17"); + /** Two Curve c2tnb359v1, OID: 1.2.840.10045.1.0.18 */ + static final ASN1ObjectIdentifier c2tnb359v1 = cTwoCurve.branch("18"); + /** Two Curve c2pnb368w1, OID: 1.2.840.10045.1.0.19 */ + static final ASN1ObjectIdentifier c2pnb368w1 = cTwoCurve.branch("19"); + /** Two Curve c2tnb431r1, OID: 1.2.840.10045.1.0.20 */ + static final ASN1ObjectIdentifier c2tnb431r1 = cTwoCurve.branch("20"); + + /** + * Prime Curves + *
+ * OID: 1.2.840.10045.1.1 + */ + static final ASN1ObjectIdentifier primeCurve = ellipticCurve.branch("1"); + + /** Prime Curve prime192v1, OID: 1.2.840.10045.1.1.1 */ + static final ASN1ObjectIdentifier prime192v1 = primeCurve.branch("1"); + /** Prime Curve prime192v2, OID: 1.2.840.10045.1.1.2 */ + static final ASN1ObjectIdentifier prime192v2 = primeCurve.branch("2"); + /** Prime Curve prime192v3, OID: 1.2.840.10045.1.1.3 */ + static final ASN1ObjectIdentifier prime192v3 = primeCurve.branch("3"); + /** Prime Curve prime239v1, OID: 1.2.840.10045.1.1.4 */ + static final ASN1ObjectIdentifier prime239v1 = primeCurve.branch("4"); + /** Prime Curve prime239v2, OID: 1.2.840.10045.1.1.5 */ + static final ASN1ObjectIdentifier prime239v2 = primeCurve.branch("5"); + /** Prime Curve prime239v3, OID: 1.2.840.10045.1.1.6 */ + static final ASN1ObjectIdentifier prime239v3 = primeCurve.branch("6"); + /** Prime Curve prime256v1, OID: 1.2.840.10045.1.1.7 */ + static final ASN1ObjectIdentifier prime256v1 = primeCurve.branch("7"); + + /** + * DSA + *
+ * dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2) + * us(840) ansi-x957(10040) number-type(4) 1 } + *+ * Base OID: 1.2.840.10040.4.1 + */ + static final ASN1ObjectIdentifier id_dsa = new ASN1ObjectIdentifier("1.2.840.10040.4.1"); + + /** + *+ * id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } + *+ * OID: 1.2.840.10040.4.3 + */ + static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3"); + + /** + * X9.63 - Signature Specification + *+ * Base OID: 1.3.133.16.840.63.0 + */ + static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0"); + /** OID: 1.3.133.16.840.63.0.2 */ + static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme = x9_63_scheme.branch("2"); + /** OID: 1.3.133.16.840.63.0.3 */ + static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3"); + /** OID: 1.3.133.16.840.63.0.16 */ + static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme = x9_63_scheme.branch("16"); + + /** + * X9.42 + */ + + static final ASN1ObjectIdentifier ansi_X9_42 = new ASN1ObjectIdentifier("1.2.840.10046"); + + /** + * Diffie-Hellman + *
+ * dhpublicnumber OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 + * } + *+ * OID: 1.2.840.10046.2.1 + */ + static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1"); + + /** X9.42 schemas base OID: 1.2.840.10046.3 */ + static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3"); + /** X9.42 dhStatic OID: 1.2.840.10046.3.1 */ + static final ASN1ObjectIdentifier dhStatic = x9_42_schemes.branch("1"); + /** X9.42 dhEphem OID: 1.2.840.10046.3.2 */ + static final ASN1ObjectIdentifier dhEphem = x9_42_schemes.branch("2"); + /** X9.42 dhOneFlow OID: 1.2.840.10046.3.3 */ + static final ASN1ObjectIdentifier dhOneFlow = x9_42_schemes.branch("3"); + /** X9.42 dhHybrid1 OID: 1.2.840.10046.3.4 */ + static final ASN1ObjectIdentifier dhHybrid1 = x9_42_schemes.branch("4"); + /** X9.42 dhHybrid2 OID: 1.2.840.10046.3.5 */ + static final ASN1ObjectIdentifier dhHybrid2 = x9_42_schemes.branch("5"); + /** X9.42 dhHybridOneFlow OID: 1.2.840.10046.3.6 */ + static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6"); + /** X9.42 MQV2 OID: 1.2.840.10046.3.7 */ + static final ASN1ObjectIdentifier mqv2 = x9_42_schemes.branch("7"); + /** X9.42 MQV1 OID: 1.2.840.10046.3.8 */ + static final ASN1ObjectIdentifier mqv1 = x9_42_schemes.branch("8"); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricBlockCipher.java new file mode 100644 index 000000000..a026a69a9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricBlockCipher.java @@ -0,0 +1,45 @@ +package org.spongycastle.crypto; + + +/** + * base interface that a public/private key block cipher needs + * to conform to. + */ +public interface AsymmetricBlockCipher +{ + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + */ + public void init(boolean forEncryption, CipherParameters param); + + /** + * returns the largest size an input block can be. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize(); + + /** + * returns the maximum size of the block produced by this cipher. + * + * @return maximum size of the output block produced by the cipher. + */ + public int getOutputBlockSize(); + + /** + * process the block of len bytes stored in in from offset inOff. + * + * @param in the input data + * @param inOff offset into the in array where the data starts + * @param len the length of the block to be processed. + * @return the resulting byte array of the encryption/decryption process. + * @exception InvalidCipherTextException data decrypts improperly. + * @exception DataLengthException the input data is too large for the cipher. + */ + public byte[] processBlock(byte[] in, int inOff, int len) + throws InvalidCipherTextException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricCipherKeyPair.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricCipherKeyPair.java new file mode 100644 index 000000000..cc8fa1a53 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricCipherKeyPair.java @@ -0,0 +1,61 @@ +package org.spongycastle.crypto; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +/** + * a holding class for public/private parameter pairs. + */ +public class AsymmetricCipherKeyPair +{ + private AsymmetricKeyParameter publicParam; + private AsymmetricKeyParameter privateParam; + + /** + * basic constructor. + * + * @param publicParam a public key parameters object. + * @param privateParam the corresponding private key parameters. + */ + public AsymmetricCipherKeyPair( + AsymmetricKeyParameter publicParam, + AsymmetricKeyParameter privateParam) + { + this.publicParam = publicParam; + this.privateParam = privateParam; + } + + /** + * basic constructor. + * + * @param publicParam a public key parameters object. + * @param privateParam the corresponding private key parameters. + * @deprecated use AsymmetricKeyParameter + */ + public AsymmetricCipherKeyPair( + CipherParameters publicParam, + CipherParameters privateParam) + { + this.publicParam = (AsymmetricKeyParameter)publicParam; + this.privateParam = (AsymmetricKeyParameter)privateParam; + } + + /** + * return the public key parameters. + * + * @return the public key parameters. + */ + public AsymmetricKeyParameter getPublic() + { + return publicParam; + } + + /** + * return the private key parameters. + * + * @return the private key parameters. + */ + public AsymmetricKeyParameter getPrivate() + { + return privateParam; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricCipherKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricCipherKeyPairGenerator.java new file mode 100644 index 000000000..7dad90fd7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/AsymmetricCipherKeyPairGenerator.java @@ -0,0 +1,22 @@ +package org.spongycastle.crypto; + +/** + * interface that a public/private key pair generator should conform to. + */ +public interface AsymmetricCipherKeyPairGenerator +{ + /** + * intialise the key pair generator. + * + * @param param the parameters the key pair is to be initialised with. + */ + public void init(KeyGenerationParameters param); + + /** + * return an AsymmetricCipherKeyPair containing the generated keys. + * + * @return an AsymmetricCipherKeyPair containing the generated keys. + */ + public AsymmetricCipherKeyPair generateKeyPair(); +} + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BasicAgreement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BasicAgreement.java new file mode 100644 index 000000000..e6e003c63 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BasicAgreement.java @@ -0,0 +1,26 @@ +package org.spongycastle.crypto; + +import java.math.BigInteger; + +/** + * The basic interface that basic Diffie-Hellman implementations + * conforms to. + */ +public interface BasicAgreement +{ + /** + * initialise the agreement engine. + */ + void init(CipherParameters param); + + /** + * return the field size for the agreement algorithm in bytes. + */ + int getFieldSize(); + + /** + * given a public key from a given party calculate the next + * message in the agreement sequence. + */ + BigInteger calculateAgreement(CipherParameters pubKey); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BlockCipher.java new file mode 100644 index 000000000..a7d0573a9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BlockCipher.java @@ -0,0 +1,56 @@ +package org.spongycastle.crypto; + + +/** + * Block cipher engines are expected to conform to this interface. + */ +public interface BlockCipher +{ + /** + * Initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getAlgorithmName(); + + /** + * Return the block size for this cipher (in bytes). + * + * @return the block size for this cipher in bytes. + */ + public int getBlockSize(); + + /** + * 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. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException; + + /** + * Reset the cipher. After resetting the cipher is in the same state + * as it was after the last init (if there was one). + */ + public void reset(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BufferedAsymmetricBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BufferedAsymmetricBlockCipher.java new file mode 100644 index 000000000..9cefae149 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BufferedAsymmetricBlockCipher.java @@ -0,0 +1,171 @@ +package org.spongycastle.crypto; + +/** + * a buffer wrapper for an asymmetric block cipher, allowing input + * to be accumulated in a piecemeal fashion until final processing. + */ +public class BufferedAsymmetricBlockCipher +{ + protected byte[] buf; + protected int bufOff; + + private final AsymmetricBlockCipher cipher; + + /** + * base constructor. + * + * @param cipher the cipher this buffering object wraps. + */ + public BufferedAsymmetricBlockCipher( + AsymmetricBlockCipher cipher) + { + this.cipher = cipher; + } + + /** + * return the underlying cipher for the buffer. + * + * @return the underlying cipher for the buffer. + */ + public AsymmetricBlockCipher getUnderlyingCipher() + { + return cipher; + } + + /** + * return the amount of data sitting in the buffer. + * + * @return the amount of data sitting in the buffer. + */ + public int getBufferPosition() + { + return bufOff; + } + + /** + * initialise the buffer and the underlying cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + reset(); + + cipher.init(forEncryption, params); + + // + // we allow for an extra byte where people are using their own padding + // mechanisms on a raw cipher. + // + buf = new byte[cipher.getInputBlockSize() + (forEncryption ? 1 : 0)]; + bufOff = 0; + } + + /** + * returns the largest size an input block can be. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + return cipher.getInputBlockSize(); + } + + /** + * returns the maximum size of the block produced by this cipher. + * + * @return maximum size of the output block produced by the cipher. + */ + public int getOutputBlockSize() + { + return cipher.getOutputBlockSize(); + } + + /** + * add another byte for processing. + * + * @param in the input byte. + */ + public void processByte( + byte in) + { + if (bufOff >= buf.length) + { + throw new DataLengthException("attempt to process message too long for cipher"); + } + + buf[bufOff++] = in; + } + + /** + * add len bytes to the buffer for processing. + * + * @param in the input data + * @param inOff offset into the in array where the data starts + * @param len the length of the block to be processed. + */ + public void processBytes( + byte[] in, + int inOff, + int len) + { + if (len == 0) + { + return; + } + + if (len < 0) + { + throw new IllegalArgumentException("Can't have a negative input length!"); + } + + if (bufOff + len > buf.length) + { + throw new DataLengthException("attempt to process message too long for cipher"); + } + + System.arraycopy(in, inOff, buf, bufOff, len); + bufOff += len; + } + + /** + * process the contents of the buffer using the underlying + * cipher. + * + * @return the result of the encryption/decryption process on the + * buffer. + * @exception InvalidCipherTextException if we are given a garbage block. + */ + public byte[] doFinal() + throws InvalidCipherTextException + { + byte[] out = cipher.processBlock(buf, 0, bufOff); + + reset(); + + return out; + } + + /** + * Reset the buffer and the underlying cipher. + */ + public void reset() + { + /* + * clean the buffer. + */ + if (buf != null) + { + for (int i = 0; i < buf.length; i++) + { + buf[i] = 0; + } + } + + bufOff = 0; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BufferedBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BufferedBlockCipher.java new file mode 100644 index 000000000..941943322 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/BufferedBlockCipher.java @@ -0,0 +1,313 @@ +package org.spongycastle.crypto; + + +/** + * A wrapper class that allows block ciphers to be used to process data in + * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the + * buffer is full and more data is being added, or on a doFinal. + *+ * Note: in the case where the underlying cipher is either a CFB cipher or an + * OFB one the last block may not be a multiple of the block size. + */ +public class BufferedBlockCipher +{ + protected byte[] buf; + protected int bufOff; + + protected boolean forEncryption; + protected BlockCipher cipher; + + protected boolean partialBlockOkay; + protected boolean pgpCFB; + + /** + * constructor for subclasses + */ + protected BufferedBlockCipher() + { + } + + /** + * Create a buffered block cipher without padding. + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public BufferedBlockCipher( + BlockCipher cipher) + { + this.cipher = cipher; + + buf = new byte[cipher.getBlockSize()]; + bufOff = 0; + + // + // check if we can handle partial blocks on doFinal. + // + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; + + pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); + + if (pgpCFB) + { + partialBlockOkay = true; + } + else + { + partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("GCFB", idx) ||name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx))); + } + } + + /** + * return the cipher this object wraps. + * + * @return the cipher this object wraps. + */ + public BlockCipher getUnderlyingCipher() + { + return cipher; + } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + throws IllegalArgumentException + { + this.forEncryption = forEncryption; + + reset(); + + cipher.init(forEncryption, params); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public int getBlockSize() + { + return cipher.getBlockSize(); + } + + /** + * return the size of the output buffer required for an update + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update + * with len bytes of input. + */ + public int getUpdateOutputSize( + int len) + { + int total = len + bufOff; + int leftOver; + + if (pgpCFB) + { + leftOver = total % buf.length - (cipher.getBlockSize() + 2); + } + else + { + leftOver = total % buf.length; + } + + return total - leftOver; + } + + /** + * return the size of the output buffer required for an update plus a + * doFinal with an input of 'length' bytes. + * + * @param length the length of the input. + * @return the space required to accommodate a call to update and doFinal + * with 'length' bytes of input. + */ + public int getOutputSize( + int length) + { + // Note: Can assume partialBlockOkay is true for purposes of this calculation + return length + bufOff; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @param in the input byte. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processByte( + byte in, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + int resultLen = 0; + + buf[bufOff++] = in; + + if (bufOff == buf.length) + { + resultLen = cipher.processBlock(buf, 0, out, outOff); + bufOff = 0; + } + + return resultLen; + } + + /** + * process an array of bytes, producing output if necessary. + * + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if (len < 0) + { + throw new IllegalArgumentException("Can't have a negative input length!"); + } + + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + + if (length > 0) + { + if ((outOff + length) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.length - bufOff; + + if (len > gapLen) + { + System.arraycopy(in, inOff, buf, bufOff, gapLen); + + resultLen += cipher.processBlock(buf, 0, out, outOff); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > buf.length) + { + resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen); + + len -= blockSize; + inOff += blockSize; + } + } + + System.arraycopy(in, inOff, buf, bufOff, len); + + bufOff += len; + + if (bufOff == buf.length) + { + resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen); + bufOff = 0; + } + + return resultLen; + } + + /** + * Process the last block in the buffer. + * + * @param out the array the block currently being held is copied into. + * @param outOff the offset at which the copying starts. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @exception IllegalStateException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + * @exception DataLengthException if the input is not block size + * aligned. + */ + public int doFinal( + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException, InvalidCipherTextException + { + try + { + int resultLen = 0; + + if (outOff + bufOff > out.length) + { + throw new OutputLengthException("output buffer too short for doFinal()"); + } + + if (bufOff != 0) + { + if (!partialBlockOkay) + { + throw new DataLengthException("data not block size aligned"); + } + + cipher.processBlock(buf, 0, buf, 0); + resultLen = bufOff; + bufOff = 0; + System.arraycopy(buf, 0, out, outOff, resultLen); + } + + return resultLen; + } + finally + { + reset(); + } + } + + /** + * Reset the buffer and cipher. After resetting the object is in the same + * state as it was after the last init (if there was one). + */ + public void reset() + { + // + // clean the buffer. + // + for (int i = 0; i < buf.length; i++) + { + buf[i] = 0; + } + + bufOff = 0; + + // + // reset the underlying cipher. + // + cipher.reset(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CipherKeyGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CipherKeyGenerator.java new file mode 100644 index 000000000..5377e739c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CipherKeyGenerator.java @@ -0,0 +1,38 @@ +package org.spongycastle.crypto; + +import java.security.SecureRandom; + +/** + * The base class for symmetric, or secret, cipher key generators. + */ +public class CipherKeyGenerator +{ + protected SecureRandom random; + protected int strength; + + /** + * initialise the key generator. + * + * @param param the parameters to be used for key generation + */ + public void init( + KeyGenerationParameters param) + { + this.random = param.getRandom(); + this.strength = (param.getStrength() + 7) / 8; + } + + /** + * generate a secret key. + * + * @return a byte array containing the key value. + */ + public byte[] generateKey() + { + byte[] key = new byte[strength]; + + random.nextBytes(key); + + return key; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CipherParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CipherParameters.java new file mode 100644 index 000000000..5a9576bf1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CipherParameters.java @@ -0,0 +1,8 @@ +package org.spongycastle.crypto; + +/** + * all parameter classes implement this. + */ +public interface CipherParameters +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Commitment.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Commitment.java new file mode 100644 index 000000000..9f5bcdf92 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Commitment.java @@ -0,0 +1,42 @@ +package org.spongycastle.crypto; + +/** + * General holding class for a commitment. + */ +public class Commitment +{ + private final byte[] secret; + private final byte[] commitment; + + /** + * Base constructor. + * + * @param secret an encoding of the secret required to reveal the commitment. + * @param commitment an encoding of the sealed commitment. + */ + public Commitment(byte[] secret, byte[] commitment) + { + this.secret = secret; + this.commitment = commitment; + } + + /** + * The secret required to reveal the commitment. + * + * @return an encoding of the secret associated with the commitment. + */ + public byte[] getSecret() + { + return secret; + } + + /** + * The sealed commitment. + * + * @return an encoding of the sealed commitment. + */ + public byte[] getCommitment() + { + return commitment; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Committer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Committer.java new file mode 100644 index 000000000..777486645 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Committer.java @@ -0,0 +1,24 @@ +package org.spongycastle.crypto; + +/** + * General interface fdr classes that produce and validate commitments. + */ +public interface Committer +{ + /** + * Generate a commitment for the passed in message. + * + * @param message the message to be committed to, + * @return a Commitment + */ + Commitment commit(byte[] message); + + /** + * Return true if the passed in commitment represents a commitment to the passed in maessage. + * + * @param commitment a commitment previously generated. + * @param message the message that was expected to have been committed to. + * @return true if commitment matches message, false otherwise. + */ + boolean isRevealed(Commitment commitment, byte[] message); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CryptoException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CryptoException.java new file mode 100644 index 000000000..a33adf95b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/CryptoException.java @@ -0,0 +1,48 @@ +package org.spongycastle.crypto; + +/** + * the foundation class for the hard exceptions thrown by the crypto packages. + */ +public class CryptoException + extends Exception +{ + private Throwable cause; + + /** + * base constructor. + */ + public CryptoException() + { + } + + /** + * create a CryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public CryptoException( + String message) + { + super(message); + } + + /** + * Create a CryptoException with the given message and underlying cause. + * + * @param message message describing exception. + * @param cause the throwable that was the underlying cause. + */ + public CryptoException( + String message, + Throwable cause) + { + super(message); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DSA.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DSA.java new file mode 100644 index 000000000..7243ddd94 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DSA.java @@ -0,0 +1,36 @@ +package org.spongycastle.crypto; + +import java.math.BigInteger; + +/** + * interface for classes implementing algorithms modeled similar to the Digital Signature Alorithm. + */ +public interface DSA +{ + /** + * initialise the signer for signature generation or signature + * verification. + * + * @param forSigning true if we are generating a signature, false + * otherwise. + * @param param key parameters for signature generation. + */ + public void init(boolean forSigning, CipherParameters param); + + /** + * sign the passed in message (usually the output of a hash function). + * + * @param message the message to be signed. + * @return two big integers representing the r and s values respectively. + */ + public BigInteger[] generateSignature(byte[] message); + + /** + * verify the message message against the signature values r and s. + * + * @param message the message that was supposed to have been signed. + * @param r the r signature value. + * @param s the s signature value. + */ + public boolean verifySignature(byte[] message, BigInteger r, BigInteger s); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DataLengthException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DataLengthException.java new file mode 100644 index 000000000..c89e8bf1b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DataLengthException.java @@ -0,0 +1,29 @@ +package org.spongycastle.crypto; + +/** + * this exception is thrown if a buffer that is meant to have output + * copied into it turns out to be too short, or if we've been given + * insufficient input. In general this exception will get thrown rather + * than an ArrayOutOfBounds exception. + */ +public class DataLengthException + extends RuntimeCryptoException +{ + /** + * base constructor. + */ + public DataLengthException() + { + } + + /** + * create a DataLengthException with the given message. + * + * @param message the message to be carried with the exception. + */ + public DataLengthException( + String message) + { + super(message); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DerivationFunction.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DerivationFunction.java new file mode 100644 index 000000000..b8e913fd2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DerivationFunction.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto; + +/** + * base interface for general purpose byte derivation functions. + */ +public interface DerivationFunction +{ + public void init(DerivationParameters param); + + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DerivationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DerivationParameters.java new file mode 100644 index 000000000..19b23ec66 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DerivationParameters.java @@ -0,0 +1,8 @@ +package org.spongycastle.crypto; + +/** + * Parameters for key/byte stream derivation classes + */ +public interface DerivationParameters +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Digest.java new file mode 100644 index 000000000..33103b26a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Digest.java @@ -0,0 +1,51 @@ +package org.spongycastle.crypto; + +/** + * interface that a message digest conforms to. + */ +public interface Digest +{ + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName(); + + /** + * 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(); + + /** + * update the message digest with a single byte. + * + * @param in the input byte to be entered. + */ + public void update(byte in); + + /** + * update the message digest with a block of bytes. + * + * @param in the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public void update(byte[] in, int inOff, int len); + + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOff); + + /** + * reset the digest back to it's initial state. + */ + public void reset(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DigestDerivationFunction.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DigestDerivationFunction.java new file mode 100644 index 000000000..7ed0fe15d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/DigestDerivationFunction.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto; + +/** + * base interface for general purpose Digest based byte derivation functions. + */ +public interface DigestDerivationFunction + extends DerivationFunction +{ + /** + * return the message digest used as the basis for the function + */ + public Digest getDigest(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/EphemeralKeyPair.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/EphemeralKeyPair.java new file mode 100644 index 000000000..042850c34 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/EphemeralKeyPair.java @@ -0,0 +1,23 @@ +package org.spongycastle.crypto; + +public class EphemeralKeyPair +{ + private AsymmetricCipherKeyPair keyPair; + private KeyEncoder publicKeyEncoder; + + public EphemeralKeyPair(AsymmetricCipherKeyPair keyPair, KeyEncoder publicKeyEncoder) + { + this.keyPair = keyPair; + this.publicKeyEncoder = publicKeyEncoder; + } + + public AsymmetricCipherKeyPair getKeyPair() + { + return keyPair; + } + + public byte[] getEncodedPublicKey() + { + return publicKeyEncoder.getEncoded(keyPair.getPublic()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ExtendedDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ExtendedDigest.java new file mode 100644 index 000000000..7cc339f27 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ExtendedDigest.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto; + +public interface ExtendedDigest + extends Digest +{ + /** + * 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(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/InvalidCipherTextException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/InvalidCipherTextException.java new file mode 100644 index 000000000..802683fc3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/InvalidCipherTextException.java @@ -0,0 +1,40 @@ +package org.spongycastle.crypto; + +/** + * this exception is thrown whenever we find something we don't expect in a + * message. + */ +public class InvalidCipherTextException + extends CryptoException +{ + /** + * base constructor. + */ + public InvalidCipherTextException() + { + } + + /** + * create a InvalidCipherTextException with the given message. + * + * @param message the message to be carried with the exception. + */ + public InvalidCipherTextException( + String message) + { + super(message); + } + + /** + * create a InvalidCipherTextException with the given message. + * + * @param message the message to be carried with the exception. + * @param cause the root cause of the exception. + */ + public InvalidCipherTextException( + String message, + Throwable cause) + { + super(message, cause); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyEncapsulation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyEncapsulation.java new file mode 100755 index 000000000..1a53e7908 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyEncapsulation.java @@ -0,0 +1,22 @@ +package org.spongycastle.crypto; + +/** + * The basic interface for key encapsulation mechanisms. + */ +public interface KeyEncapsulation +{ + /** + * Initialise the key encapsulation mechanism. + */ + public void init(CipherParameters param); + + /** + * Encapsulate a randomly generated session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen); + + /** + * Decapsulate an encapsulated session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyEncoder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyEncoder.java new file mode 100644 index 000000000..b3cc17985 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyEncoder.java @@ -0,0 +1,8 @@ +package org.spongycastle.crypto; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +public interface KeyEncoder +{ + byte[] getEncoded(AsymmetricKeyParameter keyParameter); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyGenerationParameters.java new file mode 100644 index 000000000..853f9c139 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyGenerationParameters.java @@ -0,0 +1,48 @@ +package org.spongycastle.crypto; + +import java.security.SecureRandom; + +/** + * The base class for parameters to key generators. + */ +public class KeyGenerationParameters +{ + private SecureRandom random; + private int strength; + + /** + * initialise the generator with a source of randomness + * and a strength (in bits). + * + * @param random the random byte source. + * @param strength the size, in bits, of the keys we want to produce. + */ + public KeyGenerationParameters( + SecureRandom random, + int strength) + { + this.random = random; + this.strength = strength; + } + + /** + * return the random source associated with this + * generator. + * + * @return the generators random source. + */ + public SecureRandom getRandom() + { + return random; + } + + /** + * return the bit strength for keys produced by this generator, + * + * @return the strength of the keys this generator produces (in bits). + */ + public int getStrength() + { + return strength; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyParser.java new file mode 100644 index 000000000..7992ff865 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/KeyParser.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto; + +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +public interface KeyParser +{ + AsymmetricKeyParameter readKey(InputStream stream) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Mac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Mac.java new file mode 100644 index 000000000..8be61143a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Mac.java @@ -0,0 +1,71 @@ +package org.spongycastle.crypto; + + +/** + * The base interface for implementations of message authentication codes (MACs). + */ +public interface Mac +{ + /** + * Initialise the MAC. + * + * @param params the key and other data required by the MAC. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the MAC implements. + * + * @return the name of the algorithm the MAC implements. + */ + public String getAlgorithmName(); + + /** + * Return the block size for this MAC (in bytes). + * + * @return the block size for this MAC in bytes. + */ + public int getMacSize(); + + /** + * add a single byte to the mac for processing. + * + * @param in the byte to be processed. + * @exception IllegalStateException if the MAC is not initialised. + */ + public void update(byte in) + throws IllegalStateException; + + /** + * @param in the array containing the input. + * @param inOff the index in the array the data begins at. + * @param len the length of the input starting at inOff. + * @exception IllegalStateException if the MAC is not initialised. + * @exception DataLengthException if there isn't enough data in in. + */ + public void update(byte[] in, int inOff, int len) + throws DataLengthException, IllegalStateException; + + /** + * Compute the final stage of the MAC writing the output to the out + * parameter. + *
+ * doFinal leaves the MAC in the same state it was after the last init. + * + * @param out the array the MAC is to be output to. + * @param outOff the offset into the out buffer the output is to start at. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the MAC is not initialised. + */ + public int doFinal(byte[] out, int outOff) + throws DataLengthException, IllegalStateException; + + /** + * Reset the MAC. At the end of resetting the MAC should be in the + * in the same state it was after the last init (if there was one). + */ + public void reset(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/MacDerivationFunction.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/MacDerivationFunction.java new file mode 100644 index 000000000..0478e9a62 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/MacDerivationFunction.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto; + +/** + * base interface for general purpose Mac based byte derivation functions. + */ +public interface MacDerivationFunction + extends DerivationFunction +{ + /** + * return the MAC used as the basis for the function + */ + public Mac getMac(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/MaxBytesExceededException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/MaxBytesExceededException.java new file mode 100644 index 000000000..b49f2661f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/MaxBytesExceededException.java @@ -0,0 +1,27 @@ +package org.spongycastle.crypto; + +/** + * this exception is thrown whenever a cipher requires a change of key, iv + * or similar after x amount of bytes enciphered + */ +public class MaxBytesExceededException + extends RuntimeCryptoException +{ + /** + * base constructor. + */ + public MaxBytesExceededException() + { + } + + /** + * create an with the given message. + * + * @param message the message to be carried with the exception. + */ + public MaxBytesExceededException( + String message) + { + super(message); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/OutputLengthException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/OutputLengthException.java new file mode 100644 index 000000000..e1f23f316 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/OutputLengthException.java @@ -0,0 +1,10 @@ +package org.spongycastle.crypto; + +public class OutputLengthException + extends DataLengthException +{ + public OutputLengthException(String msg) + { + super(msg); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/PBEParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/PBEParametersGenerator.java new file mode 100644 index 000000000..6788d14eb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/PBEParametersGenerator.java @@ -0,0 +1,171 @@ +package org.spongycastle.crypto; + +import org.spongycastle.util.Strings; + +/** + * super class for all Password Based Encryption (PBE) parameter generator classes. + */ +public abstract class PBEParametersGenerator +{ + protected byte[] password; + protected byte[] salt; + protected int iterationCount; + + /** + * base constructor. + */ + protected PBEParametersGenerator() + { + } + + /** + * initialise the PBE generator. + * + * @param password the password converted into bytes (see below). + * @param salt the salt to be mixed with the password. + * @param iterationCount the number of iterations the "mixing" function + * is to be applied for. + */ + public void init( + byte[] password, + byte[] salt, + int iterationCount) + { + this.password = password; + this.salt = salt; + this.iterationCount = iterationCount; + } + + /** + * return the password byte array. + * + * @return the password byte array. + */ + public byte[] getPassword() + { + return password; + } + + /** + * return the salt byte array. + * + * @return the salt byte array. + */ + public byte[] getSalt() + { + return salt; + } + + /** + * return the iteration count. + * + * @return the iteration count. + */ + public int getIterationCount() + { + return iterationCount; + } + + /** + * generate derived parameters for a key of length keySize. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract CipherParameters generateDerivedParameters(int keySize); + + /** + * generate derived parameters for a key of length keySize, and + * an initialisation vector (IV) of length ivSize. + * + * @param keySize the length, in bits, of the key required. + * @param ivSize the length, in bits, of the iv required. + * @return a parameters object representing a key and an IV. + */ + public abstract CipherParameters generateDerivedParameters(int keySize, int ivSize); + + /** + * generate derived parameters for a key of length keySize, specifically + * for use with a MAC. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract CipherParameters generateDerivedMacParameters(int keySize); + + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (ascii, no padding) + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS5PasswordToBytes( + char[] password) + { + if (password != null) + { + byte[] bytes = new byte[password.length]; + + for (int i = 0; i != bytes.length; i++) + { + bytes[i] = (byte)password[i]; + } + + return bytes; + } + else + { + return new byte[0]; + } + } + + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (UTF-8, no padding) + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS5PasswordToUTF8Bytes( + char[] password) + { + if (password != null) + { + return Strings.toUTF8ByteArray(password); + } + else + { + return new byte[0]; + } + } + + /** + * converts a password to a byte array according to the scheme in + * PKCS12 (unicode, big endian, 2 zero pad bytes at the end). + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS12PasswordToBytes( + char[] password) + { + if (password != null && password.length > 0) + { + // +1 for extra 2 pad bytes. + byte[] bytes = new byte[(password.length + 1) * 2]; + + for (int i = 0; i != password.length; i ++) + { + bytes[i * 2] = (byte)(password[i] >>> 8); + bytes[i * 2 + 1] = (byte)password[i]; + } + + return bytes; + } + else + { + return new byte[0]; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/RuntimeCryptoException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/RuntimeCryptoException.java new file mode 100644 index 000000000..4eeb18f41 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/RuntimeCryptoException.java @@ -0,0 +1,26 @@ +package org.spongycastle.crypto; + +/** + * the foundation class for the exceptions thrown by the crypto packages. + */ +public class RuntimeCryptoException + extends RuntimeException +{ + /** + * base constructor. + */ + public RuntimeCryptoException() + { + } + + /** + * create a RuntimeCryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public RuntimeCryptoException( + String message) + { + super(message); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Signer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Signer.java new file mode 100644 index 000000000..503e12009 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Signer.java @@ -0,0 +1,43 @@ +package org.spongycastle.crypto; + +/** + * Generic signer interface for hash based and message recovery signers. + */ +public interface Signer +{ + /** + * Initialise the signer for signing or verification. + * + * @param forSigning true if for signing, false otherwise + * @param param necessary parameters. + */ + public void init(boolean forSigning, CipherParameters param); + + /** + * update the internal digest with the byte b + */ + public void update(byte b); + + /** + * update the internal digest with the byte array in + */ + public void update(byte[] in, int off, int len); + + /** + * generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + public byte[] generateSignature() + throws CryptoException, DataLengthException; + + /** + * return true if the internal state represents the signature described + * in the passed in array. + */ + public boolean verifySignature(byte[] signature); + + /** + * reset the internal state + */ + public void reset(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/SignerWithRecovery.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/SignerWithRecovery.java new file mode 100644 index 000000000..e7a39a660 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/SignerWithRecovery.java @@ -0,0 +1,34 @@ +package org.spongycastle.crypto; + +/** + * Signer with message recovery. + */ +public interface SignerWithRecovery + extends Signer +{ + /** + * Returns true if the signer has recovered the full message as + * part of signature verification. + * + * @return true if full message recovered. + */ + public boolean hasFullMessage(); + + /** + * Returns a reference to what message was recovered (if any). + * + * @return full/partial message, null if nothing. + */ + public byte[] getRecoveredMessage(); + + /** + * Perform an update with the recovered message before adding any other data. This must + * be the first update method called, and calling it will result in the signer assuming + * that further calls to update will include message content past what is recoverable. + * + * @param signature the signature that we are in the process of verifying. + * @throws IllegalStateException + */ + public void updateWithRecoveredMessage(byte[] signature) + throws InvalidCipherTextException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/StreamBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/StreamBlockCipher.java new file mode 100644 index 000000000..d1fa86e0a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/StreamBlockCipher.java @@ -0,0 +1,108 @@ +package org.spongycastle.crypto; + +/** + * a wrapper for block ciphers with a single byte block size, so that they + * can be treated like stream ciphers. + */ +public class StreamBlockCipher + implements StreamCipher +{ + private BlockCipher cipher; + + private byte[] oneByte = new byte[1]; + + /** + * basic constructor. + * + * @param cipher the block cipher to be wrapped. + * @exception IllegalArgumentException if the cipher has a block size other than + * one. + */ + public StreamBlockCipher( + BlockCipher cipher) + { + if (cipher.getBlockSize() != 1) + { + throw new IllegalArgumentException("block cipher block size != 1."); + } + + this.cipher = cipher; + } + + /** + * initialise the underlying cipher. + * + * @param forEncryption true if we are setting up for encryption, false otherwise. + * @param params the necessary parameters for the underlying cipher to be initialised. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + cipher.init(forEncryption, params); + } + + /** + * return the name of the algorithm we are wrapping. + * + * @return the name of the algorithm we are wrapping. + */ + public String getAlgorithmName() + { + return cipher.getAlgorithmName(); + } + + /** + * encrypt/decrypt a single byte returning the result. + * + * @param in the byte to be processed. + * @return the result of processing the input byte. + */ + public byte returnByte( + byte in) + { + oneByte[0] = in; + + cipher.processBlock(oneByte, 0, oneByte, 0); + + return oneByte[0]; + } + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data stars at. + * @exception DataLengthException if the output buffer is too small. + */ + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException + { + if (outOff + len > out.length) + { + throw new DataLengthException("output buffer too small in processBytes()"); + } + + for (int i = 0; i != len; i++) + { + cipher.processBlock(in, inOff + i, out, outOff + i); + } + } + + /** + * reset the underlying cipher. This leaves it in the same state + * it was at after the last init (if there was one). + */ + public void reset() + { + cipher.reset(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/StreamCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/StreamCipher.java new file mode 100644 index 000000000..1ca9a8799 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/StreamCipher.java @@ -0,0 +1,53 @@ +package org.spongycastle.crypto; + +/** + * the interface stream ciphers conform to. + */ +public interface StreamCipher +{ + /** + * Initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getAlgorithmName(); + + /** + * encrypt/decrypt a single byte returning the result. + * + * @param in the byte to be processed. + * @return the result of processing the input byte. + */ + public byte returnByte(byte in); + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data starts at. + * @exception DataLengthException if the output buffer is too small. + */ + public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + /** + * reset the cipher. This leaves it in the same state + * it was at after the last init (if there was one). + */ + public void reset(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Wrapper.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Wrapper.java new file mode 100644 index 000000000..f517c1271 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/Wrapper.java @@ -0,0 +1,18 @@ +package org.spongycastle.crypto; + +public interface Wrapper +{ + public void init(boolean forWrapping, CipherParameters param); + + /** + * Return the name of the algorithm the wrapper implements. + * + * @return the name of the algorithm the wrapper implements. + */ + public String getAlgorithmName(); + + public byte[] wrap(byte[] in, int inOff, int inLen); + + public byte[] unwrap(byte[] in, int inOff, int inLen) + throws InvalidCipherTextException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHAgreement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHAgreement.java new file mode 100644 index 000000000..9dd5d0400 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHAgreement.java @@ -0,0 +1,94 @@ +package org.spongycastle.crypto.agreement; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.generators.DHKeyPairGenerator; +import org.spongycastle.crypto.params.DHKeyGenerationParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; + +/** + * a Diffie-Hellman key exchange engine. + *
+ * note: This uses MTI/A0 key agreement in order to make the key agreement + * secure against passive attacks. If you're doing Diffie-Hellman and both + * parties have long term public keys you should look at using this. For + * further information have a look at RFC 2631. + *
+ * It's possible to extend this to more than two parties as well, for the moment + * that is left as an exercise for the reader. + */ +public class DHAgreement +{ + private DHPrivateKeyParameters key; + private DHParameters dhParams; + private BigInteger privateValue; + private SecureRandom random; + + public void init( + CipherParameters param) + { + AsymmetricKeyParameter kParam; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + this.random = rParam.getRandom(); + kParam = (AsymmetricKeyParameter)rParam.getParameters(); + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)param; + } + + + if (!(kParam instanceof DHPrivateKeyParameters)) + { + throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters"); + } + + this.key = (DHPrivateKeyParameters)kParam; + this.dhParams = key.getParameters(); + } + + /** + * calculate our initial message. + */ + public BigInteger calculateMessage() + { + DHKeyPairGenerator dhGen = new DHKeyPairGenerator(); + dhGen.init(new DHKeyGenerationParameters(random, dhParams)); + AsymmetricCipherKeyPair dhPair = dhGen.generateKeyPair(); + + this.privateValue = ((DHPrivateKeyParameters)dhPair.getPrivate()).getX(); + + return ((DHPublicKeyParameters)dhPair.getPublic()).getY(); + } + + /** + * given a message from a given party and the corresponding public key, + * calculate the next message in the agreement sequence. In this case + * this will represent the shared secret. + */ + public BigInteger calculateAgreement( + DHPublicKeyParameters pub, + BigInteger message) + { + if (!pub.getParameters().equals(dhParams)) + { + throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters."); + } + + BigInteger p = dhParams.getP(); + + return message.modPow(key.getX(), p).multiply(pub.getY().modPow(privateValue, p)).mod(p); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHBasicAgreement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHBasicAgreement.java new file mode 100644 index 000000000..6359ecdbd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHBasicAgreement.java @@ -0,0 +1,71 @@ +package org.spongycastle.crypto.agreement; + +import java.math.BigInteger; + +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.crypto.params.ParametersWithRandom; + +/** + * a Diffie-Hellman key agreement class. + *
+ * note: This is only the basic algorithm, it doesn't take advantage of + * long term public keys if they are available. See the DHAgreement class + * for a "better" implementation. + */ +public class DHBasicAgreement + implements BasicAgreement +{ + private DHPrivateKeyParameters key; + private DHParameters dhParams; + + public void init( + CipherParameters param) + { + AsymmetricKeyParameter kParam; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + kParam = (AsymmetricKeyParameter)rParam.getParameters(); + } + else + { + kParam = (AsymmetricKeyParameter)param; + } + + if (!(kParam instanceof DHPrivateKeyParameters)) + { + throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters"); + } + + this.key = (DHPrivateKeyParameters)kParam; + this.dhParams = key.getParameters(); + } + + public int getFieldSize() + { + return (key.getParameters().getP().bitLength() + 7) / 8; + } + + /** + * given a short term public key from a given party calculate the next + * message in the agreement sequence. + */ + public BigInteger calculateAgreement( + CipherParameters pubKey) + { + DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey; + + if (!pub.getParameters().equals(dhParams)) + { + throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters."); + } + + return pub.getY().modPow(key.getX(), dhParams.getP()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHStandardGroups.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHStandardGroups.java new file mode 100644 index 000000000..e506d509d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/DHStandardGroups.java @@ -0,0 +1,206 @@ +package org.spongycastle.crypto.agreement; + +import java.math.BigInteger; + +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.util.encoders.Hex; + +/** + * Standard Diffie-Hellman groups from various IETF specifications. + */ +public class DHStandardGroups +{ + + private static DHParameters fromPG(String hexP, String hexG) + { + BigInteger p = new BigInteger(1, Hex.decode(hexP)); + BigInteger g = new BigInteger(1, Hex.decode(hexG)); + return new DHParameters(p, g); + } + + private static DHParameters fromPGQ(String hexP, String hexG, String hexQ) + { + BigInteger p = new BigInteger(1, Hex.decode(hexP)); + BigInteger g = new BigInteger(1, Hex.decode(hexG)); + BigInteger q = new BigInteger(1, Hex.decode(hexQ)); + return new DHParameters(p, g, q); + } + + /* + * RFC 2409 + */ + private static final String rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"; + private static final String rfc2409_768_g = "02"; + public static final DHParameters rfc2409_768 = fromPG(rfc2409_768_p, rfc2409_768_g); + + private static final String rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" + + "FFFFFFFFFFFFFFFF"; + private static final String rfc2409_1024_g = "02"; + public static final DHParameters rfc2409_1024 = fromPG(rfc2409_1024_p, rfc2409_1024_g); + + /* + * RFC 3526 + */ + private static final String rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"; + private static final String rfc3526_1536_g = "02"; + public static final DHParameters rfc3526_1536 = fromPG(rfc3526_1536_p, rfc3526_1536_g); + + private static final String rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; + private static final String rfc3526_2048_g = "02"; + public static final DHParameters rfc3526_2048 = fromPG(rfc3526_2048_p, rfc3526_2048_g); + + private static final String rfc3526_3072_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"; + private static final String rfc3526_3072_g = "02"; + public static final DHParameters rfc3526_3072 = fromPG(rfc3526_3072_p, rfc3526_3072_g); + + private static final String rfc3526_4096_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF"; + private static final String rfc3526_4096_g = "02"; + public static final DHParameters rfc3526_4096 = fromPG(rfc3526_4096_p, rfc3526_4096_g); + + private static final String rfc3526_6144_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF"; + private static final String rfc3526_6144_g = "02"; + public static final DHParameters rfc3526_6144 = fromPG(rfc3526_6144_p, rfc3526_6144_g); + + private static final String rfc3526_8192_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" + + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" + + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" + + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" + + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" + + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" + + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" + + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" + + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" + + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"; + private static final String rfc3526_8192_g = "02"; + public static final DHParameters rfc3526_8192 = fromPG(rfc3526_8192_p, rfc3526_8192_g); + + /* + * RFC 4306 + */ + public static final DHParameters rfc4306_768 = rfc2409_768; + public static final DHParameters rfc4306_1024 = rfc2409_1024; + + /* + * RFC 5114 + */ + private static final String rfc5114_1024_160_p = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" + + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" + + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" + + "DF1FB2BC2E4A4371"; + private static final String rfc5114_1024_160_g = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" + + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" + + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" + + "855E6EEB22B3B2E5"; + private static final String rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353"; + public static final DHParameters rfc5114_1024_160 = fromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g, + rfc5114_1024_160_q); + + private static final String rfc5114_2048_224_p = "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" + + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212" + + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708" + + "B3BF8A317091883681286130BC8985DB1602E714415D9330" + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D" + + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763" + + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" + "CF9DE5384E71B81C0AC4DFFE0C10E64F"; + private static final String rfc5114_2048_224_g = "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF" + + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA" + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7" + + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A" + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE" + + "F180EB34118E98D119529A45D6F834566E3025E316A330EF" + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB" + + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269" + + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA"; + private static final String rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB"; + public static final DHParameters rfc5114_2048_224 = fromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g, + rfc5114_2048_224_q); + + private static final String rfc5114_2048_256_p = "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2" + + "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30" + "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD" + + "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B" + "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C" + + "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E" + "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9" + + "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026" + "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3" + + "75F26375D7014103A4B54330C198AF126116D2276E11715F" + "693877FAD7EF09CADB094AE91E1A1597"; + private static final String rfc5114_2048_256_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054" + + "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555" + "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18" + + "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83" + + "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55" + "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14" + + "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6" + + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659"; + private static final String rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B" + + "A308B0FE64F5FBD3"; + public static final DHParameters rfc5114_2048_256 = fromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g, + rfc5114_2048_256_q); + + /* + * RFC 5996 + */ + public static final DHParameters rfc5996_768 = rfc4306_768; + public static final DHParameters rfc5996_1024 = rfc4306_1024; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECDHBasicAgreement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECDHBasicAgreement.java new file mode 100644 index 000000000..535597603 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECDHBasicAgreement.java @@ -0,0 +1,54 @@ +package org.spongycastle.crypto.agreement; + +import java.math.BigInteger; + +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.math.ec.ECPoint; + +/** + * P1363 7.2.1 ECSVDP-DH + * + * ECSVDP-DH is Elliptic Curve Secret Value Derivation Primitive, + * Diffie-Hellman version. It is based on the work of [DH76], [Mil86], + * and [Kob87]. This primitive derives a shared secret value from one + * party's private key and another party's public key, where both have + * the same set of EC domain parameters. If two parties correctly + * execute this primitive, they will produce the same output. This + * primitive can be invoked by a scheme to derive a shared secret key; + * specifically, it may be used with the schemes ECKAS-DH1 and + * DL/ECKAS-DH2. It assumes that the input keys are valid (see also + * Section 7.2.2). + */ +public class ECDHBasicAgreement + implements BasicAgreement +{ + private ECPrivateKeyParameters key; + + public void init( + CipherParameters key) + { + this.key = (ECPrivateKeyParameters)key; + } + + public int getFieldSize() + { + return (key.getParameters().getCurve().getFieldSize() + 7) / 8; + } + + public BigInteger calculateAgreement( + CipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey; + ECPoint P = pub.getQ().multiply(key.getD()).normalize(); + + if (P.isInfinity()) + { + throw new IllegalStateException("Infinity is not a valid agreement value for ECDH"); + } + + return P.getAffineXCoord().toBigInteger(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECDHCBasicAgreement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECDHCBasicAgreement.java new file mode 100644 index 000000000..49677805d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECDHCBasicAgreement.java @@ -0,0 +1,64 @@ +package org.spongycastle.crypto.agreement; + +import java.math.BigInteger; + +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.math.ec.ECPoint; + +/** + * P1363 7.2.2 ECSVDP-DHC + * + * ECSVDP-DHC is Elliptic Curve Secret Value Derivation Primitive, + * Diffie-Hellman version with cofactor multiplication. It is based on + * the work of [DH76], [Mil86], [Kob87], [LMQ98] and [Kal98a]. This + * primitive derives a shared secret value from one party's private key + * and another party's public key, where both have the same set of EC + * domain parameters. If two parties correctly execute this primitive, + * they will produce the same output. This primitive can be invoked by a + * scheme to derive a shared secret key; specifically, it may be used + * with the schemes ECKAS-DH1 and DL/ECKAS-DH2. It does not assume the + * validity of the input public key (see also Section 7.2.1). + *
+ * Note: As stated P1363 compatibility mode with ECDH can be preset, and + * in this case the implementation doesn't have a ECDH compatibility mode + * (if you want that just use ECDHBasicAgreement and note they both implement + * BasicAgreement!). + */ +public class ECDHCBasicAgreement + implements BasicAgreement +{ + ECPrivateKeyParameters key; + + public void init( + CipherParameters key) + { + this.key = (ECPrivateKeyParameters)key; + } + + public int getFieldSize() + { + return (key.getParameters().getCurve().getFieldSize() + 7) / 8; + } + + public BigInteger calculateAgreement( + CipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey; + ECDomainParameters params = pub.getParameters(); + + BigInteger hd = params.getH().multiply(key.getD()).mod(params.getN()); + + ECPoint P = pub.getQ().multiply(hd).normalize(); + + if (P.isInfinity()) + { + throw new IllegalStateException("Infinity is not a valid agreement value for ECDHC"); + } + + return P.getAffineXCoord().toBigInteger(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECMQVBasicAgreement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECMQVBasicAgreement.java new file mode 100644 index 000000000..9faa50068 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/ECMQVBasicAgreement.java @@ -0,0 +1,91 @@ +package org.spongycastle.crypto.agreement; + +import java.math.BigInteger; + +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.MQVPrivateParameters; +import org.spongycastle.crypto.params.MQVPublicParameters; +import org.spongycastle.math.ec.ECAlgorithms; +import org.spongycastle.math.ec.ECConstants; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class ECMQVBasicAgreement + implements BasicAgreement +{ + MQVPrivateParameters privParams; + + public void init( + CipherParameters key) + { + this.privParams = (MQVPrivateParameters)key; + } + + public int getFieldSize() + { + return (privParams.getStaticPrivateKey().getParameters().getCurve().getFieldSize() + 7) / 8; + } + + public BigInteger calculateAgreement(CipherParameters pubKey) + { + MQVPublicParameters pubParams = (MQVPublicParameters)pubKey; + + ECPrivateKeyParameters staticPrivateKey = privParams.getStaticPrivateKey(); + + ECPoint agreement = calculateMqvAgreement(staticPrivateKey.getParameters(), staticPrivateKey, + privParams.getEphemeralPrivateKey(), privParams.getEphemeralPublicKey(), + pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey()).normalize(); + + if (agreement.isInfinity()) + { + throw new IllegalStateException("Infinity is not a valid agreement value for MQV"); + } + + return agreement.getAffineXCoord().toBigInteger(); + } + + // The ECMQV Primitive as described in SEC-1, 3.4 + private ECPoint calculateMqvAgreement( + ECDomainParameters parameters, + ECPrivateKeyParameters d1U, + ECPrivateKeyParameters d2U, + ECPublicKeyParameters Q2U, + ECPublicKeyParameters Q1V, + ECPublicKeyParameters Q2V) + { + BigInteger n = parameters.getN(); + int e = (n.bitLength() + 1) / 2; + BigInteger powE = ECConstants.ONE.shiftLeft(e); + + ECCurve curve = parameters.getCurve(); + + ECPoint[] points = new ECPoint[]{ + // The Q2U public key is optional + ECAlgorithms.importPoint(curve, Q2U == null ? parameters.getG().multiply(d2U.getD()) : Q2U.getQ()), + ECAlgorithms.importPoint(curve, Q1V.getQ()), + ECAlgorithms.importPoint(curve, Q2V.getQ()) + }; + + curve.normalizeAll(points); + + ECPoint q2u = points[0], q1v = points[1], q2v = points[2]; + + BigInteger x = q2u.getAffineXCoord().toBigInteger(); + BigInteger xBar = x.mod(powE); + BigInteger Q2UBar = xBar.setBit(e); + BigInteger s = d1U.getD().multiply(Q2UBar).add(d2U.getD()).mod(n); + + BigInteger xPrime = q2v.getAffineXCoord().toBigInteger(); + BigInteger xPrimeBar = xPrime.mod(powE); + BigInteger Q2VBar = xPrimeBar.setBit(e); + + BigInteger hs = parameters.getH().multiply(s).mod(n); + + return ECAlgorithms.sumOfTwoMultiplies( + q1v, Q2VBar.multiply(hs).mod(n), q2v, hs); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/jpake/JPAKEParticipant.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/jpake/JPAKEParticipant.java new file mode 100644 index 000000000..7cf2c4faf --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/jpake/JPAKEParticipant.java @@ -0,0 +1,573 @@ +package org.spongycastle.crypto.agreement.jpake; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.util.Arrays; + +/** + * A participant in a Password Authenticated Key Exchange by Juggling (J-PAKE) exchange. + *
+ * + * The J-PAKE exchange is defined by Feng Hao and Peter Ryan in the paper + * + * "Password Authenticated Key Exchange by Juggling, 2008." + * + * + * The J-PAKE protocol is symmetric. + * There is no notion of a client or server, but rather just two participants. + * An instance of {@link JPAKEParticipant} represents one participant, and + * is the primary interface for executing the exchange. + * + * + * To execute an exchange, construct a {@link JPAKEParticipant} on each end, + * and call the following 7 methods + * (once and only once, in the given order, for each participant, sending messages between them as described): + *+ *
+ * + * + * Each side should derive a session key from the keying material returned by {@link #calculateKeyingMaterial()}. + * The caller is responsible for deriving the session key using a secure key derivation function (KDF). + * + * + * Round 3 is an optional key confirmation process. + * If you do not execute round 3, then there is no assurance that both participants are using the same key. + * (i.e. if the participants used different passwords, then their session keys will differ.) + * + * + * If the round 3 validation succeeds, then the keys are guaranteed to be the same on both sides. + * + * + * The symmetric design can easily support the asymmetric cases when one party initiates the communication. + * e.g. Sometimes the round1 payload and round2 payload may be sent in one pass. + * Also, in some cases, the key confirmation payload can be sent together with the round2 payload. + * These are the trivial techniques to optimize the communication. + * + * + * The key confirmation process is implemented as specified in + * NIST SP 800-56A Revision 1, + * Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes. + * + * + * This class is stateful and NOT threadsafe. + * Each instance should only be used for ONE complete J-PAKE exchange + * (i.e. a new {@link JPAKEParticipant} should be constructed for each new J-PAKE exchange). + * + * + * See {@link JPAKEExample} for example usage. + */ +public class JPAKEParticipant +{ + /* + * Possible internal states. Used for state checking. + */ + + public static final int STATE_INITIALIZED = 0; + public static final int STATE_ROUND_1_CREATED = 10; + public static final int STATE_ROUND_1_VALIDATED = 20; + public static final int STATE_ROUND_2_CREATED = 30; + public static final int STATE_ROUND_2_VALIDATED = 40; + public static final int STATE_KEY_CALCULATED = 50; + public static final int STATE_ROUND_3_CREATED = 60; + public static final int STATE_ROUND_3_VALIDATED = 70; + + /** + * Unique identifier of this participant. + * The two participants in the exchange must NOT share the same id. + */ + private final String participantId; + + /** + * Shared secret. This only contains the secret between construction + * and the call to {@link #calculateKeyingMaterial()}. + * + * i.e. When {@link #calculateKeyingMaterial()} is called, this buffer overwritten with 0's, + * and the field is set to null. + */ + private char[] password; + + /** + * Digest to use during calculations. + */ + private final Digest digest; + + /** + * Source of secure random data. + */ + private final SecureRandom random; + + private final BigInteger p; + private final BigInteger q; + private final BigInteger g; + + /** + * The participantId of the other participant in this exchange. + */ + private String partnerParticipantId; + + /** + * Alice's x1 or Bob's x3. + */ + private BigInteger x1; + /** + * Alice's x2 or Bob's x4. + */ + private BigInteger x2; + /** + * Alice's g^x1 or Bob's g^x3. + */ + private BigInteger gx1; + /** + * Alice's g^x2 or Bob's g^x4. + */ + private BigInteger gx2; + /** + * Alice's g^x3 or Bob's g^x1. + */ + private BigInteger gx3; + /** + * Alice's g^x4 or Bob's g^x2. + */ + private BigInteger gx4; + /** + * Alice's B or Bob's A. + */ + private BigInteger b; + + /** + * The current state. + * See the STATE_* constants for possible values. + */ + private int state; + + /** + * Convenience constructor for a new {@link JPAKEParticipant} that uses + * the {@link JPAKEPrimeOrderGroups#NIST_3072} prime order group, + * a SHA-256 digest, and a default {@link SecureRandom} implementation. + * + * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}. + * + * @param participantId unique identifier of this participant. + * The two participants in the exchange must NOT share the same id. + * @param password shared secret. + * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called). + * Caller should clear the input password as soon as possible. + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if password is empty + */ + public JPAKEParticipant( + String participantId, + char[] password) + { + this( + participantId, + password, + JPAKEPrimeOrderGroups.NIST_3072); + } + + + /** + * Convenience constructor for a new {@link JPAKEParticipant} that uses + * a SHA-256 digest and a default {@link SecureRandom} implementation. + * + * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}. + * + * @param participantId unique identifier of this participant. + * The two participants in the exchange must NOT share the same id. + * @param password shared secret. + * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called). + * Caller should clear the input password as soon as possible. + * @param group prime order group. + * See {@link JPAKEPrimeOrderGroups} for standard groups + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if password is empty + */ + public JPAKEParticipant( + String participantId, + char[] password, + JPAKEPrimeOrderGroup group) + { + this( + participantId, + password, + group, + new SHA256Digest(), + new SecureRandom()); + } + + + /** + * Construct a new {@link JPAKEParticipant}. + * + * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}. + * + * @param participantId unique identifier of this participant. + * The two participants in the exchange must NOT share the same id. + * @param password shared secret. + * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called). + * Caller should clear the input password as soon as possible. + * @param group prime order group. + * See {@link JPAKEPrimeOrderGroups} for standard groups + * @param digest digest to use during zero knowledge proofs and key confirmation (SHA-256 or stronger preferred) + * @param random source of secure random data for x1 and x2, and for the zero knowledge proofs + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if password is empty + */ + public JPAKEParticipant( + String participantId, + char[] password, + JPAKEPrimeOrderGroup group, + Digest digest, + SecureRandom random) + { + JPAKEUtil.validateNotNull(participantId, "participantId"); + JPAKEUtil.validateNotNull(password, "password"); + JPAKEUtil.validateNotNull(group, "p"); + JPAKEUtil.validateNotNull(digest, "digest"); + JPAKEUtil.validateNotNull(random, "random"); + if (password.length == 0) + { + throw new IllegalArgumentException("Password must not be empty."); + } + + this.participantId = participantId; + + /* + * Create a defensive copy so as to fully encapsulate the password. + * + * This array will contain the password for the lifetime of this + * participant BEFORE {@link #calculateKeyingMaterial()} is called. + * + * i.e. When {@link #calculateKeyingMaterial()} is called, the array will be cleared + * in order to remove the password from memory. + * + * The caller is responsible for clearing the original password array + * given as input to this constructor. + */ + this.password = Arrays.copyOf(password, password.length); + + this.p = group.getP(); + this.q = group.getQ(); + this.g = group.getG(); + + this.digest = digest; + this.random = random; + + this.state = STATE_INITIALIZED; + } + + /** + * Gets the current state of this participant. + * See the STATE_* constants for possible values. + */ + public int getState() + { + return this.state; + } + + /** + * Creates and returns the payload to send to the other participant during round 1. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_CREATED}. + */ + public JPAKERound1Payload createRound1PayloadToSend() + { + if (this.state >= STATE_ROUND_1_CREATED) + { + throw new IllegalStateException("Round1 payload already created for " + participantId); + } + + this.x1 = JPAKEUtil.generateX1(q, random); + this.x2 = JPAKEUtil.generateX2(q, random); + + this.gx1 = JPAKEUtil.calculateGx(p, g, x1); + this.gx2 = JPAKEUtil.calculateGx(p, g, x2); + BigInteger[] knowledgeProofForX1 = JPAKEUtil.calculateZeroKnowledgeProof(p, q, g, gx1, x1, participantId, digest, random); + BigInteger[] knowledgeProofForX2 = JPAKEUtil.calculateZeroKnowledgeProof(p, q, g, gx2, x2, participantId, digest, random); + + this.state = STATE_ROUND_1_CREATED; + + return new JPAKERound1Payload(participantId, gx1, gx2, knowledgeProofForX1, knowledgeProofForX2); + } + + /** + * Validates the payload received from the other participant during round 1. + * + * + * Must be called prior to {@link #createRound2PayloadToSend()}. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_VALIDATED}. + * + * @throws CryptoException if validation fails. + * @throws IllegalStateException if called multiple times. + */ + public void validateRound1PayloadReceived(JPAKERound1Payload round1PayloadReceived) + throws CryptoException + { + if (this.state >= STATE_ROUND_1_VALIDATED) + { + throw new IllegalStateException("Validation already attempted for round1 payload for" + participantId); + } + this.partnerParticipantId = round1PayloadReceived.getParticipantId(); + this.gx3 = round1PayloadReceived.getGx1(); + this.gx4 = round1PayloadReceived.getGx2(); + + BigInteger[] knowledgeProofForX3 = round1PayloadReceived.getKnowledgeProofForX1(); + BigInteger[] knowledgeProofForX4 = round1PayloadReceived.getKnowledgeProofForX2(); + + JPAKEUtil.validateParticipantIdsDiffer(participantId, round1PayloadReceived.getParticipantId()); + JPAKEUtil.validateGx4(gx4); + JPAKEUtil.validateZeroKnowledgeProof(p, q, g, gx3, knowledgeProofForX3, round1PayloadReceived.getParticipantId(), digest); + JPAKEUtil.validateZeroKnowledgeProof(p, q, g, gx4, knowledgeProofForX4, round1PayloadReceived.getParticipantId(), digest); + + this.state = STATE_ROUND_1_VALIDATED; + } + + /** + * Creates and returns the payload to send to the other participant during round 2. + * + * + * {@link #validateRound1PayloadReceived(JPAKERound1Payload)} must be called prior to this method. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_CREATED}. + * + * @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times + */ + public JPAKERound2Payload createRound2PayloadToSend() + { + if (this.state >= STATE_ROUND_2_CREATED) + { + throw new IllegalStateException("Round2 payload already created for " + this.participantId); + } + if (this.state < STATE_ROUND_1_VALIDATED) + { + throw new IllegalStateException("Round1 payload must be validated prior to creating Round2 payload for " + this.participantId); + } + BigInteger gA = JPAKEUtil.calculateGA(p, gx1, gx3, gx4); + BigInteger s = JPAKEUtil.calculateS(password); + BigInteger x2s = JPAKEUtil.calculateX2s(q, x2, s); + BigInteger A = JPAKEUtil.calculateA(p, q, gA, x2s); + BigInteger[] knowledgeProofForX2s = JPAKEUtil.calculateZeroKnowledgeProof(p, q, gA, A, x2s, participantId, digest, random); + + this.state = STATE_ROUND_2_CREATED; + + return new JPAKERound2Payload(participantId, A, knowledgeProofForX2s); + } + + /** + * Validates the payload received from the other participant during round 2. + * + * + * Note that this DOES NOT detect a non-common password. + * The only indication of a non-common password is through derivation + * of different keys (which can be detected explicitly by executing round 3 and round 4) + * + * + * Must be called prior to {@link #calculateKeyingMaterial()}. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_VALIDATED}. + * + * @throws CryptoException if validation fails. + * @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times + */ + public void validateRound2PayloadReceived(JPAKERound2Payload round2PayloadReceived) + throws CryptoException + { + if (this.state >= STATE_ROUND_2_VALIDATED) + { + throw new IllegalStateException("Validation already attempted for round2 payload for" + participantId); + } + if (this.state < STATE_ROUND_1_VALIDATED) + { + throw new IllegalStateException("Round1 payload must be validated prior to validating Round2 payload for " + this.participantId); + } + BigInteger gB = JPAKEUtil.calculateGA(p, gx3, gx1, gx2); + this.b = round2PayloadReceived.getA(); + BigInteger[] knowledgeProofForX4s = round2PayloadReceived.getKnowledgeProofForX2s(); + + JPAKEUtil.validateParticipantIdsDiffer(participantId, round2PayloadReceived.getParticipantId()); + JPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, round2PayloadReceived.getParticipantId()); + JPAKEUtil.validateGa(gB); + JPAKEUtil.validateZeroKnowledgeProof(p, q, gB, b, knowledgeProofForX4s, round2PayloadReceived.getParticipantId(), digest); + + this.state = STATE_ROUND_2_VALIDATED; + } + + /** + * Calculates and returns the key material. + * A session key must be derived from this key material using a secure key derivation function (KDF). + * The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}). + * + * + * The keying material will be identical for each participant if and only if + * each participant's password is the same. i.e. If the participants do not + * share the same password, then each participant will derive a different key. + * Therefore, if you immediately start using a key derived from + * the keying material, then you must handle detection of incorrect keys. + * If you want to handle this detection explicitly, you can optionally perform + * rounds 3 and 4. See {@link JPAKEParticipant} for details on how to execute + * rounds 3 and 4. + * + * + * The keying material will be in the range [0, p-1]. + * + * + * {@link #validateRound2PayloadReceived(JPAKERound2Payload)} must be called prior to this method. + * + * + * As a side effect, the internal {@link #password} array is cleared, since it is no longer needed. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_KEY_CALCULATED}. + * + * @throws IllegalStateException if called prior to {@link #validateRound2PayloadReceived(JPAKERound2Payload)}, + * or if called multiple times. + */ + public BigInteger calculateKeyingMaterial() + { + if (this.state >= STATE_KEY_CALCULATED) + { + throw new IllegalStateException("Key already calculated for " + participantId); + } + if (this.state < STATE_ROUND_2_VALIDATED) + { + throw new IllegalStateException("Round2 payload must be validated prior to creating key for " + participantId); + } + BigInteger s = JPAKEUtil.calculateS(password); + + /* + * Clear the password array from memory, since we don't need it anymore. + * + * Also set the field to null as a flag to indicate that the key has already been calculated. + */ + Arrays.fill(password, (char)0); + this.password = null; + + BigInteger keyingMaterial = JPAKEUtil.calculateKeyingMaterial(p, q, gx4, x2, s, b); + + /* + * Clear the ephemeral private key fields as well. + * Note that we're relying on the garbage collector to do its job to clean these up. + * The old objects will hang around in memory until the garbage collector destroys them. + * + * If the ephemeral private keys x1 and x2 are leaked, + * the attacker might be able to brute-force the password. + */ + this.x1 = null; + this.x2 = null; + this.b = null; + + /* + * Do not clear gx* yet, since those are needed by round 3. + */ + + this.state = STATE_KEY_CALCULATED; + + return keyingMaterial; + } + + + /** + * Creates and returns the payload to send to the other participant during round 3. + * + * + * See {@link JPAKEParticipant} for more details on round 3. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_CREATED}. + * + * @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}. + * @throws IllegalStateException if called prior to {@link #calculateKeyingMaterial()}, or multiple times + */ + public JPAKERound3Payload createRound3PayloadToSend(BigInteger keyingMaterial) + { + if (this.state >= STATE_ROUND_3_CREATED) + { + throw new IllegalStateException("Round3 payload already created for " + this.participantId); + } + if (this.state < STATE_KEY_CALCULATED) + { + throw new IllegalStateException("Keying material must be calculated prior to creating Round3 payload for " + this.participantId); + } + + BigInteger macTag = JPAKEUtil.calculateMacTag( + this.participantId, + this.partnerParticipantId, + this.gx1, + this.gx2, + this.gx3, + this.gx4, + keyingMaterial, + this.digest); + + this.state = STATE_ROUND_3_CREATED; + + return new JPAKERound3Payload(participantId, macTag); + } + + /** + * Validates the payload received from the other participant during round 3. + * + * + * See {@link JPAKEParticipant} for more details on round 3. + * + * + * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_VALIDATED}. + * + * @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}. + * @throws CryptoException if validation fails. + * @throws IllegalStateException if called prior to {@link #calculateKeyingMaterial()}, or multiple times + */ + public void validateRound3PayloadReceived(JPAKERound3Payload round3PayloadReceived, BigInteger keyingMaterial) + throws CryptoException + { + if (this.state >= STATE_ROUND_3_VALIDATED) + { + throw new IllegalStateException("Validation already attempted for round3 payload for" + participantId); + } + if (this.state < STATE_KEY_CALCULATED) + { + throw new IllegalStateException("Keying material must be calculated validated prior to validating Round3 payload for " + this.participantId); + } + JPAKEUtil.validateParticipantIdsDiffer(participantId, round3PayloadReceived.getParticipantId()); + JPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, round3PayloadReceived.getParticipantId()); + + JPAKEUtil.validateMacTag( + this.participantId, + this.partnerParticipantId, + this.gx1, + this.gx2, + this.gx3, + this.gx4, + keyingMaterial, + this.digest, + round3PayloadReceived.getMacTag()); + + + /* + * Clear the rest of the fields. + */ + this.gx1 = null; + this.gx2 = null; + this.gx3 = null; + this.gx4 = null; + + this.state = STATE_ROUND_3_VALIDATED; + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java new file mode 100644 index 000000000..01f82e68c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java @@ -0,0 +1,122 @@ +package org.spongycastle.crypto.agreement.jpake; + +import java.math.BigInteger; + +/** + * A pre-computed prime order group for use during a J-PAKE exchange. + * + * + * Typically a Schnorr group is used. In general, J-PAKE can use any prime order group + * that is suitable for public key cryptography, including elliptic curve cryptography. + * + * + * See {@link JPAKEPrimeOrderGroups} for convenient standard groups. + * + * + * NIST publishes + * many groups that can be used for the desired level of security. + */ +public class JPAKEPrimeOrderGroup +{ + private final BigInteger p; + private final BigInteger q; + private final BigInteger g; + + /** + * Constructs a new {@link JPAKEPrimeOrderGroup}. + * + * + * In general, you should use one of the pre-approved groups from + * {@link JPAKEPrimeOrderGroups}, rather than manually constructing one. + * + * + * The following basic checks are performed: + *- {@link #createRound1PayloadToSend()} - and send the payload to the other participant
+ *- {@link #validateRound1PayloadReceived(JPAKERound1Payload)} - use the payload received from the other participant
+ *- {@link #createRound2PayloadToSend()} - and send the payload to the other participant
+ *- {@link #validateRound2PayloadReceived(JPAKERound2Payload)} - use the payload received from the other participant
+ *- {@link #calculateKeyingMaterial()}
+ *- {@link #createRound3PayloadToSend(BigInteger)} - and send the payload to the other participant
+ *- {@link #validateRound3PayloadReceived(JPAKERound3Payload, BigInteger)} - use the payload received from the other participant
+ *
+ * Alice could simply check ga != 1 to ensure it is a generator. + * In fact, as we will explain in Section 3, (x1 + x3 + x4 ) is random over Zq even in the face of active attacks. + * Hence, the probability for ga = 1 is extremely small - on the order of 2^160 for 160-bit q. + *+ * + * @throws CryptoException if ga is 1 + */ + public static void validateGa(BigInteger ga) + throws CryptoException + { + if (ga.equals(ONE)) + { + throw new CryptoException("ga is equal to 1. It should not be. The chances of this happening are on the order of 2^160 for a 160-bit q. Try again."); + } + } + + /** + * Validates the zero knowledge proof (generated by + * {@link #calculateZeroKnowledgeProof(BigInteger, BigInteger, BigInteger, BigInteger, BigInteger, String, Digest, SecureRandom)}) + * is correct. + * + * @throws CryptoException if the zero knowledge proof is not correct + */ + public static void validateZeroKnowledgeProof( + BigInteger p, + BigInteger q, + BigInteger g, + BigInteger gx, + BigInteger[] zeroKnowledgeProof, + String participantId, + Digest digest) + throws CryptoException + { + + /* sig={g^v,r} */ + BigInteger gv = zeroKnowledgeProof[0]; + BigInteger r = zeroKnowledgeProof[1]; + + BigInteger h = calculateHashForZeroKnowledgeProof(g, gv, gx, participantId, digest); + if (!(gx.compareTo(ZERO) == 1 && // g^x > 0 + gx.compareTo(p) == -1 && // g^x < p + gx.modPow(q, p).compareTo(ONE) == 0 && // g^x^q mod q = 1 + /* + * Below, I took an straightforward way to compute g^r * g^x^h, + * which needs 2 exp. Using a simultaneous computation technique + * would only need 1 exp. + */ + g.modPow(r, p).multiply(gx.modPow(h, p)).mod(p).compareTo(gv) == 0)) // g^v=g^r * g^x^h + { + throw new CryptoException("Zero-knowledge proof validation failed"); + } + } + + /** + * Calculates the keying material, which can be done after round 2 has completed. + * A session key must be derived from this key material using a secure key derivation function (KDF). + * The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}). + * + * + *
+ * KeyingMaterial = (B/g^{x2*x4*s})^x2 + *+ */ + public static BigInteger calculateKeyingMaterial( + BigInteger p, + BigInteger q, + BigInteger gx4, + BigInteger x2, + BigInteger s, + BigInteger B) + { + return gx4.modPow(x2.multiply(s).negate().mod(q), p).multiply(B).modPow(x2, p); + } + + /** + * Validates that the given participant ids are not equal. + * (For the J-PAKE exchange, each participant must use a unique id.) + * + * @throws CryptoException if the participantId strings are equal. + */ + public static void validateParticipantIdsDiffer(String participantId1, String participantId2) + throws CryptoException + { + if (participantId1.equals(participantId2)) + { + throw new CryptoException( + "Both participants are using the same participantId (" + + participantId1 + + "). This is not allowed. " + + "Each participant must use a unique participantId."); + } + } + + /** + * Validates that the given participant ids are equal. + * This is used to ensure that the payloads received from + * each round all come from the same participant. + * + * @throws CryptoException if the participantId strings are equal. + */ + public static void validateParticipantIdsEqual(String expectedParticipantId, String actualParticipantId) + throws CryptoException + { + if (!expectedParticipantId.equals(actualParticipantId)) + { + throw new CryptoException( + "Received payload from incorrect partner (" + + actualParticipantId + + "). Expected to receive payload from " + + expectedParticipantId + + "."); + } + } + + /** + * Validates that the given object is not null. + * + * @param object object in question + * @param description name of the object (to be used in exception message) + * @throws NullPointerException if the object is null. + */ + public static void validateNotNull(Object object, String description) + { + if (object == null) + { + throw new NullPointerException(description + " must not be null"); + } + } + + /** + * Calculates the MacTag (to be used for key confirmation), as defined by + * NIST SP 800-56A Revision 1, + * Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes. + * + * + *
+ * MacTag = HMAC(MacKey, MacLen, MacData) + * + * MacKey = H(K || "JPAKE_KC") + * + * MacData = "KC_1_U" || participantId || partnerParticipantId || gx1 || gx2 || gx3 || gx4 + * + * Note that both participants use "KC_1_U" because the sender of the round 3 message + * is always the initiator for key confirmation. + * + * HMAC = {@link HMac} used with the given {@link Digest} + * H = The given {@link Digest} + * MacLen = length of MacTag + *+ * + */ + public static BigInteger calculateMacTag( + String participantId, + String partnerParticipantId, + BigInteger gx1, + BigInteger gx2, + BigInteger gx3, + BigInteger gx4, + BigInteger keyingMaterial, + Digest digest) + { + byte[] macKey = calculateMacKey( + keyingMaterial, + digest); + + HMac mac = new HMac(digest); + byte[] macOutput = new byte[mac.getMacSize()]; + mac.init(new KeyParameter(macKey)); + + /* + * MacData = "KC_1_U" || participantId_Alice || participantId_Bob || gx1 || gx2 || gx3 || gx4. + */ + updateMac(mac, "KC_1_U"); + updateMac(mac, participantId); + updateMac(mac, partnerParticipantId); + updateMac(mac, gx1); + updateMac(mac, gx2); + updateMac(mac, gx3); + updateMac(mac, gx4); + + mac.doFinal(macOutput, 0); + + Arrays.fill(macKey, (byte)0); + + return new BigInteger(macOutput); + + } + + /** + * Calculates the MacKey (i.e. the key to use when calculating the MagTag for key confirmation). + * + * + *
+ * MacKey = H(K || "JPAKE_KC") + *+ */ + private static byte[] calculateMacKey(BigInteger keyingMaterial, Digest digest) + { + digest.reset(); + + updateDigest(digest, keyingMaterial); + /* + * This constant is used to ensure that the macKey is NOT the same as the derived key. + */ + updateDigest(digest, "JPAKE_KC"); + + byte[] output = new byte[digest.getDigestSize()]; + digest.doFinal(output, 0); + + return output; + } + + /** + * Validates the MacTag received from the partner participant. + * + * + * @param partnerMacTag the MacTag received from the partner. + * @throws CryptoException if the participantId strings are equal. + */ + public static void validateMacTag( + String participantId, + String partnerParticipantId, + BigInteger gx1, + BigInteger gx2, + BigInteger gx3, + BigInteger gx4, + BigInteger keyingMaterial, + Digest digest, + BigInteger partnerMacTag) + throws CryptoException + { + /* + * Calculate the expected MacTag using the parameters as the partner + * would have used when the partner called calculateMacTag. + * + * i.e. basically all the parameters are reversed. + * participantId <-> partnerParticipantId + * x1 <-> x3 + * x2 <-> x4 + */ + BigInteger expectedMacTag = calculateMacTag( + partnerParticipantId, + participantId, + gx3, + gx4, + gx1, + gx2, + keyingMaterial, + digest); + + if (!expectedMacTag.equals(partnerMacTag)) + { + throw new CryptoException( + "Partner MacTag validation failed. " + + "Therefore, the password, MAC, or digest algorithm of each participant does not match."); + } + } + + private static void updateDigest(Digest digest, BigInteger bigInteger) + { + byte[] byteArray = BigIntegers.asUnsignedByteArray(bigInteger); + digest.update(byteArray, 0, byteArray.length); + Arrays.fill(byteArray, (byte)0); + } + + private static void updateDigestIncludingSize(Digest digest, BigInteger bigInteger) + { + byte[] byteArray = BigIntegers.asUnsignedByteArray(bigInteger); + digest.update(intToByteArray(byteArray.length), 0, 4); + digest.update(byteArray, 0, byteArray.length); + Arrays.fill(byteArray, (byte)0); + } + + private static void updateDigest(Digest digest, String string) + { + byte[] byteArray = Strings.toUTF8ByteArray(string); + digest.update(byteArray, 0, byteArray.length); + Arrays.fill(byteArray, (byte)0); + } + + private static void updateDigestIncludingSize(Digest digest, String string) + { + byte[] byteArray = Strings.toUTF8ByteArray(string); + digest.update(intToByteArray(byteArray.length), 0, 4); + digest.update(byteArray, 0, byteArray.length); + Arrays.fill(byteArray, (byte)0); + } + + private static void updateMac(Mac mac, BigInteger bigInteger) + { + byte[] byteArray = BigIntegers.asUnsignedByteArray(bigInteger); + mac.update(byteArray, 0, byteArray.length); + Arrays.fill(byteArray, (byte)0); + } + + private static void updateMac(Mac mac, String string) + { + byte[] byteArray = Strings.toUTF8ByteArray(string); + mac.update(byteArray, 0, byteArray.length); + Arrays.fill(byteArray, (byte)0); + } + + private static byte[] intToByteArray(int value) + { + return new byte[]{ + (byte)(value >>> 24), + (byte)(value >>> 16), + (byte)(value >>> 8), + (byte)value + }; + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/DHKDFParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/DHKDFParameters.java new file mode 100644 index 000000000..96656cedf --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/DHKDFParameters.java @@ -0,0 +1,54 @@ +package org.spongycastle.crypto.agreement.kdf; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.crypto.DerivationParameters; + +public class DHKDFParameters + implements DerivationParameters +{ + private ASN1ObjectIdentifier algorithm; + private int keySize; + private byte[] z; + private byte[] extraInfo; + + public DHKDFParameters( + DERObjectIdentifier algorithm, + int keySize, + byte[] z) + { + this(algorithm, keySize, z, null); + } + + public DHKDFParameters( + DERObjectIdentifier algorithm, + int keySize, + byte[] z, + byte[] extraInfo) + { + this.algorithm = new ASN1ObjectIdentifier(algorithm.getId()); + this.keySize = keySize; + this.z = z; + this.extraInfo = extraInfo; + } + + public ASN1ObjectIdentifier getAlgorithm() + { + return algorithm; + } + + public int getKeySize() + { + return keySize; + } + + public byte[] getZ() + { + return z; + } + + public byte[] getExtraInfo() + { + return extraInfo; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/DHKEKGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/DHKEKGenerator.java new file mode 100644 index 000000000..6d27a9db7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/DHKEKGenerator.java @@ -0,0 +1,131 @@ +package org.spongycastle.crypto.agreement.kdf; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.DerivationFunction; +import org.spongycastle.crypto.DerivationParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.util.Pack; + +/** + * RFC 2631 Diffie-hellman KEK derivation function. + */ +public class DHKEKGenerator + implements DerivationFunction +{ + private final Digest digest; + + private DERObjectIdentifier algorithm; + private int keySize; + private byte[] z; + private byte[] partyAInfo; + + public DHKEKGenerator( + Digest digest) + { + this.digest = digest; + } + + public void init(DerivationParameters param) + { + DHKDFParameters params = (DHKDFParameters)param; + + this.algorithm = params.getAlgorithm(); + this.keySize = params.getKeySize(); + this.z = params.getZ(); + this.partyAInfo = params.getExtraInfo(); + } + + public Digest getDigest() + { + return digest; + } + + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException + { + if ((out.length - len) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBytes = len; + int outLen = digest.getDigestSize(); + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) + { + throw new IllegalArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.getDigestSize()]; + + int counter = 1; + + for (int i = 0; i < cThreshold; i++) + { + digest.update(z, 0, z.length); + + // OtherInfo + ASN1EncodableVector v1 = new ASN1EncodableVector(); + // KeySpecificInfo + ASN1EncodableVector v2 = new ASN1EncodableVector(); + + v2.add(algorithm); + v2.add(new DEROctetString(Pack.intToBigEndian(counter))); + + v1.add(new DERSequence(v2)); + + if (partyAInfo != null) + { + v1.add(new DERTaggedObject(true, 0, new DEROctetString(partyAInfo))); + } + + v1.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize)))); + + try + { + byte[] other = new DERSequence(v1).getEncoded(ASN1Encoding.DER); + + digest.update(other, 0, other.length); + } + catch (IOException e) + { + throw new IllegalArgumentException("unable to encode parameter info: " + e.getMessage()); + } + + digest.doFinal(dig, 0); + + if (len > outLen) + { + System.arraycopy(dig, 0, out, outOff, outLen); + outOff += outLen; + len -= outLen; + } + else + { + System.arraycopy(dig, 0, out, outOff, len); + } + + counter++; + } + + digest.reset(); + + return (int)oBytes; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/ECDHKEKGenerator.java new file mode 100644 index 000000000..d427f9895 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/kdf/ECDHKEKGenerator.java @@ -0,0 +1,74 @@ +package org.spongycastle.crypto.agreement.kdf; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERTaggedObject; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.DerivationParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.DigestDerivationFunction; +import org.spongycastle.crypto.generators.KDF2BytesGenerator; +import org.spongycastle.crypto.params.KDFParameters; +import org.spongycastle.crypto.util.Pack; + +/** + * X9.63 based key derivation function for ECDH CMS. + */ +public class ECDHKEKGenerator + implements DigestDerivationFunction +{ + private DigestDerivationFunction kdf; + + private ASN1ObjectIdentifier algorithm; + private int keySize; + private byte[] z; + + public ECDHKEKGenerator( + Digest digest) + { + this.kdf = new KDF2BytesGenerator(digest); + } + + public void init(DerivationParameters param) + { + DHKDFParameters params = (DHKDFParameters)param; + + this.algorithm = params.getAlgorithm(); + this.keySize = params.getKeySize(); + this.z = params.getZ(); + } + + public Digest getDigest() + { + return kdf.getDigest(); + } + + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException + { + // TODO Create an ASN.1 class for this (RFC3278) + // ECC-CMS-SharedInfo + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new AlgorithmIdentifier(algorithm, DERNull.INSTANCE)); + v.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize)))); + + try + { + kdf.init(new KDFParameters(z, new DERSequence(v).getEncoded(ASN1Encoding.DER))); + } + catch (IOException e) + { + throw new IllegalArgumentException("unable to initialise kdf: " + e.getMessage()); + } + + return kdf.generateBytes(out, outOff, len); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java new file mode 100644 index 000000000..d47ad39ec --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java @@ -0,0 +1,93 @@ +package org.spongycastle.crypto.agreement.srp; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; + +/** + * Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe. + * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper + * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002" + */ +public class SRP6Client +{ + protected BigInteger N; + protected BigInteger g; + + protected BigInteger a; + protected BigInteger A; + + protected BigInteger B; + + protected BigInteger x; + protected BigInteger u; + protected BigInteger S; + + protected Digest digest; + protected SecureRandom random; + + public SRP6Client() + { + } + + /** + * Initialises the client to begin new authentication attempt + * @param N The safe prime associated with the client's verifier + * @param g The group parameter associated with the client's verifier + * @param digest The digest algorithm associated with the client's verifier + * @param random For key generation + */ + public void init(BigInteger N, BigInteger g, Digest digest, SecureRandom random) + { + this.N = N; + this.g = g; + this.digest = digest; + this.random = random; + } + + /** + * Generates client's credentials given the client's salt, identity and password + * @param salt The salt used in the client's verifier. + * @param identity The user's identity (eg. username) + * @param password The user's password + * @return Client's public value to send to server + */ + public BigInteger generateClientCredentials(byte[] salt, byte[] identity, byte[] password) + { + this.x = SRP6Util.calculateX(digest, N, salt, identity, password); + this.a = selectPrivateValue(); + this.A = g.modPow(a, N); + + return A; + } + + /** + * Generates client's verification message given the server's credentials + * @param serverB The server's credentials + * @return Client's verification message for the server + * @throws CryptoException If server's credentials are invalid + */ + public BigInteger calculateSecret(BigInteger serverB) throws CryptoException + { + this.B = SRP6Util.validatePublicValue(N, serverB); + this.u = SRP6Util.calculateU(digest, N, A, B); + this.S = calculateS(); + + return S; + } + + protected BigInteger selectPrivateValue() + { + return SRP6Util.generatePrivateValue(digest, N, g, random); + } + + private BigInteger calculateS() + { + BigInteger k = SRP6Util.calculateK(digest, N, g); + BigInteger exp = u.multiply(x).add(a); + BigInteger tmp = g.modPow(x, N).multiply(k).mod(N); + return B.subtract(tmp).mod(N).modPow(exp, N); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java new file mode 100644 index 000000000..50f6f7cea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java @@ -0,0 +1,90 @@ +package org.spongycastle.crypto.agreement.srp; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; + +/** + * Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe. + * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper + * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002" + */ +public class SRP6Server +{ + protected BigInteger N; + protected BigInteger g; + protected BigInteger v; + + protected SecureRandom random; + protected Digest digest; + + protected BigInteger A; + + protected BigInteger b; + protected BigInteger B; + + protected BigInteger u; + protected BigInteger S; + + public SRP6Server() + { + } + + /** + * Initialises the server to accept a new client authentication attempt + * @param N The safe prime associated with the client's verifier + * @param g The group parameter associated with the client's verifier + * @param v The client's verifier + * @param digest The digest algorithm associated with the client's verifier + * @param random For key generation + */ + public void init(BigInteger N, BigInteger g, BigInteger v, Digest digest, SecureRandom random) + { + this.N = N; + this.g = g; + this.v = v; + + this.random = random; + this.digest = digest; + } + + /** + * Generates the server's credentials that are to be sent to the client. + * @return The server's public value to the client + */ + public BigInteger generateServerCredentials() + { + BigInteger k = SRP6Util.calculateK(digest, N, g); + this.b = selectPrivateValue(); + this.B = k.multiply(v).mod(N).add(g.modPow(b, N)).mod(N); + + return B; + } + + /** + * Processes the client's credentials. If valid the shared secret is generated and returned. + * @param clientA The client's credentials + * @return A shared secret BigInteger + * @throws CryptoException If client's credentials are invalid + */ + public BigInteger calculateSecret(BigInteger clientA) throws CryptoException + { + this.A = SRP6Util.validatePublicValue(N, clientA); + this.u = SRP6Util.calculateU(digest, N, A, B); + this.S = calculateS(); + + return S; + } + + protected BigInteger selectPrivateValue() + { + return SRP6Util.generatePrivateValue(digest, N, g, random); + } + + private BigInteger calculateS() + { + return v.modPow(u, N).multiply(A).mod(N).modPow(b, N); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java new file mode 100644 index 000000000..40ec01c0f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java @@ -0,0 +1,91 @@ +package org.spongycastle.crypto.agreement.srp; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.util.BigIntegers; + +public class SRP6Util +{ + private static BigInteger ZERO = BigInteger.valueOf(0); + private static BigInteger ONE = BigInteger.valueOf(1); + + public static BigInteger calculateK(Digest digest, BigInteger N, BigInteger g) + { + return hashPaddedPair(digest, N, N, g); + } + + public static BigInteger calculateU(Digest digest, BigInteger N, BigInteger A, BigInteger B) + { + return hashPaddedPair(digest, N, A, B); + } + + public static BigInteger calculateX(Digest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password) + { + byte[] output = new byte[digest.getDigestSize()]; + + digest.update(identity, 0, identity.length); + digest.update((byte)':'); + digest.update(password, 0, password.length); + digest.doFinal(output, 0); + + digest.update(salt, 0, salt.length); + digest.update(output, 0, output.length); + digest.doFinal(output, 0); + + return new BigInteger(1, output); + } + + public static BigInteger generatePrivateValue(Digest digest, BigInteger N, BigInteger g, SecureRandom random) + { + int minBits = Math.min(256, N.bitLength() / 2); + BigInteger min = ONE.shiftLeft(minBits - 1); + BigInteger max = N.subtract(ONE); + + return BigIntegers.createRandomInRange(min, max, random); + } + + public static BigInteger validatePublicValue(BigInteger N, BigInteger val) + throws CryptoException + { + val = val.mod(N); + + // Check that val % N != 0 + if (val.equals(ZERO)) + { + throw new CryptoException("Invalid public value: 0"); + } + + return val; + } + + private static BigInteger hashPaddedPair(Digest digest, BigInteger N, BigInteger n1, BigInteger n2) + { + int padLength = (N.bitLength() + 7) / 8; + + byte[] n1_bytes = getPadded(n1, padLength); + byte[] n2_bytes = getPadded(n2, padLength); + + digest.update(n1_bytes, 0, n1_bytes.length); + digest.update(n2_bytes, 0, n2_bytes.length); + + byte[] output = new byte[digest.getDigestSize()]; + digest.doFinal(output, 0); + + return new BigInteger(1, output); + } + + private static byte[] getPadded(BigInteger n, int length) + { + byte[] bs = BigIntegers.asUnsignedByteArray(n); + if (bs.length < length) + { + byte[] tmp = new byte[length]; + System.arraycopy(bs, 0, tmp, length - bs.length, bs.length); + bs = tmp; + } + return bs; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java new file mode 100644 index 000000000..0324af187 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java @@ -0,0 +1,47 @@ +package org.spongycastle.crypto.agreement.srp; + +import java.math.BigInteger; + +import org.spongycastle.crypto.Digest; + +/** + * Generates new SRP verifier for user + */ +public class SRP6VerifierGenerator +{ + protected BigInteger N; + protected BigInteger g; + protected Digest digest; + + public SRP6VerifierGenerator() + { + } + + /** + * Initialises generator to create new verifiers + * @param N The safe prime to use (see DHParametersGenerator) + * @param g The group parameter to use (see DHParametersGenerator) + * @param digest The digest to use. The same digest type will need to be used later for the actual authentication + * attempt. Also note that the final session key size is dependent on the chosen digest. + */ + public void init(BigInteger N, BigInteger g, Digest digest) + { + this.N = N; + this.g = g; + this.digest = digest; + } + + /** + * Creates a new SRP verifier + * @param salt The salt to use, generally should be large and random + * @param identity The user's identifying information (eg. username) + * @param password The user's password + * @return A new verifier for use in future SRP authentication + */ + public BigInteger generateVerifier(byte[] salt, byte[] identity, byte[] password) + { + BigInteger x = SRP6Util.calculateX(digest, N, salt, identity, password); + + return g.modPow(x, N); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/commitments/GeneralHashCommitter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/commitments/GeneralHashCommitter.java new file mode 100644 index 000000000..e37c534e1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/commitments/GeneralHashCommitter.java @@ -0,0 +1,93 @@ +package org.spongycastle.crypto.commitments; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.Commitment; +import org.spongycastle.crypto.Committer; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.util.Arrays; + +/** + * A basic hash-committer based on the one described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking", + * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002). + *
+ * The algorithm used by this class differs from the one given in that it includes the length of the message in the hash calculation. + *
+ */ +public class GeneralHashCommitter + implements Committer +{ + private final Digest digest; + private final int byteLength; + private final SecureRandom random; + + /** + * Base Constructor. The maximum message length that can be committed to is half the length of the internal + * block size for the digest (ExtendedDigest.getBlockLength()). + * + * @param digest digest to use for creating commitments. + * @param random source of randomness for generating secrets. + */ + public GeneralHashCommitter(ExtendedDigest digest, SecureRandom random) + { + this.digest = digest; + this.byteLength = digest.getByteLength(); + this.random = random; + } + + /** + * Generate a commitment for the passed in message. + * + * @param message the message to be committed to, + * @return a Commitment + */ + public Commitment commit(byte[] message) + { + if (message.length > byteLength / 2) + { + throw new DataLengthException("Message to be committed to too large for digest."); + } + + byte[] w = new byte[byteLength - message.length]; + + random.nextBytes(w); + + return new Commitment(w, calculateCommitment(w, message)); + } + + /** + * Return true if the passed in commitment represents a commitment to the passed in message. + * + * @param commitment a commitment previously generated. + * @param message the message that was expected to have been committed to. + * @return true if commitment matches message, false otherwise. + */ + public boolean isRevealed(Commitment commitment, byte[] message) + { + if (message.length + commitment.getSecret().length != byteLength) + { + throw new DataLengthException("Message and witness secret lengths do not match."); + } + + byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message); + + return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment); + } + + private byte[] calculateCommitment(byte[] w, byte[] message) + { + byte[] commitment = new byte[digest.getDigestSize()]; + + digest.update(w, 0, w.length); + digest.update(message, 0, message.length); + + digest.update((byte)((message.length >>> 8))); + digest.update((byte)(message.length)); + + digest.doFinal(commitment, 0); + + return commitment; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/commitments/HashCommitter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/commitments/HashCommitter.java new file mode 100644 index 000000000..4320d86d3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/commitments/HashCommitter.java @@ -0,0 +1,89 @@ +package org.spongycastle.crypto.commitments; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.Commitment; +import org.spongycastle.crypto.Committer; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.util.Arrays; + +/** + * A basic hash-committer as described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking", + * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002). + *+ * Use this class if you can enforce fixed length for messages. If you need something more general, use the GeneralHashCommitter. + *
+ */ +public class HashCommitter + implements Committer +{ + private final Digest digest; + private final int byteLength; + private final SecureRandom random; + + /** + * Base Constructor. The maximum message length that can be committed to is half the length of the internal + * block size for the digest (ExtendedDigest.getBlockLength()). + * + * @param digest digest to use for creating commitments. + * @param random source of randomness for generating secrets. + */ + public HashCommitter(ExtendedDigest digest, SecureRandom random) + { + this.digest = digest; + this.byteLength = digest.getByteLength(); + this.random = random; + } + + /** + * Generate a commitment for the passed in message. + * + * @param message the message to be committed to, + * @return a Commitment + */ + public Commitment commit(byte[] message) + { + if (message.length > byteLength / 2) + { + throw new DataLengthException("Message to be committed to too large for digest."); + } + + byte[] w = new byte[byteLength - message.length]; + + random.nextBytes(w); + + return new Commitment(w, calculateCommitment(w, message)); + } + + /** + * Return true if the passed in commitment represents a commitment to the passed in message. + * + * @param commitment a commitment previously generated. + * @param message the message that was expected to have been committed to. + * @return true if commitment matches message, false otherwise. + */ + public boolean isRevealed(Commitment commitment, byte[] message) + { + if (message.length + commitment.getSecret().length != byteLength) + { + throw new DataLengthException("Message and witness secret lengths do not match."); + } + + byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message); + + return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment); + } + + private byte[] calculateCommitment(byte[] w, byte[] message) + { + byte[] commitment = new byte[digest.getDigestSize()]; + + digest.update(w, 0, w.length); + digest.update(message, 0, message.length); + digest.doFinal(commitment, 0); + + return commitment; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/GOST3411Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/GOST3411Digest.java new file mode 100644 index 000000000..a540c99f2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/GOST3411Digest.java @@ -0,0 +1,362 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.crypto.engines.GOST28147Engine; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithSBox; +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Memoable; + +/** + * implementation of GOST R 34.11-94 + */ +public class GOST3411Digest + implements ExtendedDigest, Memoable +{ + private static final int DIGEST_LENGTH = 32; + + private byte[] H = new byte[32], L = new byte[32], + M = new byte[32], Sum = new byte[32]; + private byte[][] C = new byte[4][32]; + + private byte[] xBuf = new byte[32]; + private int xBufOff; + private long byteCount; + + private BlockCipher cipher = new GOST28147Engine(); + private byte[] sBox; + + /** + * Standard constructor + */ + public GOST3411Digest() + { + sBox = GOST28147Engine.getSBox("D-A"); + cipher.init(true, new ParametersWithSBox(null, sBox)); + + reset(); + } + + /** + * Constructor to allow use of a particular sbox with GOST28147 + * @see GOST28147Engine#getSBox(String) + */ + public GOST3411Digest(byte[] sBoxParam) + { + sBox = Arrays.clone(sBoxParam); + cipher.init(true, new ParametersWithSBox(null, sBox)); + + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public GOST3411Digest(GOST3411Digest t) + { + reset(t); + } + + public String getAlgorithmName() + { + return "GOST3411"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + public void update(byte in) + { + xBuf[xBufOff++] = in; + if (xBufOff == xBuf.length) + { + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + xBufOff = 0; + } + byteCount++; + } + + public void update(byte[] in, int inOff, int len) + { + while ((xBufOff != 0) && (len > 0)) + { + update(in[inOff]); + inOff++; + len--; + } + + while (len > xBuf.length) + { + System.arraycopy(in, inOff, xBuf, 0, xBuf.length); + + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + inOff += xBuf.length; + len -= xBuf.length; + byteCount += xBuf.length; + } + + // load in the remainder. + while (len > 0) + { + update(in[inOff]); + inOff++; + len--; + } + } + + // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8 + private byte[] K = new byte[32]; + + private byte[] P(byte[] in) + { + for(int k = 0; k < 8; k++) + { + K[4*k] = in[k]; + K[1 + 4*k] = in[ 8 + k]; + K[2 + 4*k] = in[16 + k]; + K[3 + 4*k] = in[24 + k]; + } + + return K; + } + + //A (x) = (x0 ^ x1) || x3 || x2 || x1 + byte[] a = new byte[8]; + private byte[] A(byte[] in) + { + for(int j=0; j<8; j++) + { + a[j]=(byte)(in[j] ^ in[j+8]); + } + + System.arraycopy(in, 8, in, 0, 24); + System.arraycopy(a, 0, in, 24, 8); + + return in; + } + + //Encrypt function, ECB mode + private void E(byte[] key, byte[] s, int sOff, byte[] in, int inOff) + { + cipher.init(true, new KeyParameter(key)); + + cipher.processBlock(in, inOff, s, sOff); + } + + // (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2 + short[] wS = new short[16], w_S = new short[16]; + + private void fw(byte[] in) + { + cpyBytesToShort(in, wS); + w_S[15] = (short)(wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15]); + System.arraycopy(wS, 1, w_S, 0, 15); + cpyShortToBytes(w_S, in); + } + + // block processing + byte[] S = new byte[32]; + byte[] U = new byte[32], V = new byte[32], W = new byte[32]; + + protected void processBlock(byte[] in, int inOff) + { + System.arraycopy(in, inOff, M, 0, 32); + + //key step 1 + + // H = h3 || h2 || h1 || h0 + // S = s3 || s2 || s1 || s0 + System.arraycopy(H, 0, U, 0, 32); + System.arraycopy(M, 0, V, 0, 32); + for (int j=0; j<32; j++) + { + W[j] = (byte)(U[j]^V[j]); + } + // Encrypt gost28147-ECB + E(P(W), S, 0, H, 0); // s0 = EK0 [h0] + + //keys step 2,3,4 + for (int i=1; i<4; i++) + { + byte[] tmpA = A(U); + for (int j=0; j<32; j++) + { + U[j] = (byte)(tmpA[j] ^ C[i][j]); + } + V = A(A(V)); + for (int j=0; j<32; j++) + { + W[j] = (byte)(U[j]^V[j]); + } + // Encrypt gost28147-ECB + E(P(W), S, i * 8, H, i * 8); // si = EKi [hi] + } + + // x(M, H) = y61(H^y(M^y12(S))) + for(int n = 0; n < 12; n++) + { + fw(S); + } + for(int n = 0; n < 32; n++) + { + S[n] = (byte)(S[n] ^ M[n]); + } + + fw(S); + + for(int n = 0; n < 32; n++) + { + S[n] = (byte)(H[n] ^ S[n]); + } + for(int n = 0; n < 61; n++) + { + fw(S); + } + System.arraycopy(S, 0, H, 0, H.length); + } + + private void finish() + { + Pack.longToLittleEndian(byteCount * 8, L, 0); // get length into L (byteCount * 8 = bitCount) + + while (xBufOff != 0) + { + update((byte)0); + } + + processBlock(L, 0); + processBlock(Sum, 0); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + System.arraycopy(H, 0, out, outOff, H.length); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + private static final byte[] C2 = { + 0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF, + (byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00, + 0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF,0x00,0x00,(byte)0xFF, + (byte)0xFF,0x00,0x00,0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF}; + + public void reset() + { + byteCount = 0; + xBufOff = 0; + + for(int i=0; i+ * NOTE: This algorithm is only included for backwards compatability + * with legacy applications, it's not secure, don't use it for anything new! + */ +public class MD4Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public MD4Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD4Digest(MD4Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(MD4Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "MD4"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static final int S11 = 3; + private static final int S12 = 7; + private static final int S13 = 11; + private static final int S14 = 19; + + // + // round 2 left rotates + // + private static final int S21 = 3; + private static final int S22 = 5; + private static final int S23 = 9; + private static final int S24 = 13; + + // + // round 3 left rotates + // + private static final int S31 = 3; + private static final int S32 = 9; + private static final int S33 = 11; + private static final int S34 = 15; + + /* + * rotate int x left n bits. + */ + private int rotateLeft( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * F, G, H and I are the basic MD4 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & v) | (u & w) | (v & w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + protected void processBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = rotateLeft(a + F(b, c, d) + X[ 0], S11); + d = rotateLeft(d + F(a, b, c) + X[ 1], S12); + c = rotateLeft(c + F(d, a, b) + X[ 2], S13); + b = rotateLeft(b + F(c, d, a) + X[ 3], S14); + a = rotateLeft(a + F(b, c, d) + X[ 4], S11); + d = rotateLeft(d + F(a, b, c) + X[ 5], S12); + c = rotateLeft(c + F(d, a, b) + X[ 6], S13); + b = rotateLeft(b + F(c, d, a) + X[ 7], S14); + a = rotateLeft(a + F(b, c, d) + X[ 8], S11); + d = rotateLeft(d + F(a, b, c) + X[ 9], S12); + c = rotateLeft(c + F(d, a, b) + X[10], S13); + b = rotateLeft(b + F(c, d, a) + X[11], S14); + a = rotateLeft(a + F(b, c, d) + X[12], S11); + d = rotateLeft(d + F(a, b, c) + X[13], S12); + c = rotateLeft(c + F(d, a, b) + X[14], S13); + b = rotateLeft(b + F(c, d, a) + X[15], S14); + + // + // Round 2 - G cycle, 16 times. + // + a = rotateLeft(a + G(b, c, d) + X[ 0] + 0x5a827999, S21); + d = rotateLeft(d + G(a, b, c) + X[ 4] + 0x5a827999, S22); + c = rotateLeft(c + G(d, a, b) + X[ 8] + 0x5a827999, S23); + b = rotateLeft(b + G(c, d, a) + X[12] + 0x5a827999, S24); + a = rotateLeft(a + G(b, c, d) + X[ 1] + 0x5a827999, S21); + d = rotateLeft(d + G(a, b, c) + X[ 5] + 0x5a827999, S22); + c = rotateLeft(c + G(d, a, b) + X[ 9] + 0x5a827999, S23); + b = rotateLeft(b + G(c, d, a) + X[13] + 0x5a827999, S24); + a = rotateLeft(a + G(b, c, d) + X[ 2] + 0x5a827999, S21); + d = rotateLeft(d + G(a, b, c) + X[ 6] + 0x5a827999, S22); + c = rotateLeft(c + G(d, a, b) + X[10] + 0x5a827999, S23); + b = rotateLeft(b + G(c, d, a) + X[14] + 0x5a827999, S24); + a = rotateLeft(a + G(b, c, d) + X[ 3] + 0x5a827999, S21); + d = rotateLeft(d + G(a, b, c) + X[ 7] + 0x5a827999, S22); + c = rotateLeft(c + G(d, a, b) + X[11] + 0x5a827999, S23); + b = rotateLeft(b + G(c, d, a) + X[15] + 0x5a827999, S24); + + // + // Round 3 - H cycle, 16 times. + // + a = rotateLeft(a + H(b, c, d) + X[ 0] + 0x6ed9eba1, S31); + d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x6ed9eba1, S32); + c = rotateLeft(c + H(d, a, b) + X[ 4] + 0x6ed9eba1, S33); + b = rotateLeft(b + H(c, d, a) + X[12] + 0x6ed9eba1, S34); + a = rotateLeft(a + H(b, c, d) + X[ 2] + 0x6ed9eba1, S31); + d = rotateLeft(d + H(a, b, c) + X[10] + 0x6ed9eba1, S32); + c = rotateLeft(c + H(d, a, b) + X[ 6] + 0x6ed9eba1, S33); + b = rotateLeft(b + H(c, d, a) + X[14] + 0x6ed9eba1, S34); + a = rotateLeft(a + H(b, c, d) + X[ 1] + 0x6ed9eba1, S31); + d = rotateLeft(d + H(a, b, c) + X[ 9] + 0x6ed9eba1, S32); + c = rotateLeft(c + H(d, a, b) + X[ 5] + 0x6ed9eba1, S33); + b = rotateLeft(b + H(c, d, a) + X[13] + 0x6ed9eba1, S34); + a = rotateLeft(a + H(b, c, d) + X[ 3] + 0x6ed9eba1, S31); + d = rotateLeft(d + H(a, b, c) + X[11] + 0x6ed9eba1, S32); + c = rotateLeft(c + H(d, a, b) + X[ 7] + 0x6ed9eba1, S33); + b = rotateLeft(b + H(c, d, a) + X[15] + 0x6ed9eba1, S34); + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new MD4Digest(this); + } + + public void reset(Memoable other) + { + MD4Digest d = (MD4Digest)other; + + copyIn(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/MD5Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/MD5Digest.java new file mode 100644 index 000000000..61c83494c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/MD5Digest.java @@ -0,0 +1,323 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; + +/** + * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. + */ +public class MD5Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public MD5Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD5Digest(MD5Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(MD5Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "MD5"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static final int S11 = 7; + private static final int S12 = 12; + private static final int S13 = 17; + private static final int S14 = 22; + + // + // round 2 left rotates + // + private static final int S21 = 5; + private static final int S22 = 9; + private static final int S23 = 14; + private static final int S24 = 20; + + // + // round 3 left rotates + // + private static final int S31 = 4; + private static final int S32 = 11; + private static final int S33 = 16; + private static final int S34 = 23; + + // + // round 4 left rotates + // + private static final int S41 = 6; + private static final int S42 = 10; + private static final int S43 = 15; + private static final int S44 = 21; + + /* + * rotate int x left n bits. + */ + private int rotateLeft( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * F, G, H and I are the basic MD5 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & w) | (v & ~w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + private int K( + int u, + int v, + int w) + { + return v ^ (u | ~w); + } + + protected void processBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = rotateLeft(a + F(b, c, d) + X[ 0] + 0xd76aa478, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 1] + 0xe8c7b756, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[ 2] + 0x242070db, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[ 3] + 0xc1bdceee, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[ 4] + 0xf57c0faf, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 5] + 0x4787c62a, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[ 6] + 0xa8304613, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[ 7] + 0xfd469501, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[ 8] + 0x698098d8, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 9] + 0x8b44f7af, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = rotateLeft(a + G(b, c, d) + X[ 1] + 0xf61e2562, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[ 6] + 0xc040b340, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 0] + 0xe9b6c7aa, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[ 5] + 0xd62f105d, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 4] + 0xe7d3fbc8, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[ 9] + 0x21e1cde6, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[ 3] + 0xf4d50d87, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 8] + 0x455a14ed, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[ 2] + 0xfcefa3f8, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[ 7] + 0x676f02d9, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = rotateLeft(a + H(b, c, d) + X[ 5] + 0xfffa3942, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x8771f681, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[ 1] + 0xa4beea44, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 4] + 0x4bdecfa9, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[ 7] + 0xf6bb4b60, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 0] + 0xeaa127fa, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[ 3] + 0xd4ef3085, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[ 6] + 0x04881d05, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[ 9] + 0xd9d4d039, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[ 2] + 0xc4ac5665, S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = rotateLeft(a + K(b, c, d) + X[ 0] + 0xf4292244, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[ 7] + 0x432aff97, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 5] + 0xfc93a039, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[ 3] + 0x8f0ccc92, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 1] + 0x85845dd1, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[ 8] + 0x6fa87e4f, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[ 6] + 0xa3014314, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[ 4] + 0xf7537e82, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[ 2] + 0x2ad7d2bb, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 9] + 0xeb86d391, S44) + c; + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new MD5Digest(this); + } + + public void reset(Memoable other) + { + MD5Digest d = (MD5Digest)other; + + copyIn(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/NonMemoableDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/NonMemoableDigest.java new file mode 100644 index 000000000..7ce89469c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/NonMemoableDigest.java @@ -0,0 +1,64 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; + +/** + * Wrapper removes exposure to the Memoable interface on an ExtendedDigest implementation. + */ +public class NonMemoableDigest + implements ExtendedDigest +{ + private ExtendedDigest baseDigest; + + /** + * Base constructor. + * + * @param baseDigest underlying digest to use. + * @exception IllegalArgumentException if baseDigest is null + */ + public NonMemoableDigest( + ExtendedDigest baseDigest) + { + if (baseDigest == null) + { + throw new IllegalArgumentException("baseDigest must not be null"); + } + + this.baseDigest = baseDigest; + } + + public String getAlgorithmName() + { + return baseDigest.getAlgorithmName(); + } + + public int getDigestSize() + { + return baseDigest.getDigestSize(); + } + + public void update(byte in) + { + baseDigest.update(in); + } + + public void update(byte[] in, int inOff, int len) + { + baseDigest.update(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + return baseDigest.doFinal(out, outOff); + } + + public void reset() + { + baseDigest.reset(); + } + + public int getByteLength() + { + return baseDigest.getByteLength(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/NullDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/NullDigest.java new file mode 100644 index 000000000..6b3c8106f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/NullDigest.java @@ -0,0 +1,48 @@ +package org.spongycastle.crypto.digests; + +import java.io.ByteArrayOutputStream; + +import org.spongycastle.crypto.Digest; + + +public class NullDigest + implements Digest +{ + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + public String getAlgorithmName() + { + return "NULL"; + } + + public int getDigestSize() + { + return bOut.size(); + } + + public void update(byte in) + { + bOut.write(in); + } + + public void update(byte[] in, int inOff, int len) + { + bOut.write(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + byte[] res = bOut.toByteArray(); + + System.arraycopy(res, 0, out, outOff, res.length); + + reset(); + + return res.length; + } + + public void reset() + { + bOut.reset(); + } +} \ No newline at end of file diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD128Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD128Digest.java new file mode 100644 index 000000000..26a8b702f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD128Digest.java @@ -0,0 +1,482 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; + +/** + * implementation of RIPEMD128 + */ +public class RIPEMD128Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 16; + + private int H0, H1, H2, H3; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RIPEMD128Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RIPEMD128Digest(RIPEMD128Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(RIPEMD128Digest t) + { + super.copyIn(t); + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "RIPEMD128"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4 are the basic RIPEMD128 functions. + */ + + /* + * F + */ + private int f1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * G + */ + private int f2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * H + */ + private int f3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * I + */ + private int f4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + private int F1( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f1(b, c, d) + x, s); + } + + private int F2( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f2(b, c, d) + x + 0x5a827999, s); + } + + private int F3( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f3(b, c, d) + x + 0x6ed9eba1, s); + } + + private int F4( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f4(b, c, d) + x + 0x8f1bbcdc, s); + } + + private int FF1( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f1(b, c, d) + x, s); + } + + private int FF2( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f2(b, c, d) + x + 0x6d703ef3, s); + } + + private int FF3( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f3(b, c, d) + x + 0x5c4dd124, s); + } + + private int FF4( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f4(b, c, d) + x + 0x50a28be6, s); + } + + protected void processBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + + // + // Round 1 + // + a = F1(a, b, c, d, X[ 0], 11); + d = F1(d, a, b, c, X[ 1], 14); + c = F1(c, d, a, b, X[ 2], 15); + b = F1(b, c, d, a, X[ 3], 12); + a = F1(a, b, c, d, X[ 4], 5); + d = F1(d, a, b, c, X[ 5], 8); + c = F1(c, d, a, b, X[ 6], 7); + b = F1(b, c, d, a, X[ 7], 9); + a = F1(a, b, c, d, X[ 8], 11); + d = F1(d, a, b, c, X[ 9], 13); + c = F1(c, d, a, b, X[10], 14); + b = F1(b, c, d, a, X[11], 15); + a = F1(a, b, c, d, X[12], 6); + d = F1(d, a, b, c, X[13], 7); + c = F1(c, d, a, b, X[14], 9); + b = F1(b, c, d, a, X[15], 8); + + // + // Round 2 + // + a = F2(a, b, c, d, X[ 7], 7); + d = F2(d, a, b, c, X[ 4], 6); + c = F2(c, d, a, b, X[13], 8); + b = F2(b, c, d, a, X[ 1], 13); + a = F2(a, b, c, d, X[10], 11); + d = F2(d, a, b, c, X[ 6], 9); + c = F2(c, d, a, b, X[15], 7); + b = F2(b, c, d, a, X[ 3], 15); + a = F2(a, b, c, d, X[12], 7); + d = F2(d, a, b, c, X[ 0], 12); + c = F2(c, d, a, b, X[ 9], 15); + b = F2(b, c, d, a, X[ 5], 9); + a = F2(a, b, c, d, X[ 2], 11); + d = F2(d, a, b, c, X[14], 7); + c = F2(c, d, a, b, X[11], 13); + b = F2(b, c, d, a, X[ 8], 12); + + // + // Round 3 + // + a = F3(a, b, c, d, X[ 3], 11); + d = F3(d, a, b, c, X[10], 13); + c = F3(c, d, a, b, X[14], 6); + b = F3(b, c, d, a, X[ 4], 7); + a = F3(a, b, c, d, X[ 9], 14); + d = F3(d, a, b, c, X[15], 9); + c = F3(c, d, a, b, X[ 8], 13); + b = F3(b, c, d, a, X[ 1], 15); + a = F3(a, b, c, d, X[ 2], 14); + d = F3(d, a, b, c, X[ 7], 8); + c = F3(c, d, a, b, X[ 0], 13); + b = F3(b, c, d, a, X[ 6], 6); + a = F3(a, b, c, d, X[13], 5); + d = F3(d, a, b, c, X[11], 12); + c = F3(c, d, a, b, X[ 5], 7); + b = F3(b, c, d, a, X[12], 5); + + // + // Round 4 + // + a = F4(a, b, c, d, X[ 1], 11); + d = F4(d, a, b, c, X[ 9], 12); + c = F4(c, d, a, b, X[11], 14); + b = F4(b, c, d, a, X[10], 15); + a = F4(a, b, c, d, X[ 0], 14); + d = F4(d, a, b, c, X[ 8], 15); + c = F4(c, d, a, b, X[12], 9); + b = F4(b, c, d, a, X[ 4], 8); + a = F4(a, b, c, d, X[13], 9); + d = F4(d, a, b, c, X[ 3], 14); + c = F4(c, d, a, b, X[ 7], 5); + b = F4(b, c, d, a, X[15], 6); + a = F4(a, b, c, d, X[14], 8); + d = F4(d, a, b, c, X[ 5], 6); + c = F4(c, d, a, b, X[ 6], 5); + b = F4(b, c, d, a, X[ 2], 12); + + // + // Parallel round 1 + // + aa = FF4(aa, bb, cc, dd, X[ 5], 8); + dd = FF4(dd, aa, bb, cc, X[14], 9); + cc = FF4(cc, dd, aa, bb, X[ 7], 9); + bb = FF4(bb, cc, dd, aa, X[ 0], 11); + aa = FF4(aa, bb, cc, dd, X[ 9], 13); + dd = FF4(dd, aa, bb, cc, X[ 2], 15); + cc = FF4(cc, dd, aa, bb, X[11], 15); + bb = FF4(bb, cc, dd, aa, X[ 4], 5); + aa = FF4(aa, bb, cc, dd, X[13], 7); + dd = FF4(dd, aa, bb, cc, X[ 6], 7); + cc = FF4(cc, dd, aa, bb, X[15], 8); + bb = FF4(bb, cc, dd, aa, X[ 8], 11); + aa = FF4(aa, bb, cc, dd, X[ 1], 14); + dd = FF4(dd, aa, bb, cc, X[10], 14); + cc = FF4(cc, dd, aa, bb, X[ 3], 12); + bb = FF4(bb, cc, dd, aa, X[12], 6); + + // + // Parallel round 2 + // + aa = FF3(aa, bb, cc, dd, X[ 6], 9); + dd = FF3(dd, aa, bb, cc, X[11], 13); + cc = FF3(cc, dd, aa, bb, X[ 3], 15); + bb = FF3(bb, cc, dd, aa, X[ 7], 7); + aa = FF3(aa, bb, cc, dd, X[ 0], 12); + dd = FF3(dd, aa, bb, cc, X[13], 8); + cc = FF3(cc, dd, aa, bb, X[ 5], 9); + bb = FF3(bb, cc, dd, aa, X[10], 11); + aa = FF3(aa, bb, cc, dd, X[14], 7); + dd = FF3(dd, aa, bb, cc, X[15], 7); + cc = FF3(cc, dd, aa, bb, X[ 8], 12); + bb = FF3(bb, cc, dd, aa, X[12], 7); + aa = FF3(aa, bb, cc, dd, X[ 4], 6); + dd = FF3(dd, aa, bb, cc, X[ 9], 15); + cc = FF3(cc, dd, aa, bb, X[ 1], 13); + bb = FF3(bb, cc, dd, aa, X[ 2], 11); + + // + // Parallel round 3 + // + aa = FF2(aa, bb, cc, dd, X[15], 9); + dd = FF2(dd, aa, bb, cc, X[ 5], 7); + cc = FF2(cc, dd, aa, bb, X[ 1], 15); + bb = FF2(bb, cc, dd, aa, X[ 3], 11); + aa = FF2(aa, bb, cc, dd, X[ 7], 8); + dd = FF2(dd, aa, bb, cc, X[14], 6); + cc = FF2(cc, dd, aa, bb, X[ 6], 6); + bb = FF2(bb, cc, dd, aa, X[ 9], 14); + aa = FF2(aa, bb, cc, dd, X[11], 12); + dd = FF2(dd, aa, bb, cc, X[ 8], 13); + cc = FF2(cc, dd, aa, bb, X[12], 5); + bb = FF2(bb, cc, dd, aa, X[ 2], 14); + aa = FF2(aa, bb, cc, dd, X[10], 13); + dd = FF2(dd, aa, bb, cc, X[ 0], 13); + cc = FF2(cc, dd, aa, bb, X[ 4], 7); + bb = FF2(bb, cc, dd, aa, X[13], 5); + + // + // Parallel round 4 + // + aa = FF1(aa, bb, cc, dd, X[ 8], 15); + dd = FF1(dd, aa, bb, cc, X[ 6], 5); + cc = FF1(cc, dd, aa, bb, X[ 4], 8); + bb = FF1(bb, cc, dd, aa, X[ 1], 11); + aa = FF1(aa, bb, cc, dd, X[ 3], 14); + dd = FF1(dd, aa, bb, cc, X[11], 14); + cc = FF1(cc, dd, aa, bb, X[15], 6); + bb = FF1(bb, cc, dd, aa, X[ 0], 14); + aa = FF1(aa, bb, cc, dd, X[ 5], 6); + dd = FF1(dd, aa, bb, cc, X[12], 9); + cc = FF1(cc, dd, aa, bb, X[ 2], 12); + bb = FF1(bb, cc, dd, aa, X[13], 9); + aa = FF1(aa, bb, cc, dd, X[ 9], 12); + dd = FF1(dd, aa, bb, cc, X[ 7], 5); + cc = FF1(cc, dd, aa, bb, X[10], 15); + bb = FF1(bb, cc, dd, aa, X[14], 8); + + dd += c + H1; // final result for H0 + + // + // combine the results + // + H1 = H2 + d + aa; + H2 = H3 + a + bb; + H3 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new RIPEMD128Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD128Digest d = (RIPEMD128Digest)other; + + copyIn(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD160Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD160Digest.java new file mode 100644 index 000000000..c59ba7f7c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD160Digest.java @@ -0,0 +1,443 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; + +/** + * implementation of RIPEMD see, + * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html + */ +public class RIPEMD160Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H0, H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RIPEMD160Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RIPEMD160Digest(RIPEMD160Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(RIPEMD160Digest t) + { + super.copyIn(t); + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "RIPEMD160"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + unpackWord(H4, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic RIPEMD160 functions. + */ + + /* + * rounds 0-15 + */ + private int f1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int f2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * rounds 32-47 + */ + private int f3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * rounds 48-63 + */ + private int f4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + /* + * rounds 64-79 + */ + private int f5( + int x, + int y, + int z) + { + return x ^ (y | ~z); + } + + protected void processBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + e = ee = H4; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + f1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[10], 14) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[11], 15) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[12], 6) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[13], 7) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[14], 9) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + f5(bb,cc,dd) + X[ 5] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[14] + 0x50a28be6, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 7] + 0x50a28be6, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[ 0] + 0x50a28be6, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 9] + 0x50a28be6, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[ 2] + 0x50a28be6, 15) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[11] + 0x50a28be6, 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 4] + 0x50a28be6, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[13] + 0x50a28be6, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 6] + 0x50a28be6, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[15] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[ 8] + 0x50a28be6, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 1] + 0x50a28be6, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[10] + 0x50a28be6, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 3] + 0x50a28be6, 12) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[12] + 0x50a28be6, 6) + ee; cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + f2(a,b,c) + X[ 7] + 0x5a827999, 7) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 4] + 0x5a827999, 6) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[13] + 0x5a827999, 8) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[ 1] + 0x5a827999, 13) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[10] + 0x5a827999, 11) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 6] + 0x5a827999, 9) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[15] + 0x5a827999, 7) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 3] + 0x5a827999, 15) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[12] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[ 0] + 0x5a827999, 12) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 9] + 0x5a827999, 15) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 5] + 0x5a827999, 9) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 2] + 0x5a827999, 11) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[14] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[11] + 0x5a827999, 13) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 8] + 0x5a827999, 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + f4(aa,bb,cc) + X[ 6] + 0x5c4dd124, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[11] + 0x5c4dd124, 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 3] + 0x5c4dd124, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 7] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 0] + 0x5c4dd124, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[13] + 0x5c4dd124, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[ 5] + 0x5c4dd124, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[10] + 0x5c4dd124, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[14] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[15] + 0x5c4dd124, 7) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 8] + 0x5c4dd124, 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[12] + 0x5c4dd124, 7) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 4] + 0x5c4dd124, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 9] + 0x5c4dd124, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 1] + 0x5c4dd124, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 2] + 0x5c4dd124, 11) + dd; bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + f3(e,a,b) + X[ 3] + 0x6ed9eba1, 11) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[10] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[14] + 0x6ed9eba1, 6) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 4] + 0x6ed9eba1, 7) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 9] + 0x6ed9eba1, 14) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[15] + 0x6ed9eba1, 9) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 8] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[ 1] + 0x6ed9eba1, 15) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 2] + 0x6ed9eba1, 14) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 7] + 0x6ed9eba1, 8) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[ 0] + 0x6ed9eba1, 13) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 6] + 0x6ed9eba1, 6) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[13] + 0x6ed9eba1, 5) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[11] + 0x6ed9eba1, 12) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 5] + 0x6ed9eba1, 7) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[12] + 0x6ed9eba1, 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + f3(ee,aa,bb) + X[15] + 0x6d703ef3, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 5] + 0x6d703ef3, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 1] + 0x6d703ef3, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 3] + 0x6d703ef3, 11) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 7] + 0x6d703ef3, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[14] + 0x6d703ef3, 6) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 6] + 0x6d703ef3, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 9] + 0x6d703ef3, 14) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[11] + 0x6d703ef3, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 8] + 0x6d703ef3, 13) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[12] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 2] + 0x6d703ef3, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[10] + 0x6d703ef3, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 0] + 0x6d703ef3, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 4] + 0x6d703ef3, 7) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[13] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + f4(d,e,a) + X[ 1] + 0x8f1bbcdc, 11) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[ 9] + 0x8f1bbcdc, 12) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[11] + 0x8f1bbcdc, 14) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[10] + 0x8f1bbcdc, 15) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 0] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 8] + 0x8f1bbcdc, 15) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[12] + 0x8f1bbcdc, 9) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[ 4] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[13] + 0x8f1bbcdc, 9) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 3] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 7] + 0x8f1bbcdc, 5) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[15] + 0x8f1bbcdc, 6) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[14] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[ 5] + 0x8f1bbcdc, 6) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 6] + 0x8f1bbcdc, 5) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 2] + 0x8f1bbcdc, 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + f2(dd,ee,aa) + X[ 8] + 0x7a6d76e9, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[ 6] + 0x7a6d76e9, 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 4] + 0x7a6d76e9, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 1] + 0x7a6d76e9, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[ 3] + 0x7a6d76e9, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[11] + 0x7a6d76e9, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[15] + 0x7a6d76e9, 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 0] + 0x7a6d76e9, 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 5] + 0x7a6d76e9, 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[12] + 0x7a6d76e9, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[ 2] + 0x7a6d76e9, 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[13] + 0x7a6d76e9, 9) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 9] + 0x7a6d76e9, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 7] + 0x7a6d76e9, 5) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[10] + 0x7a6d76e9, 15) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[14] + 0x7a6d76e9, 8) + bb; ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + f5(c,d,e) + X[ 4] + 0xa953fd4e, 9) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 0] + 0xa953fd4e, 15) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[ 5] + 0xa953fd4e, 5) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 9] + 0xa953fd4e, 11) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 7] + 0xa953fd4e, 6) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[12] + 0xa953fd4e, 8) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 2] + 0xa953fd4e, 13) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[10] + 0xa953fd4e, 12) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[14] + 0xa953fd4e, 5) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 1] + 0xa953fd4e, 12) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[ 3] + 0xa953fd4e, 13) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 8] + 0xa953fd4e, 14) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[11] + 0xa953fd4e, 11) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 6] + 0xa953fd4e, 8) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[15] + 0xa953fd4e, 5) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[13] + 0xa953fd4e, 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + f1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10); + + dd += c + H1; + H1 = H2 + d + ee; + H2 = H3 + e + aa; + H3 = H4 + a + bb; + H4 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new RIPEMD160Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD160Digest d = (RIPEMD160Digest)other; + + copyIn(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD256Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD256Digest.java new file mode 100644 index 000000000..45d2bca85 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD256Digest.java @@ -0,0 +1,497 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; + +/** + * implementation of RIPEMD256. + *
+ * note: this algorithm offers the same level of security as RIPEMD128. + */ +public class RIPEMD256Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 32; + + private int H0, H1, H2, H3, H4, H5, H6, H7; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RIPEMD256Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RIPEMD256Digest(RIPEMD256Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(RIPEMD256Digest t) + { + super.copyIn(t); + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "RIPEMD256"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + unpackWord(H4, out, outOff + 16); + unpackWord(H5, out, outOff + 20); + unpackWord(H6, out, outOff + 24); + unpackWord(H7, out, outOff + 28); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0x76543210; + H5 = 0xFEDCBA98; + H6 = 0x89ABCDEF; + H7 = 0x01234567; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4 are the basic RIPEMD128 functions. + */ + + /* + * F + */ + private int f1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * G + */ + private int f2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * H + */ + private int f3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * I + */ + private int f4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + private int F1( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f1(b, c, d) + x, s); + } + + private int F2( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f2(b, c, d) + x + 0x5a827999, s); + } + + private int F3( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f3(b, c, d) + x + 0x6ed9eba1, s); + } + + private int F4( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f4(b, c, d) + x + 0x8f1bbcdc, s); + } + + private int FF1( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f1(b, c, d) + x, s); + } + + private int FF2( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f2(b, c, d) + x + 0x6d703ef3, s); + } + + private int FF3( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f3(b, c, d) + x + 0x5c4dd124, s); + } + + private int FF4( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + f4(b, c, d) + x + 0x50a28be6, s); + } + + protected void processBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int t; + + a = H0; + b = H1; + c = H2; + d = H3; + aa = H4; + bb = H5; + cc = H6; + dd = H7; + + // + // Round 1 + // + + a = F1(a, b, c, d, X[ 0], 11); + d = F1(d, a, b, c, X[ 1], 14); + c = F1(c, d, a, b, X[ 2], 15); + b = F1(b, c, d, a, X[ 3], 12); + a = F1(a, b, c, d, X[ 4], 5); + d = F1(d, a, b, c, X[ 5], 8); + c = F1(c, d, a, b, X[ 6], 7); + b = F1(b, c, d, a, X[ 7], 9); + a = F1(a, b, c, d, X[ 8], 11); + d = F1(d, a, b, c, X[ 9], 13); + c = F1(c, d, a, b, X[10], 14); + b = F1(b, c, d, a, X[11], 15); + a = F1(a, b, c, d, X[12], 6); + d = F1(d, a, b, c, X[13], 7); + c = F1(c, d, a, b, X[14], 9); + b = F1(b, c, d, a, X[15], 8); + + aa = FF4(aa, bb, cc, dd, X[ 5], 8); + dd = FF4(dd, aa, bb, cc, X[14], 9); + cc = FF4(cc, dd, aa, bb, X[ 7], 9); + bb = FF4(bb, cc, dd, aa, X[ 0], 11); + aa = FF4(aa, bb, cc, dd, X[ 9], 13); + dd = FF4(dd, aa, bb, cc, X[ 2], 15); + cc = FF4(cc, dd, aa, bb, X[11], 15); + bb = FF4(bb, cc, dd, aa, X[ 4], 5); + aa = FF4(aa, bb, cc, dd, X[13], 7); + dd = FF4(dd, aa, bb, cc, X[ 6], 7); + cc = FF4(cc, dd, aa, bb, X[15], 8); + bb = FF4(bb, cc, dd, aa, X[ 8], 11); + aa = FF4(aa, bb, cc, dd, X[ 1], 14); + dd = FF4(dd, aa, bb, cc, X[10], 14); + cc = FF4(cc, dd, aa, bb, X[ 3], 12); + bb = FF4(bb, cc, dd, aa, X[12], 6); + + t = a; a = aa; aa = t; + + // + // Round 2 + // + a = F2(a, b, c, d, X[ 7], 7); + d = F2(d, a, b, c, X[ 4], 6); + c = F2(c, d, a, b, X[13], 8); + b = F2(b, c, d, a, X[ 1], 13); + a = F2(a, b, c, d, X[10], 11); + d = F2(d, a, b, c, X[ 6], 9); + c = F2(c, d, a, b, X[15], 7); + b = F2(b, c, d, a, X[ 3], 15); + a = F2(a, b, c, d, X[12], 7); + d = F2(d, a, b, c, X[ 0], 12); + c = F2(c, d, a, b, X[ 9], 15); + b = F2(b, c, d, a, X[ 5], 9); + a = F2(a, b, c, d, X[ 2], 11); + d = F2(d, a, b, c, X[14], 7); + c = F2(c, d, a, b, X[11], 13); + b = F2(b, c, d, a, X[ 8], 12); + + aa = FF3(aa, bb, cc, dd, X[ 6], 9); + dd = FF3(dd, aa, bb, cc, X[ 11], 13); + cc = FF3(cc, dd, aa, bb, X[3], 15); + bb = FF3(bb, cc, dd, aa, X[ 7], 7); + aa = FF3(aa, bb, cc, dd, X[0], 12); + dd = FF3(dd, aa, bb, cc, X[13], 8); + cc = FF3(cc, dd, aa, bb, X[5], 9); + bb = FF3(bb, cc, dd, aa, X[10], 11); + aa = FF3(aa, bb, cc, dd, X[14], 7); + dd = FF3(dd, aa, bb, cc, X[15], 7); + cc = FF3(cc, dd, aa, bb, X[ 8], 12); + bb = FF3(bb, cc, dd, aa, X[12], 7); + aa = FF3(aa, bb, cc, dd, X[ 4], 6); + dd = FF3(dd, aa, bb, cc, X[ 9], 15); + cc = FF3(cc, dd, aa, bb, X[ 1], 13); + bb = FF3(bb, cc, dd, aa, X[ 2], 11); + + t = b; b = bb; bb = t; + + // + // Round 3 + // + a = F3(a, b, c, d, X[ 3], 11); + d = F3(d, a, b, c, X[10], 13); + c = F3(c, d, a, b, X[14], 6); + b = F3(b, c, d, a, X[ 4], 7); + a = F3(a, b, c, d, X[ 9], 14); + d = F3(d, a, b, c, X[15], 9); + c = F3(c, d, a, b, X[ 8], 13); + b = F3(b, c, d, a, X[ 1], 15); + a = F3(a, b, c, d, X[ 2], 14); + d = F3(d, a, b, c, X[ 7], 8); + c = F3(c, d, a, b, X[ 0], 13); + b = F3(b, c, d, a, X[ 6], 6); + a = F3(a, b, c, d, X[13], 5); + d = F3(d, a, b, c, X[11], 12); + c = F3(c, d, a, b, X[ 5], 7); + b = F3(b, c, d, a, X[12], 5); + + aa = FF2(aa, bb, cc, dd, X[ 15], 9); + dd = FF2(dd, aa, bb, cc, X[5], 7); + cc = FF2(cc, dd, aa, bb, X[1], 15); + bb = FF2(bb, cc, dd, aa, X[ 3], 11); + aa = FF2(aa, bb, cc, dd, X[ 7], 8); + dd = FF2(dd, aa, bb, cc, X[14], 6); + cc = FF2(cc, dd, aa, bb, X[ 6], 6); + bb = FF2(bb, cc, dd, aa, X[ 9], 14); + aa = FF2(aa, bb, cc, dd, X[11], 12); + dd = FF2(dd, aa, bb, cc, X[ 8], 13); + cc = FF2(cc, dd, aa, bb, X[12], 5); + bb = FF2(bb, cc, dd, aa, X[ 2], 14); + aa = FF2(aa, bb, cc, dd, X[10], 13); + dd = FF2(dd, aa, bb, cc, X[ 0], 13); + cc = FF2(cc, dd, aa, bb, X[ 4], 7); + bb = FF2(bb, cc, dd, aa, X[13], 5); + + t = c; c = cc; cc = t; + + // + // Round 4 + // + a = F4(a, b, c, d, X[ 1], 11); + d = F4(d, a, b, c, X[ 9], 12); + c = F4(c, d, a, b, X[11], 14); + b = F4(b, c, d, a, X[10], 15); + a = F4(a, b, c, d, X[ 0], 14); + d = F4(d, a, b, c, X[ 8], 15); + c = F4(c, d, a, b, X[12], 9); + b = F4(b, c, d, a, X[ 4], 8); + a = F4(a, b, c, d, X[13], 9); + d = F4(d, a, b, c, X[ 3], 14); + c = F4(c, d, a, b, X[ 7], 5); + b = F4(b, c, d, a, X[15], 6); + a = F4(a, b, c, d, X[14], 8); + d = F4(d, a, b, c, X[ 5], 6); + c = F4(c, d, a, b, X[ 6], 5); + b = F4(b, c, d, a, X[ 2], 12); + + aa = FF1(aa, bb, cc, dd, X[ 8], 15); + dd = FF1(dd, aa, bb, cc, X[ 6], 5); + cc = FF1(cc, dd, aa, bb, X[ 4], 8); + bb = FF1(bb, cc, dd, aa, X[ 1], 11); + aa = FF1(aa, bb, cc, dd, X[ 3], 14); + dd = FF1(dd, aa, bb, cc, X[11], 14); + cc = FF1(cc, dd, aa, bb, X[15], 6); + bb = FF1(bb, cc, dd, aa, X[ 0], 14); + aa = FF1(aa, bb, cc, dd, X[ 5], 6); + dd = FF1(dd, aa, bb, cc, X[12], 9); + cc = FF1(cc, dd, aa, bb, X[ 2], 12); + bb = FF1(bb, cc, dd, aa, X[13], 9); + aa = FF1(aa, bb, cc, dd, X[ 9], 12); + dd = FF1(dd, aa, bb, cc, X[ 7], 5); + cc = FF1(cc, dd, aa, bb, X[10], 15); + bb = FF1(bb, cc, dd, aa, X[14], 8); + + t = d; d = dd; dd = t; + + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += aa; + H5 += bb; + H6 += cc; + H7 += dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new RIPEMD256Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD256Digest d = (RIPEMD256Digest)other; + + copyIn(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD320Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD320Digest.java new file mode 100644 index 000000000..e3d7286c7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/RIPEMD320Digest.java @@ -0,0 +1,481 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; + +/** + * implementation of RIPEMD 320. + *
+ * Note: this implementation offers the same level of security + * as RIPEMD 160. + */ +public class RIPEMD320Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 40; + + private int H0, H1, H2, H3, H4, H5, H6, H7, H8, H9; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RIPEMD320Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RIPEMD320Digest(RIPEMD320Digest t) + { + super(t); + + doCopy(t); + } + + private void doCopy(RIPEMD320Digest t) + { + super.copyIn(t); + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + H9 = t.H9; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "RIPEMD320"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + unpackWord(H4, out, outOff + 16); + unpackWord(H5, out, outOff + 20); + unpackWord(H6, out, outOff + 24); + unpackWord(H7, out, outOff + 28); + unpackWord(H8, out, outOff + 32); + unpackWord(H9, out, outOff + 36); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + H5 = 0x76543210; + H6 = 0xFEDCBA98; + H7 = 0x89ABCDEF; + H8 = 0x01234567; + H9 = 0x3C2D1E0F; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic RIPEMD160 functions. + */ + + /* + * rounds 0-15 + */ + private int f1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int f2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * rounds 32-47 + */ + private int f3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * rounds 48-63 + */ + private int f4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + /* + * rounds 64-79 + */ + private int f5( + int x, + int y, + int z) + { + return x ^ (y | ~z); + } + + protected void processBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + int t; + + a = H0; + b = H1; + c = H2; + d = H3; + e = H4; + aa = H5; + bb = H6; + cc = H7; + dd = H8; + ee = H9; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + f1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[10], 14) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[11], 15) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[12], 6) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[13], 7) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[14], 9) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + f5(bb,cc,dd) + X[ 5] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[14] + 0x50a28be6, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 7] + 0x50a28be6, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[ 0] + 0x50a28be6, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 9] + 0x50a28be6, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[ 2] + 0x50a28be6, 15) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[11] + 0x50a28be6, 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 4] + 0x50a28be6, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[13] + 0x50a28be6, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 6] + 0x50a28be6, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[15] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[ 8] + 0x50a28be6, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 1] + 0x50a28be6, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[10] + 0x50a28be6, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 3] + 0x50a28be6, 12) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[12] + 0x50a28be6, 6) + ee; cc = RL(cc, 10); + + t = a; a = aa; aa = t; + + // + // Rounds 16-31 + // + // left + e = RL(e + f2(a,b,c) + X[ 7] + 0x5a827999, 7) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 4] + 0x5a827999, 6) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[13] + 0x5a827999, 8) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[ 1] + 0x5a827999, 13) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[10] + 0x5a827999, 11) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 6] + 0x5a827999, 9) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[15] + 0x5a827999, 7) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 3] + 0x5a827999, 15) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[12] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[ 0] + 0x5a827999, 12) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 9] + 0x5a827999, 15) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 5] + 0x5a827999, 9) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 2] + 0x5a827999, 11) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[14] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[11] + 0x5a827999, 13) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 8] + 0x5a827999, 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + f4(aa,bb,cc) + X[ 6] + 0x5c4dd124, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[11] + 0x5c4dd124, 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 3] + 0x5c4dd124, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 7] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 0] + 0x5c4dd124, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[13] + 0x5c4dd124, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[ 5] + 0x5c4dd124, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[10] + 0x5c4dd124, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[14] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[15] + 0x5c4dd124, 7) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 8] + 0x5c4dd124, 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[12] + 0x5c4dd124, 7) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 4] + 0x5c4dd124, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 9] + 0x5c4dd124, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 1] + 0x5c4dd124, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 2] + 0x5c4dd124, 11) + dd; bb = RL(bb, 10); + + t = b; b = bb; bb = t; + + // + // Rounds 32-47 + // + // left + d = RL(d + f3(e,a,b) + X[ 3] + 0x6ed9eba1, 11) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[10] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[14] + 0x6ed9eba1, 6) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 4] + 0x6ed9eba1, 7) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 9] + 0x6ed9eba1, 14) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[15] + 0x6ed9eba1, 9) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 8] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[ 1] + 0x6ed9eba1, 15) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 2] + 0x6ed9eba1, 14) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 7] + 0x6ed9eba1, 8) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[ 0] + 0x6ed9eba1, 13) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 6] + 0x6ed9eba1, 6) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[13] + 0x6ed9eba1, 5) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[11] + 0x6ed9eba1, 12) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 5] + 0x6ed9eba1, 7) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[12] + 0x6ed9eba1, 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + f3(ee,aa,bb) + X[15] + 0x6d703ef3, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 5] + 0x6d703ef3, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 1] + 0x6d703ef3, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 3] + 0x6d703ef3, 11) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 7] + 0x6d703ef3, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[14] + 0x6d703ef3, 6) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 6] + 0x6d703ef3, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 9] + 0x6d703ef3, 14) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[11] + 0x6d703ef3, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 8] + 0x6d703ef3, 13) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[12] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 2] + 0x6d703ef3, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[10] + 0x6d703ef3, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 0] + 0x6d703ef3, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 4] + 0x6d703ef3, 7) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[13] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + + t = c; c = cc; cc = t; + + // + // Rounds 48-63 + // + // left + c = RL(c + f4(d,e,a) + X[ 1] + 0x8f1bbcdc, 11) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[ 9] + 0x8f1bbcdc, 12) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[11] + 0x8f1bbcdc, 14) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[10] + 0x8f1bbcdc, 15) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 0] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 8] + 0x8f1bbcdc, 15) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[12] + 0x8f1bbcdc, 9) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[ 4] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[13] + 0x8f1bbcdc, 9) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 3] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 7] + 0x8f1bbcdc, 5) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[15] + 0x8f1bbcdc, 6) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[14] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[ 5] + 0x8f1bbcdc, 6) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 6] + 0x8f1bbcdc, 5) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 2] + 0x8f1bbcdc, 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + f2(dd,ee,aa) + X[ 8] + 0x7a6d76e9, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[ 6] + 0x7a6d76e9, 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 4] + 0x7a6d76e9, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 1] + 0x7a6d76e9, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[ 3] + 0x7a6d76e9, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[11] + 0x7a6d76e9, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[15] + 0x7a6d76e9, 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 0] + 0x7a6d76e9, 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 5] + 0x7a6d76e9, 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[12] + 0x7a6d76e9, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[ 2] + 0x7a6d76e9, 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[13] + 0x7a6d76e9, 9) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 9] + 0x7a6d76e9, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 7] + 0x7a6d76e9, 5) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[10] + 0x7a6d76e9, 15) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[14] + 0x7a6d76e9, 8) + bb; ee = RL(ee, 10); + + t = d; d = dd; dd = t; + + // + // Rounds 64-79 + // + // left + b = RL(b + f5(c,d,e) + X[ 4] + 0xa953fd4e, 9) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 0] + 0xa953fd4e, 15) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[ 5] + 0xa953fd4e, 5) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 9] + 0xa953fd4e, 11) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 7] + 0xa953fd4e, 6) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[12] + 0xa953fd4e, 8) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 2] + 0xa953fd4e, 13) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[10] + 0xa953fd4e, 12) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[14] + 0xa953fd4e, 5) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 1] + 0xa953fd4e, 12) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[ 3] + 0xa953fd4e, 13) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 8] + 0xa953fd4e, 14) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[11] + 0xa953fd4e, 11) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 6] + 0xa953fd4e, 8) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[15] + 0xa953fd4e, 5) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[13] + 0xa953fd4e, 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + f1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10); + + // + // do (e, ee) swap as part of assignment. + // + + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += ee; + H5 += aa; + H6 += bb; + H7 += cc; + H8 += dd; + H9 += e; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new RIPEMD320Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD320Digest d = (RIPEMD320Digest)other; + + doCopy(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA1Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA1Digest.java new file mode 100644 index 000000000..cc63dcdfa --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA1Digest.java @@ -0,0 +1,309 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Memoable; + +/** + * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349. + * + * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5 + * is the "endianness" of the word processing! + */ +public class SHA1Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H1, H2, H3, H4, H5; + + private int[] X = new int[80]; + private int xOff; + + /** + * Standard constructor + */ + public SHA1Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA1Digest(SHA1Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(SHA1Digest t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-1"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[ inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // Additive constants + // + private static final int Y1 = 0x5a827999; + private static final int Y2 = 0x6ed9eba1; + private static final int Y3 = 0x8f1bbcdc; + private static final int Y4 = 0xca62c1d6; + + private int f( + int u, + int v, + int w) + { + return ((u & v) | ((~u) & w)); + } + + private int h( + int u, + int v, + int w) + { + return (u ^ v ^ w); + } + + private int g( + int u, + int v, + int w) + { + return ((u & v) | (u & w) | (v & w)); + } + + protected void processBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) + { + int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]; + X[i] = t << 1 | t >>> 31; + } + + // + // set up working variables. + // + int A = H1; + int B = H2; + int C = H3; + int D = H4; + int E = H5; + + // + // round 1 + // + int idx = 0; + + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1; + C = C << 30 | C >>> 2; + } + + // + // round 2 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2; + C = C << 30 | C >>> 2; + } + + // + // round 3 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3; + C = C << 30 | C >>> 2; + } + + // + // round 4 + // + for (int j = 0; j <= 3; j++) + { + // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4; + C = C << 30 | C >>> 2; + } + + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset start of the buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new SHA1Digest(this); + } + + public void reset(Memoable other) + { + SHA1Digest d = (SHA1Digest)other; + + super.copyIn(d); + copyIn(d); + } +} + + + + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA224Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA224Digest.java new file mode 100644 index 000000000..4a0063b42 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA224Digest.java @@ -0,0 +1,311 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Memoable; + + +/** + * SHA-224 as described in RFC 3874 + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-224 512 32 224 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ +public class SHA224Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 28; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public SHA224Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA224Digest(SHA224Digest t) + { + super(t); + + doCopy(t); + } + + private void doCopy(SHA224Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-224"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[ inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + Pack.intToBigEndian(H6, out, outOff + 20); + Pack.intToBigEndian(H7, out, outOff + 24); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-224 initial hash value + */ + + H1 = 0xc1059ed8; + H2 = 0x367cd507; + H3 = 0x3070dd17; + H4 = 0xf70e5939; + H5 = 0xffc00b31; + H6 = 0x68581511; + H7 = 0x64f98fa7; + H8 = 0xbefa4fa4; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + protected void processBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int t = 16; t <= 63; t++) + { + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0(a) + Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0(h) + Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0(g) + Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0(f) + Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0(e) + Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0(d) + Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0(c) + Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0(b) + Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + /* SHA-224 functions */ + private int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private int Sum0( + int x) + { + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10)); + } + + private int Sum1( + int x) + { + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7)); + } + + private int Theta0( + int x) + { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + private int Theta1( + int x) + { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); + } + + /* SHA-224 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + public Memoable copy() + { + return new SHA224Digest(this); + } + + public void reset(Memoable other) + { + SHA224Digest d = (SHA224Digest)other; + + doCopy(d); + } +} + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA256Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA256Digest.java new file mode 100644 index 000000000..f14c50ae6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA256Digest.java @@ -0,0 +1,314 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Memoable; + + +/** + * FIPS 180-2 implementation of SHA-256. + * + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ +public class SHA256Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 32; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public SHA256Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA256Digest(SHA256Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(SHA256Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-256"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + Pack.intToBigEndian(H6, out, outOff + 20); + Pack.intToBigEndian(H7, out, outOff + 24); + Pack.intToBigEndian(H8, out, outOff + 28); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-256 initial hash value + * The first 32 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + + H1 = 0x6a09e667; + H2 = 0xbb67ae85; + H3 = 0x3c6ef372; + H4 = 0xa54ff53a; + H5 = 0x510e527f; + H6 = 0x9b05688c; + H7 = 0x1f83d9ab; + H8 = 0x5be0cd19; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + protected void processBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int t = 16; t <= 63; t++) + { + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0(a) + Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0(h) + Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0(g) + Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0(f) + Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0(e) + Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0(d) + Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0(c) + Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0(b) + Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + /* SHA-256 functions */ + private int Ch( + int x, + int y, + int z) + { + return (x & y) ^ ((~x) & z); + } + + private int Maj( + int x, + int y, + int z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + private int Sum0( + int x) + { + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10)); + } + + private int Sum1( + int x) + { + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7)); + } + + private int Theta0( + int x) + { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + private int Theta1( + int x) + { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); + } + + /* SHA-256 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + public Memoable copy() + { + return new SHA256Digest(this); + } + + public void reset(Memoable other) + { + SHA256Digest d = (SHA256Digest)other; + + copyIn(d); + } +} + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA384Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA384Digest.java new file mode 100644 index 000000000..f6a5ba0a8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA384Digest.java @@ -0,0 +1,99 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Memoable; + + +/** + * FIPS 180-2 implementation of SHA-384. + * + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ +public class SHA384Digest + extends LongDigest +{ + private static final int DIGEST_LENGTH = 48; + + /** + * Standard constructor + */ + public SHA384Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA384Digest(SHA384Digest t) + { + super(t); + } + + public String getAlgorithmName() + { + return "SHA-384"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.longToBigEndian(H1, out, outOff); + Pack.longToBigEndian(H2, out, outOff + 8); + Pack.longToBigEndian(H3, out, outOff + 16); + Pack.longToBigEndian(H4, out, outOff + 24); + Pack.longToBigEndian(H5, out, outOff + 32); + Pack.longToBigEndian(H6, out, outOff + 40); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-384 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the 9th through 16th prime numbers + */ + H1 = 0xcbbb9d5dc1059ed8l; + H2 = 0x629a292a367cd507l; + H3 = 0x9159015a3070dd17l; + H4 = 0x152fecd8f70e5939l; + H5 = 0x67332667ffc00b31l; + H6 = 0x8eb44a8768581511l; + H7 = 0xdb0c2e0d64f98fa7l; + H8 = 0x47b5481dbefa4fa4l; + } + + public Memoable copy() + { + return new SHA384Digest(this); + } + + public void reset(Memoable other) + { + SHA384Digest d = (SHA384Digest)other; + + super.copyIn(d); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA3Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA3Digest.java new file mode 100644 index 000000000..3416b7139 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA3Digest.java @@ -0,0 +1,547 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.util.Arrays; + +/** + * implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + * + * Following the naming conventions used in the C source code to enable easy review of the implementation. + */ +public class SHA3Digest + 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; + } + + private byte[] state = new byte[(1600 / 8)]; + private byte[] dataQueue = new byte[(1536 / 8)]; + private int rate; + private int bitsInQueue; + private int fixedOutputLength; + private boolean squeezing; + private int bitsAvailableForSqueezing; + private byte[] chunk; + private byte[] oneByte; + + private void clearDataQueueSection(int off, int len) + { + for (int i = off; i != off + len; i++) + { + dataQueue[i] = 0; + } + } + + public SHA3Digest() + { + init(0); + } + + public SHA3Digest(int bitLength) + { + init(bitLength); + } + + public SHA3Digest(SHA3Digest 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 "SHA3-" + fixedOutputLength; + } + + public int getDigestSize() + { + return fixedOutputLength / 8; + } + + public void update(byte in) + { + oneByte[0] = in; + + doUpdate(oneByte, 0, 8L); + } + + public void update(byte[] in, int inOff, int len) + { + doUpdate(in, inOff, len * 8L); + } + + public int doFinal(byte[] out, int outOff) + { + 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 0: + case 288: + initSponge(1024, 576); + 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 224, 256, 384, or 512."); + } + } + + private void doUpdate(byte[] data, int off, long databitlen) + { + if ((databitlen % 8) == 0) + { + absorb(data, off, databitlen); + } + else + { + absorb(data, off, databitlen - (databitlen % 8)); + + byte[] lastByte = new byte[1]; + + lastByte[0] = (byte)(data[off + (int)(databitlen / 8)] >> (8 - (databitlen % 8))); + absorb(lastByte, off, databitlen % 8); + } + } + + 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; + this.fixedOutputLength = 0; + 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; + } + + private 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; + } + + private 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/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA512Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA512Digest.java new file mode 100644 index 000000000..2fd3711d5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA512Digest.java @@ -0,0 +1,102 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Memoable; + + +/** + * FIPS 180-2 implementation of SHA-512. + * + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ +public class SHA512Digest + extends LongDigest +{ + private static final int DIGEST_LENGTH = 64; + + /** + * Standard constructor + */ + public SHA512Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA512Digest(SHA512Digest t) + { + super(t); + } + + public String getAlgorithmName() + { + return "SHA-512"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.longToBigEndian(H1, out, outOff); + Pack.longToBigEndian(H2, out, outOff + 8); + Pack.longToBigEndian(H3, out, outOff + 16); + Pack.longToBigEndian(H4, out, outOff + 24); + Pack.longToBigEndian(H5, out, outOff + 32); + Pack.longToBigEndian(H6, out, outOff + 40); + Pack.longToBigEndian(H7, out, outOff + 48); + Pack.longToBigEndian(H8, out, outOff + 56); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-512 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + H1 = 0x6a09e667f3bcc908L; + H2 = 0xbb67ae8584caa73bL; + H3 = 0x3c6ef372fe94f82bL; + H4 = 0xa54ff53a5f1d36f1L; + H5 = 0x510e527fade682d1L; + H6 = 0x9b05688c2b3e6c1fL; + H7 = 0x1f83d9abfb41bd6bL; + H8 = 0x5be0cd19137e2179L; + } + + public Memoable copy() + { + return new SHA512Digest(this); + } + + public void reset(Memoable other) + { + SHA512Digest d = (SHA512Digest)other; + + copyIn(d); + } +} + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA512tDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA512tDigest.java new file mode 100644 index 000000000..c6d2dd767 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SHA512tDigest.java @@ -0,0 +1,205 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.MemoableResetException; + +/** + * FIPS 180-4 implementation of SHA-512/t + */ +public class SHA512tDigest + extends LongDigest +{ + private final int digestLength; + + private long H1t, H2t, H3t, H4t, H5t, H6t, H7t, H8t; + + /** + * Standard constructor + */ + public SHA512tDigest(int bitLength) + { + if (bitLength >= 512) + { + throw new IllegalArgumentException("bitLength cannot be >= 512"); + } + + if (bitLength % 8 != 0) + { + throw new IllegalArgumentException("bitLength needs to be a multiple of 8"); + } + + if (bitLength == 384) + { + throw new IllegalArgumentException("bitLength cannot be 384 use SHA384 instead"); + } + + this.digestLength = bitLength / 8; + + tIvGenerate(digestLength * 8); + + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA512tDigest(SHA512tDigest t) + { + super(t); + + this.digestLength = t.digestLength; + + reset(t); + } + + public String getAlgorithmName() + { + return "SHA-512/" + Integer.toString(digestLength * 8); + } + + public int getDigestSize() + { + return digestLength; + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + longToBigEndian(H1, out, outOff, digestLength); + longToBigEndian(H2, out, outOff + 8, digestLength - 8); + longToBigEndian(H3, out, outOff + 16, digestLength - 16); + longToBigEndian(H4, out, outOff + 24, digestLength - 24); + longToBigEndian(H5, out, outOff + 32, digestLength - 32); + longToBigEndian(H6, out, outOff + 40, digestLength - 40); + longToBigEndian(H7, out, outOff + 48, digestLength - 48); + longToBigEndian(H8, out, outOff + 56, digestLength - 56); + + reset(); + + return digestLength; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* + * initial hash values use the iv generation algorithm for t. + */ + H1 = H1t; + H2 = H2t; + H3 = H3t; + H4 = H4t; + H5 = H5t; + H6 = H6t; + H7 = H7t; + H8 = H8t; + } + + private void tIvGenerate(int bitLength) + { + H1 = 0x6a09e667f3bcc908L ^ 0xa5a5a5a5a5a5a5a5L; + H2 = 0xbb67ae8584caa73bL ^ 0xa5a5a5a5a5a5a5a5L; + H3 = 0x3c6ef372fe94f82bL ^ 0xa5a5a5a5a5a5a5a5L; + H4 = 0xa54ff53a5f1d36f1L ^ 0xa5a5a5a5a5a5a5a5L; + H5 = 0x510e527fade682d1L ^ 0xa5a5a5a5a5a5a5a5L; + H6 = 0x9b05688c2b3e6c1fL ^ 0xa5a5a5a5a5a5a5a5L; + H7 = 0x1f83d9abfb41bd6bL ^ 0xa5a5a5a5a5a5a5a5L; + H8 = 0x5be0cd19137e2179L ^ 0xa5a5a5a5a5a5a5a5L; + + update((byte)0x53); + update((byte)0x48); + update((byte)0x41); + update((byte)0x2D); + update((byte)0x35); + update((byte)0x31); + update((byte)0x32); + update((byte)0x2F); + + if (bitLength > 100) + { + update((byte)(bitLength / 100 + 0x30)); + bitLength = bitLength % 100; + update((byte)(bitLength / 10 + 0x30)); + bitLength = bitLength % 10; + update((byte)(bitLength + 0x30)); + } + else if (bitLength > 10) + { + update((byte)(bitLength / 10 + 0x30)); + bitLength = bitLength % 10; + update((byte)(bitLength + 0x30)); + } + else + { + update((byte)(bitLength + 0x30)); + } + + finish(); + + H1t = H1; + H2t = H2; + H3t = H3; + H4t = H4; + H5t = H5; + H6t = H6; + H7t = H7; + H8t = H8; + } + + private static void longToBigEndian(long n, byte[] bs, int off, int max) + { + if (max > 0) + { + intToBigEndian((int)(n >>> 32), bs, off, max); + + if (max > 4) + { + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4, max - 4); + } + } + } + + private static void intToBigEndian(int n, byte[] bs, int off, int max) + { + int num = Math.min(4, max); + while (--num >= 0) + { + int shift = 8 * (3 - num); + bs[off + num] = (byte)(n >>> shift); + } + } + + public Memoable copy() + { + return new SHA512tDigest(this); + } + + public void reset(Memoable other) + { + SHA512tDigest t = (SHA512tDigest)other; + + if (this.digestLength != t.digestLength) + { + throw new MemoableResetException("digestLength inappropriate in other"); + } + + super.copyIn(t); + + this.H1t = t.H1t; + this.H2t = t.H2t; + this.H3t = t.H3t; + this.H4t = t.H4t; + this.H5t = t.H5t; + this.H6t = t.H6t; + this.H7t = t.H7t; + this.H8t = t.H8t; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SM3Digest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SM3Digest.java new file mode 100644 index 000000000..b7117a22d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SM3Digest.java @@ -0,0 +1,333 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Memoable; + +/** + * Implementation of Chinese SM3 digest as described at + * http://tools.ietf.org/html/draft-shen-sm3-hash-00 + * and at .... ( Chinese PDF ) + * + * The specification says "process a bit stream", + * but this is written to process bytes in blocks of 4, + * meaning this will process 32-bit word groups. + * But so do also most other digest specifications, + * including the SHA-256 which was a origin for + * this specification. + */ +public class SM3Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 32; // bytes + private static final int BLOCK_SIZE = 64 / 4; // of 32 bit ints (16 ints) + + private int[] V = new int[DIGEST_LENGTH / 4]; // in 32 bit ints (8 ints) + private int[] inwords = new int[BLOCK_SIZE]; + private int xOff; + + // Work-bufs used within processBlock() + private int[] W = new int[68]; + private int[] W1 = new int[64]; + + // Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions. + private static final int[] T = new int[64]; + + static + { + for (int i = 0; i < 16; ++i) + { + int t = 0x79CC4519; + T[i] = (t << i) | (t >>> (32 - i)); + } + for (int i = 16; i < 64; ++i) + { + int n = i % 32; + int t = 0x7A879D8A; + T[i] = (t << n) | (t >>> (32 - n)); + } + } + + + /** + * Standard constructor + */ + public SM3Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SM3Digest(SM3Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(SM3Digest t) + { + System.arraycopy(t.V, 0, this.V, 0, this.V.length); + System.arraycopy(t.inwords, 0, this.inwords, 0, this.inwords.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SM3"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + + public Memoable copy() + { + return new SM3Digest(this); + } + + public void reset(Memoable other) + { + SM3Digest d = (SM3Digest)other; + + super.copyIn(d); + copyIn(d); + } + + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + this.V[0] = 0x7380166F; + this.V[1] = 0x4914B2B9; + this.V[2] = 0x172442D7; + this.V[3] = 0xDA8A0600; + this.V[4] = 0xA96F30BC; + this.V[5] = 0x163138AA; + this.V[6] = 0xE38DEE4D; + this.V[7] = 0xB0FB0E4E; + + this.xOff = 0; + } + + + public int doFinal(byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(this.V[0], out, outOff + 0); + Pack.intToBigEndian(this.V[1], out, outOff + 4); + Pack.intToBigEndian(this.V[2], out, outOff + 8); + Pack.intToBigEndian(this.V[3], out, outOff + 12); + Pack.intToBigEndian(this.V[4], out, outOff + 16); + Pack.intToBigEndian(this.V[5], out, outOff + 20); + Pack.intToBigEndian(this.V[6], out, outOff + 24); + Pack.intToBigEndian(this.V[7], out, outOff + 28); + + reset(); + + return DIGEST_LENGTH; + } + + + protected void processWord(byte[] in, + int inOff) + { + // Note: Inlined for performance + // this.inwords[xOff] = Pack.bigEndianToInt(in, inOff); + int n = (((in[inOff] & 0xff) << 24) | + ((in[++inOff] & 0xff) << 16) | + ((in[++inOff] & 0xff) << 8) | + ((in[++inOff] & 0xff))); + + this.inwords[this.xOff] = n; + ++this.xOff; + + if (this.xOff >= 16) + { + processBlock(); + } + } + + protected void processLength(long bitLength) + { + if (this.xOff > (BLOCK_SIZE - 2)) + { + // xOff == 15 --> can't fit the 64 bit length field at tail.. + this.inwords[this.xOff] = 0; // fill with zero + ++this.xOff; + + processBlock(); + } + // Fill with zero words, until reach 2nd to last slot + while (this.xOff < (BLOCK_SIZE - 2)) + { + this.inwords[this.xOff] = 0; + ++this.xOff; + } + + // Store input data length in BITS + this.inwords[this.xOff++] = (int)(bitLength >>> 32); + this.inwords[this.xOff++] = (int)(bitLength); + } + +/* + +3.4.2. Constants + + + Tj = 79cc4519 when 0 < = j < = 15 + Tj = 7a879d8a when 16 < = j < = 63 + +3.4.3. Boolean function + + + FFj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15 + = (X AND Y) OR (X AND Z) OR (Y AND Z) when 16 < = j < = 63 + + GGj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15 + = (X AND Y) OR (NOT X AND Z) when 16 < = j < = 63 + + The X, Y, Z in the fomular are words!GBP + +3.4.4. Permutation function + + + P0(X) = X XOR (X <<< 9) XOR (X <<< 17) ## ROLL, not SHIFT + P1(X) = X XOR (X <<< 15) XOR (X <<< 23) ## ROLL, not SHIFT + + The X in the fomular are a word. + +---------- + +Each ROLL converted to Java expression: + +ROLL 9 : ((x << 9) | (x >>> (32-9)))) +ROLL 17 : ((x << 17) | (x >>> (32-17))) +ROLL 15 : ((x << 15) | (x >>> (32-15))) +ROLL 23 : ((x << 23) | (x >>> (32-23))) + + */ + + private int P0(final int x) + { + final int r9 = ((x << 9) | (x >>> (32 - 9))); + final int r17 = ((x << 17) | (x >>> (32 - 17))); + return (x ^ r9 ^ r17); + } + + private int P1(final int x) + { + final int r15 = ((x << 15) | (x >>> (32 - 15))); + final int r23 = ((x << 23) | (x >>> (32 - 23))); + return (x ^ r15 ^ r23); + } + + private int FF0(final int x, final int y, final int z) + { + return (x ^ y ^ z); + } + + private int FF1(final int x, final int y, final int z) + { + return ((x & y) | (x & z) | (y & z)); + } + + private int GG0(final int x, final int y, final int z) + { + return (x ^ y ^ z); + } + + private int GG1(final int x, final int y, final int z) + { + return ((x & y) | ((~x) & z)); + } + + + protected void processBlock() + { + for (int j = 0; j < 16; ++j) + { + this.W[j] = this.inwords[j]; + } + for (int j = 16; j < 68; ++j) + { + int wj3 = this.W[j - 3]; + int r15 = ((wj3 << 15) | (wj3 >>> (32 - 15))); + int wj13 = this.W[j - 13]; + int r7 = ((wj13 << 7) | (wj13 >>> (32 - 7))); + this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6]; + } + for (int j = 0; j < 64; ++j) + { + this.W1[j] = this.W[j] ^ this.W[j + 4]; + } + + int A = this.V[0]; + int B = this.V[1]; + int C = this.V[2]; + int D = this.V[3]; + int E = this.V[4]; + int F = this.V[5]; + int G = this.V[6]; + int H = this.V[7]; + + + for (int j = 0; j < 16; ++j) + { + int a12 = ((A << 12) | (A >>> (32 - 12))); + int s1_ = a12 + E + T[j]; + int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7))); + int SS2 = SS1 ^ a12; + int TT1 = FF0(A, B, C) + D + SS2 + this.W1[j]; + int TT2 = GG0(E, F, G) + H + SS1 + this.W[j]; + D = C; + C = ((B << 9) | (B >>> (32 - 9))); + B = A; + A = TT1; + H = G; + G = ((F << 19) | (F >>> (32 - 19))); + F = E; + E = P0(TT2); + } + + // Different FF,GG functions on rounds 16..63 + for (int j = 16; j < 64; ++j) + { + int a12 = ((A << 12) | (A >>> (32 - 12))); + int s1_ = a12 + E + T[j]; + int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7))); + int SS2 = SS1 ^ a12; + int TT1 = FF1(A, B, C) + D + SS2 + this.W1[j]; + int TT2 = GG1(E, F, G) + H + SS1 + this.W[j]; + D = C; + C = ((B << 9) | (B >>> (32 - 9))); + B = A; + A = TT1; + H = G; + G = ((F << 19) | (F >>> (32 - 19))); + F = E; + E = P0(TT2); + } + + this.V[0] ^= A; + this.V[1] ^= B; + this.V[2] ^= C; + this.V[3] ^= D; + this.V[4] ^= E; + this.V[5] ^= F; + this.V[6] ^= G; + this.V[7] ^= H; + + this.xOff = 0; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/ShortenedDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/ShortenedDigest.java new file mode 100644 index 000000000..274959f56 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/ShortenedDigest.java @@ -0,0 +1,80 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; + +/** + * Wrapper class that reduces the output length of a particular digest to + * only the first n bytes of the digest function. + */ +public class ShortenedDigest + implements ExtendedDigest +{ + private ExtendedDigest baseDigest; + private int length; + + /** + * Base constructor. + * + * @param baseDigest underlying digest to use. + * @param length length in bytes of the output of doFinal. + * @exception IllegalArgumentException if baseDigest is null, or length is greater than baseDigest.getDigestSize(). + */ + public ShortenedDigest( + ExtendedDigest baseDigest, + int length) + { + if (baseDigest == null) + { + throw new IllegalArgumentException("baseDigest must not be null"); + } + + if (length > baseDigest.getDigestSize()) + { + throw new IllegalArgumentException("baseDigest output not large enough to support length"); + } + + this.baseDigest = baseDigest; + this.length = length; + } + + public String getAlgorithmName() + { + return baseDigest.getAlgorithmName() + "(" + length * 8 + ")"; + } + + public int getDigestSize() + { + return length; + } + + public void update(byte in) + { + baseDigest.update(in); + } + + public void update(byte[] in, int inOff, int len) + { + baseDigest.update(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + byte[] tmp = new byte[baseDigest.getDigestSize()]; + + baseDigest.doFinal(tmp, 0); + + System.arraycopy(tmp, 0, out, outOff, length); + + return length; + } + + public void reset() + { + baseDigest.reset(); + } + + public int getByteLength() + { + return baseDigest.getByteLength(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SkeinDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SkeinDigest.java new file mode 100644 index 000000000..4da4d7189 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SkeinDigest.java @@ -0,0 +1,116 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.crypto.engines.ThreefishEngine; +import org.spongycastle.crypto.params.SkeinParameters; +import org.spongycastle.util.Memoable; + +/** + * Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, + * based on the {@link ThreefishEngine Threefish} tweakable block cipher. + * + * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + * competition in October 2010. + * + * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + * + * + * @see SkeinEngine + * @see SkeinParameters + */ +public class SkeinDigest + implements ExtendedDigest, Memoable +{ + /** + * 256 bit block size - Skein-256 + */ + public static final int SKEIN_256 = SkeinEngine.SKEIN_256; + /** + * 512 bit block size - Skein-512 + */ + public static final int SKEIN_512 = SkeinEngine.SKEIN_512; + /** + * 1024 bit block size - Skein-1024 + */ + public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private SkeinEngine engine; + + /** + * Constructs a Skein digest with an internal state size and output size. + * + * @param stateSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or + * {@link #SKEIN_1024}. + * @param digestSizeBits the output/digest size to produce in bits, which must be an integral number of + * bytes. + */ + public SkeinDigest(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + init(null); + } + + public SkeinDigest(SkeinDigest digest) + { + this.engine = new SkeinEngine(digest.engine); + } + + public void reset(Memoable other) + { + SkeinDigest d = (SkeinDigest)other; + engine.reset(d.engine); + } + + public Memoable copy() + { + return new SkeinDigest(this); + } + + public String getAlgorithmName() + { + return "Skein-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8); + } + + public int getDigestSize() + { + return engine.getOutputSize(); + } + + public int getByteLength() + { + return engine.getBlockSize(); + } + + /** + * Optionally initialises the Skein digest with the provided parameters.
null
to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ engine.init(params);
+ }
+
+ public void reset()
+ {
+ engine.reset();
+ }
+
+ public void update(byte in)
+ {
+ engine.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ engine.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return engine.doFinal(out, outOff);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SkeinEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SkeinEngine.java
new file mode 100644
index 000000000..001adcf26
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/SkeinEngine.java
@@ -0,0 +1,817 @@
+package org.spongycastle.crypto.digests;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.macs.SkeinMac;
+import org.spongycastle.crypto.params.SkeinParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
+ * sizes, based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ *
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ *
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ *
+ * This implementation is the basis for {@link SkeinDigest} and {@link SkeinMac}, implementing the
+ * parameter based configuration system that allows Skein to be adapted to multiple applications. null
to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ this.chain = null;
+ this.key = null;
+ this.preMessageParameters = null;
+ this.postMessageParameters = null;
+
+ if (params != null)
+ {
+ byte[] key = params.getKey();
+ if (key.length < 16)
+ {
+ throw new IllegalArgumentException("Skein key must be at least 128 bits.");
+ }
+ initParams(params.getParameters());
+ }
+ createInitialState();
+
+ // Initialise message block
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void initParams(Hashtable parameters)
+ {
+ Enumeration keys = parameters.keys();
+ final Vector pre = new Vector();
+ final Vector post = new Vector();
+
+ while (keys.hasMoreElements())
+ {
+ Integer type = (Integer)keys.nextElement();
+ byte[] value = (byte[])parameters.get(type);
+
+ if (type.intValue() == PARAM_TYPE_KEY)
+ {
+ this.key = value;
+ }
+ else if (type.intValue() < PARAM_TYPE_MESSAGE)
+ {
+ pre.addElement(new Parameter(type.intValue(), value));
+ }
+ else
+ {
+ post.addElement(new Parameter(type.intValue(), value));
+ }
+ }
+ preMessageParameters = new Parameter[pre.size()];
+ pre.copyInto(preMessageParameters);
+ sort(preMessageParameters);
+
+ postMessageParameters = new Parameter[post.size()];
+ post.copyInto(postMessageParameters);
+ sort(postMessageParameters);
+ }
+
+ private static void sort(Parameter[] params)
+ {
+ if (params == null)
+ {
+ return;
+ }
+ // Insertion sort, for Java 1.1 compatibility
+ for (int i = 1; i < params.length; i++)
+ {
+ Parameter param = params[i];
+ int hole = i;
+ while (hole > 0 && param.getType() < params[hole - 1].getType())
+ {
+ params[hole] = params[hole - 1];
+ hole = hole - 1;
+ }
+ params[hole] = param;
+ }
+ }
+
+ /**
+ * Calculate the initial (pre message block) chaining state.
+ */
+ private void createInitialState()
+ {
+ long[] precalc = (long[])INITIAL_STATES.get(variantIdentifier(getBlockSize(), getOutputSize()));
+ if ((key == null) && (precalc != null))
+ {
+ // Precalculated UBI(CFG)
+ chain = Arrays.clone(precalc);
+ }
+ else
+ {
+ // Blank initial state
+ chain = new long[getBlockSize() / 8];
+
+ // Process key block
+ if (key != null)
+ {
+ ubiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
+ }
+
+ // Process configuration block
+ ubiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).getBytes());
+ }
+
+ // Process additional pre-message parameters
+ if (preMessageParameters != null)
+ {
+ for (int i = 0; i < preMessageParameters.length; i++)
+ {
+ Parameter param = preMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+ initialState = Arrays.clone(chain);
+ }
+
+ /**
+ * Reset the engine to the initial state (with the key and any pre-message parameters , ready to
+ * accept message input.
+ */
+ public void reset()
+ {
+ System.arraycopy(initialState, 0, chain, 0, chain.length);
+
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void ubiComplete(int type, byte[] value)
+ {
+ ubiInit(type);
+ this.ubi.update(value, 0, value.length, chain);
+ ubiFinal();
+ }
+
+ private void ubiInit(int type)
+ {
+ this.ubi.reset(type);
+ }
+
+ private void ubiFinal()
+ {
+ ubi.doFinal(chain);
+ }
+
+ private void checkInitialised()
+ {
+ if (this.ubi == null)
+ {
+ throw new IllegalArgumentException("Skein engine is not initialised.");
+ }
+ }
+
+ public void update(byte in)
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ checkInitialised();
+ ubi.update(in, inOff, len, chain);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ checkInitialised();
+ if (out.length < (outOff + outputSizeBytes))
+ {
+ throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes");
+ }
+
+ // Finalise message block
+ ubiFinal();
+
+ // Process additional post-message parameters
+ if (postMessageParameters != null)
+ {
+ for (int i = 0; i < postMessageParameters.length; i++)
+ {
+ Parameter param = postMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+
+ // Perform the output transform
+ final int blockSize = getBlockSize();
+ final int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
+ for (int i = 0; i < blocksRequired; i++)
+ {
+ final int toWrite = Math.min(blockSize, outputSizeBytes - (i * blockSize));
+ output(i, out, outOff + (i * blockSize), toWrite);
+ }
+
+ reset();
+
+ return outputSizeBytes;
+ }
+
+ private void output(long outputSequence, byte[] out, int outOff, int outputBytes)
+ {
+ byte[] currentBytes = new byte[8];
+ ThreefishEngine.wordToBytes(outputSequence, currentBytes, 0);
+
+ // Output is a sequence of UBI invocations all of which use and preserve the pre-output
+ // state
+ long[] outputWords = new long[chain.length];
+ ubiInit(PARAM_TYPE_OUTPUT);
+ this.ubi.update(currentBytes, 0, currentBytes.length, outputWords);
+ ubi.doFinal(outputWords);
+
+ final int wordsRequired = ((outputBytes + 8 - 1) / 8);
+ for (int i = 0; i < wordsRequired; i++)
+ {
+ int toWrite = Math.min(8, outputBytes - (i * 8));
+ if (toWrite == 8)
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], out, outOff + (i * 8));
+ }
+ else
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], currentBytes, 0);
+ System.arraycopy(currentBytes, 0, out, outOff + (i * 8), toWrite);
+ }
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/TigerDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/TigerDigest.java
new file mode 100644
index 000000000..b9d0b83a3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/TigerDigest.java
@@ -0,0 +1,879 @@
+package org.spongycastle.crypto.digests;
+
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.util.Memoable;
+
+/**
+ * implementation of Tiger based on:
+ *
+ * http://www.cs.technion.ac.il/~biham/Reports/Tiger
+ */
+public class TigerDigest
+ implements ExtendedDigest, Memoable
+{
+ private static final int BYTE_LENGTH = 64;
+
+ /*
+ * S-Boxes.
+ */
+ private static final long[] t1 = {
+ 0x02AAB17CF7E90C5EL /* 0 */, 0xAC424B03E243A8ECL /* 1 */,
+ 0x72CD5BE30DD5FCD3L /* 2 */, 0x6D019B93F6F97F3AL /* 3 */,
+ 0xCD9978FFD21F9193L /* 4 */, 0x7573A1C9708029E2L /* 5 */,
+ 0xB164326B922A83C3L /* 6 */, 0x46883EEE04915870L /* 7 */,
+ 0xEAACE3057103ECE6L /* 8 */, 0xC54169B808A3535CL /* 9 */,
+ 0x4CE754918DDEC47CL /* 10 */, 0x0AA2F4DFDC0DF40CL /* 11 */,
+ 0x10B76F18A74DBEFAL /* 12 */, 0xC6CCB6235AD1AB6AL /* 13 */,
+ 0x13726121572FE2FFL /* 14 */, 0x1A488C6F199D921EL /* 15 */,
+ 0x4BC9F9F4DA0007CAL /* 16 */, 0x26F5E6F6E85241C7L /* 17 */,
+ 0x859079DBEA5947B6L /* 18 */, 0x4F1885C5C99E8C92L /* 19 */,
+ 0xD78E761EA96F864BL /* 20 */, 0x8E36428C52B5C17DL /* 21 */,
+ 0x69CF6827373063C1L /* 22 */, 0xB607C93D9BB4C56EL /* 23 */,
+ 0x7D820E760E76B5EAL /* 24 */, 0x645C9CC6F07FDC42L /* 25 */,
+ 0xBF38A078243342E0L /* 26 */, 0x5F6B343C9D2E7D04L /* 27 */,
+ 0xF2C28AEB600B0EC6L /* 28 */, 0x6C0ED85F7254BCACL /* 29 */,
+ 0x71592281A4DB4FE5L /* 30 */, 0x1967FA69CE0FED9FL /* 31 */,
+ 0xFD5293F8B96545DBL /* 32 */, 0xC879E9D7F2A7600BL /* 33 */,
+ 0x860248920193194EL /* 34 */, 0xA4F9533B2D9CC0B3L /* 35 */,
+ 0x9053836C15957613L /* 36 */, 0xDB6DCF8AFC357BF1L /* 37 */,
+ 0x18BEEA7A7A370F57L /* 38 */, 0x037117CA50B99066L /* 39 */,
+ 0x6AB30A9774424A35L /* 40 */, 0xF4E92F02E325249BL /* 41 */,
+ 0x7739DB07061CCAE1L /* 42 */, 0xD8F3B49CECA42A05L /* 43 */,
+ 0xBD56BE3F51382F73L /* 44 */, 0x45FAED5843B0BB28L /* 45 */,
+ 0x1C813D5C11BF1F83L /* 46 */, 0x8AF0E4B6D75FA169L /* 47 */,
+ 0x33EE18A487AD9999L /* 48 */, 0x3C26E8EAB1C94410L /* 49 */,
+ 0xB510102BC0A822F9L /* 50 */, 0x141EEF310CE6123BL /* 51 */,
+ 0xFC65B90059DDB154L /* 52 */, 0xE0158640C5E0E607L /* 53 */,
+ 0x884E079826C3A3CFL /* 54 */, 0x930D0D9523C535FDL /* 55 */,
+ 0x35638D754E9A2B00L /* 56 */, 0x4085FCCF40469DD5L /* 57 */,
+ 0xC4B17AD28BE23A4CL /* 58 */, 0xCAB2F0FC6A3E6A2EL /* 59 */,
+ 0x2860971A6B943FCDL /* 60 */, 0x3DDE6EE212E30446L /* 61 */,
+ 0x6222F32AE01765AEL /* 62 */, 0x5D550BB5478308FEL /* 63 */,
+ 0xA9EFA98DA0EDA22AL /* 64 */, 0xC351A71686C40DA7L /* 65 */,
+ 0x1105586D9C867C84L /* 66 */, 0xDCFFEE85FDA22853L /* 67 */,
+ 0xCCFBD0262C5EEF76L /* 68 */, 0xBAF294CB8990D201L /* 69 */,
+ 0xE69464F52AFAD975L /* 70 */, 0x94B013AFDF133E14L /* 71 */,
+ 0x06A7D1A32823C958L /* 72 */, 0x6F95FE5130F61119L /* 73 */,
+ 0xD92AB34E462C06C0L /* 74 */, 0xED7BDE33887C71D2L /* 75 */,
+ 0x79746D6E6518393EL /* 76 */, 0x5BA419385D713329L /* 77 */,
+ 0x7C1BA6B948A97564L /* 78 */, 0x31987C197BFDAC67L /* 79 */,
+ 0xDE6C23C44B053D02L /* 80 */, 0x581C49FED002D64DL /* 81 */,
+ 0xDD474D6338261571L /* 82 */, 0xAA4546C3E473D062L /* 83 */,
+ 0x928FCE349455F860L /* 84 */, 0x48161BBACAAB94D9L /* 85 */,
+ 0x63912430770E6F68L /* 86 */, 0x6EC8A5E602C6641CL /* 87 */,
+ 0x87282515337DDD2BL /* 88 */, 0x2CDA6B42034B701BL /* 89 */,
+ 0xB03D37C181CB096DL /* 90 */, 0xE108438266C71C6FL /* 91 */,
+ 0x2B3180C7EB51B255L /* 92 */, 0xDF92B82F96C08BBCL /* 93 */,
+ 0x5C68C8C0A632F3BAL /* 94 */, 0x5504CC861C3D0556L /* 95 */,
+ 0xABBFA4E55FB26B8FL /* 96 */, 0x41848B0AB3BACEB4L /* 97 */,
+ 0xB334A273AA445D32L /* 98 */, 0xBCA696F0A85AD881L /* 99 */,
+ 0x24F6EC65B528D56CL /* 100 */, 0x0CE1512E90F4524AL /* 101 */,
+ 0x4E9DD79D5506D35AL /* 102 */, 0x258905FAC6CE9779L /* 103 */,
+ 0x2019295B3E109B33L /* 104 */, 0xF8A9478B73A054CCL /* 105 */,
+ 0x2924F2F934417EB0L /* 106 */, 0x3993357D536D1BC4L /* 107 */,
+ 0x38A81AC21DB6FF8BL /* 108 */, 0x47C4FBF17D6016BFL /* 109 */,
+ 0x1E0FAADD7667E3F5L /* 110 */, 0x7ABCFF62938BEB96L /* 111 */,
+ 0xA78DAD948FC179C9L /* 112 */, 0x8F1F98B72911E50DL /* 113 */,
+ 0x61E48EAE27121A91L /* 114 */, 0x4D62F7AD31859808L /* 115 */,
+ 0xECEBA345EF5CEAEBL /* 116 */, 0xF5CEB25EBC9684CEL /* 117 */,
+ 0xF633E20CB7F76221L /* 118 */, 0xA32CDF06AB8293E4L /* 119 */,
+ 0x985A202CA5EE2CA4L /* 120 */, 0xCF0B8447CC8A8FB1L /* 121 */,
+ 0x9F765244979859A3L /* 122 */, 0xA8D516B1A1240017L /* 123 */,
+ 0x0BD7BA3EBB5DC726L /* 124 */, 0xE54BCA55B86ADB39L /* 125 */,
+ 0x1D7A3AFD6C478063L /* 126 */, 0x519EC608E7669EDDL /* 127 */,
+ 0x0E5715A2D149AA23L /* 128 */, 0x177D4571848FF194L /* 129 */,
+ 0xEEB55F3241014C22L /* 130 */, 0x0F5E5CA13A6E2EC2L /* 131 */,
+ 0x8029927B75F5C361L /* 132 */, 0xAD139FABC3D6E436L /* 133 */,
+ 0x0D5DF1A94CCF402FL /* 134 */, 0x3E8BD948BEA5DFC8L /* 135 */,
+ 0xA5A0D357BD3FF77EL /* 136 */, 0xA2D12E251F74F645L /* 137 */,
+ 0x66FD9E525E81A082L /* 138 */, 0x2E0C90CE7F687A49L /* 139 */,
+ 0xC2E8BCBEBA973BC5L /* 140 */, 0x000001BCE509745FL /* 141 */,
+ 0x423777BBE6DAB3D6L /* 142 */, 0xD1661C7EAEF06EB5L /* 143 */,
+ 0xA1781F354DAACFD8L /* 144 */, 0x2D11284A2B16AFFCL /* 145 */,
+ 0xF1FC4F67FA891D1FL /* 146 */, 0x73ECC25DCB920ADAL /* 147 */,
+ 0xAE610C22C2A12651L /* 148 */, 0x96E0A810D356B78AL /* 149 */,
+ 0x5A9A381F2FE7870FL /* 150 */, 0xD5AD62EDE94E5530L /* 151 */,
+ 0xD225E5E8368D1427L /* 152 */, 0x65977B70C7AF4631L /* 153 */,
+ 0x99F889B2DE39D74FL /* 154 */, 0x233F30BF54E1D143L /* 155 */,
+ 0x9A9675D3D9A63C97L /* 156 */, 0x5470554FF334F9A8L /* 157 */,
+ 0x166ACB744A4F5688L /* 158 */, 0x70C74CAAB2E4AEADL /* 159 */,
+ 0xF0D091646F294D12L /* 160 */, 0x57B82A89684031D1L /* 161 */,
+ 0xEFD95A5A61BE0B6BL /* 162 */, 0x2FBD12E969F2F29AL /* 163 */,
+ 0x9BD37013FEFF9FE8L /* 164 */, 0x3F9B0404D6085A06L /* 165 */,
+ 0x4940C1F3166CFE15L /* 166 */, 0x09542C4DCDF3DEFBL /* 167 */,
+ 0xB4C5218385CD5CE3L /* 168 */, 0xC935B7DC4462A641L /* 169 */,
+ 0x3417F8A68ED3B63FL /* 170 */, 0xB80959295B215B40L /* 171 */,
+ 0xF99CDAEF3B8C8572L /* 172 */, 0x018C0614F8FCB95DL /* 173 */,
+ 0x1B14ACCD1A3ACDF3L /* 174 */, 0x84D471F200BB732DL /* 175 */,
+ 0xC1A3110E95E8DA16L /* 176 */, 0x430A7220BF1A82B8L /* 177 */,
+ 0xB77E090D39DF210EL /* 178 */, 0x5EF4BD9F3CD05E9DL /* 179 */,
+ 0x9D4FF6DA7E57A444L /* 180 */, 0xDA1D60E183D4A5F8L /* 181 */,
+ 0xB287C38417998E47L /* 182 */, 0xFE3EDC121BB31886L /* 183 */,
+ 0xC7FE3CCC980CCBEFL /* 184 */, 0xE46FB590189BFD03L /* 185 */,
+ 0x3732FD469A4C57DCL /* 186 */, 0x7EF700A07CF1AD65L /* 187 */,
+ 0x59C64468A31D8859L /* 188 */, 0x762FB0B4D45B61F6L /* 189 */,
+ 0x155BAED099047718L /* 190 */, 0x68755E4C3D50BAA6L /* 191 */,
+ 0xE9214E7F22D8B4DFL /* 192 */, 0x2ADDBF532EAC95F4L /* 193 */,
+ 0x32AE3909B4BD0109L /* 194 */, 0x834DF537B08E3450L /* 195 */,
+ 0xFA209DA84220728DL /* 196 */, 0x9E691D9B9EFE23F7L /* 197 */,
+ 0x0446D288C4AE8D7FL /* 198 */, 0x7B4CC524E169785BL /* 199 */,
+ 0x21D87F0135CA1385L /* 200 */, 0xCEBB400F137B8AA5L /* 201 */,
+ 0x272E2B66580796BEL /* 202 */, 0x3612264125C2B0DEL /* 203 */,
+ 0x057702BDAD1EFBB2L /* 204 */, 0xD4BABB8EACF84BE9L /* 205 */,
+ 0x91583139641BC67BL /* 206 */, 0x8BDC2DE08036E024L /* 207 */,
+ 0x603C8156F49F68EDL /* 208 */, 0xF7D236F7DBEF5111L /* 209 */,
+ 0x9727C4598AD21E80L /* 210 */, 0xA08A0896670A5FD7L /* 211 */,
+ 0xCB4A8F4309EBA9CBL /* 212 */, 0x81AF564B0F7036A1L /* 213 */,
+ 0xC0B99AA778199ABDL /* 214 */, 0x959F1EC83FC8E952L /* 215 */,
+ 0x8C505077794A81B9L /* 216 */, 0x3ACAAF8F056338F0L /* 217 */,
+ 0x07B43F50627A6778L /* 218 */, 0x4A44AB49F5ECCC77L /* 219 */,
+ 0x3BC3D6E4B679EE98L /* 220 */, 0x9CC0D4D1CF14108CL /* 221 */,
+ 0x4406C00B206BC8A0L /* 222 */, 0x82A18854C8D72D89L /* 223 */,
+ 0x67E366B35C3C432CL /* 224 */, 0xB923DD61102B37F2L /* 225 */,
+ 0x56AB2779D884271DL /* 226 */, 0xBE83E1B0FF1525AFL /* 227 */,
+ 0xFB7C65D4217E49A9L /* 228 */, 0x6BDBE0E76D48E7D4L /* 229 */,
+ 0x08DF828745D9179EL /* 230 */, 0x22EA6A9ADD53BD34L /* 231 */,
+ 0xE36E141C5622200AL /* 232 */, 0x7F805D1B8CB750EEL /* 233 */,
+ 0xAFE5C7A59F58E837L /* 234 */, 0xE27F996A4FB1C23CL /* 235 */,
+ 0xD3867DFB0775F0D0L /* 236 */, 0xD0E673DE6E88891AL /* 237 */,
+ 0x123AEB9EAFB86C25L /* 238 */, 0x30F1D5D5C145B895L /* 239 */,
+ 0xBB434A2DEE7269E7L /* 240 */, 0x78CB67ECF931FA38L /* 241 */,
+ 0xF33B0372323BBF9CL /* 242 */, 0x52D66336FB279C74L /* 243 */,
+ 0x505F33AC0AFB4EAAL /* 244 */, 0xE8A5CD99A2CCE187L /* 245 */,
+ 0x534974801E2D30BBL /* 246 */, 0x8D2D5711D5876D90L /* 247 */,
+ 0x1F1A412891BC038EL /* 248 */, 0xD6E2E71D82E56648L /* 249 */,
+ 0x74036C3A497732B7L /* 250 */, 0x89B67ED96361F5ABL /* 251 */,
+ 0xFFED95D8F1EA02A2L /* 252 */, 0xE72B3BD61464D43DL /* 253 */,
+ 0xA6300F170BDC4820L /* 254 */, 0xEBC18760ED78A77AL /* 255 */,
+ };
+
+ private static final long[] t2 = {
+ 0xE6A6BE5A05A12138L /* 256 */, 0xB5A122A5B4F87C98L /* 257 */,
+ 0x563C6089140B6990L /* 258 */, 0x4C46CB2E391F5DD5L /* 259 */,
+ 0xD932ADDBC9B79434L /* 260 */, 0x08EA70E42015AFF5L /* 261 */,
+ 0xD765A6673E478CF1L /* 262 */, 0xC4FB757EAB278D99L /* 263 */,
+ 0xDF11C6862D6E0692L /* 264 */, 0xDDEB84F10D7F3B16L /* 265 */,
+ 0x6F2EF604A665EA04L /* 266 */, 0x4A8E0F0FF0E0DFB3L /* 267 */,
+ 0xA5EDEEF83DBCBA51L /* 268 */, 0xFC4F0A2A0EA4371EL /* 269 */,
+ 0xE83E1DA85CB38429L /* 270 */, 0xDC8FF882BA1B1CE2L /* 271 */,
+ 0xCD45505E8353E80DL /* 272 */, 0x18D19A00D4DB0717L /* 273 */,
+ 0x34A0CFEDA5F38101L /* 274 */, 0x0BE77E518887CAF2L /* 275 */,
+ 0x1E341438B3C45136L /* 276 */, 0xE05797F49089CCF9L /* 277 */,
+ 0xFFD23F9DF2591D14L /* 278 */, 0x543DDA228595C5CDL /* 279 */,
+ 0x661F81FD99052A33L /* 280 */, 0x8736E641DB0F7B76L /* 281 */,
+ 0x15227725418E5307L /* 282 */, 0xE25F7F46162EB2FAL /* 283 */,
+ 0x48A8B2126C13D9FEL /* 284 */, 0xAFDC541792E76EEAL /* 285 */,
+ 0x03D912BFC6D1898FL /* 286 */, 0x31B1AAFA1B83F51BL /* 287 */,
+ 0xF1AC2796E42AB7D9L /* 288 */, 0x40A3A7D7FCD2EBACL /* 289 */,
+ 0x1056136D0AFBBCC5L /* 290 */, 0x7889E1DD9A6D0C85L /* 291 */,
+ 0xD33525782A7974AAL /* 292 */, 0xA7E25D09078AC09BL /* 293 */,
+ 0xBD4138B3EAC6EDD0L /* 294 */, 0x920ABFBE71EB9E70L /* 295 */,
+ 0xA2A5D0F54FC2625CL /* 296 */, 0xC054E36B0B1290A3L /* 297 */,
+ 0xF6DD59FF62FE932BL /* 298 */, 0x3537354511A8AC7DL /* 299 */,
+ 0xCA845E9172FADCD4L /* 300 */, 0x84F82B60329D20DCL /* 301 */,
+ 0x79C62CE1CD672F18L /* 302 */, 0x8B09A2ADD124642CL /* 303 */,
+ 0xD0C1E96A19D9E726L /* 304 */, 0x5A786A9B4BA9500CL /* 305 */,
+ 0x0E020336634C43F3L /* 306 */, 0xC17B474AEB66D822L /* 307 */,
+ 0x6A731AE3EC9BAAC2L /* 308 */, 0x8226667AE0840258L /* 309 */,
+ 0x67D4567691CAECA5L /* 310 */, 0x1D94155C4875ADB5L /* 311 */,
+ 0x6D00FD985B813FDFL /* 312 */, 0x51286EFCB774CD06L /* 313 */,
+ 0x5E8834471FA744AFL /* 314 */, 0xF72CA0AEE761AE2EL /* 315 */,
+ 0xBE40E4CDAEE8E09AL /* 316 */, 0xE9970BBB5118F665L /* 317 */,
+ 0x726E4BEB33DF1964L /* 318 */, 0x703B000729199762L /* 319 */,
+ 0x4631D816F5EF30A7L /* 320 */, 0xB880B5B51504A6BEL /* 321 */,
+ 0x641793C37ED84B6CL /* 322 */, 0x7B21ED77F6E97D96L /* 323 */,
+ 0x776306312EF96B73L /* 324 */, 0xAE528948E86FF3F4L /* 325 */,
+ 0x53DBD7F286A3F8F8L /* 326 */, 0x16CADCE74CFC1063L /* 327 */,
+ 0x005C19BDFA52C6DDL /* 328 */, 0x68868F5D64D46AD3L /* 329 */,
+ 0x3A9D512CCF1E186AL /* 330 */, 0x367E62C2385660AEL /* 331 */,
+ 0xE359E7EA77DCB1D7L /* 332 */, 0x526C0773749ABE6EL /* 333 */,
+ 0x735AE5F9D09F734BL /* 334 */, 0x493FC7CC8A558BA8L /* 335 */,
+ 0xB0B9C1533041AB45L /* 336 */, 0x321958BA470A59BDL /* 337 */,
+ 0x852DB00B5F46C393L /* 338 */, 0x91209B2BD336B0E5L /* 339 */,
+ 0x6E604F7D659EF19FL /* 340 */, 0xB99A8AE2782CCB24L /* 341 */,
+ 0xCCF52AB6C814C4C7L /* 342 */, 0x4727D9AFBE11727BL /* 343 */,
+ 0x7E950D0C0121B34DL /* 344 */, 0x756F435670AD471FL /* 345 */,
+ 0xF5ADD442615A6849L /* 346 */, 0x4E87E09980B9957AL /* 347 */,
+ 0x2ACFA1DF50AEE355L /* 348 */, 0xD898263AFD2FD556L /* 349 */,
+ 0xC8F4924DD80C8FD6L /* 350 */, 0xCF99CA3D754A173AL /* 351 */,
+ 0xFE477BACAF91BF3CL /* 352 */, 0xED5371F6D690C12DL /* 353 */,
+ 0x831A5C285E687094L /* 354 */, 0xC5D3C90A3708A0A4L /* 355 */,
+ 0x0F7F903717D06580L /* 356 */, 0x19F9BB13B8FDF27FL /* 357 */,
+ 0xB1BD6F1B4D502843L /* 358 */, 0x1C761BA38FFF4012L /* 359 */,
+ 0x0D1530C4E2E21F3BL /* 360 */, 0x8943CE69A7372C8AL /* 361 */,
+ 0xE5184E11FEB5CE66L /* 362 */, 0x618BDB80BD736621L /* 363 */,
+ 0x7D29BAD68B574D0BL /* 364 */, 0x81BB613E25E6FE5BL /* 365 */,
+ 0x071C9C10BC07913FL /* 366 */, 0xC7BEEB7909AC2D97L /* 367 */,
+ 0xC3E58D353BC5D757L /* 368 */, 0xEB017892F38F61E8L /* 369 */,
+ 0xD4EFFB9C9B1CC21AL /* 370 */, 0x99727D26F494F7ABL /* 371 */,
+ 0xA3E063A2956B3E03L /* 372 */, 0x9D4A8B9A4AA09C30L /* 373 */,
+ 0x3F6AB7D500090FB4L /* 374 */, 0x9CC0F2A057268AC0L /* 375 */,
+ 0x3DEE9D2DEDBF42D1L /* 376 */, 0x330F49C87960A972L /* 377 */,
+ 0xC6B2720287421B41L /* 378 */, 0x0AC59EC07C00369CL /* 379 */,
+ 0xEF4EAC49CB353425L /* 380 */, 0xF450244EEF0129D8L /* 381 */,
+ 0x8ACC46E5CAF4DEB6L /* 382 */, 0x2FFEAB63989263F7L /* 383 */,
+ 0x8F7CB9FE5D7A4578L /* 384 */, 0x5BD8F7644E634635L /* 385 */,
+ 0x427A7315BF2DC900L /* 386 */, 0x17D0C4AA2125261CL /* 387 */,
+ 0x3992486C93518E50L /* 388 */, 0xB4CBFEE0A2D7D4C3L /* 389 */,
+ 0x7C75D6202C5DDD8DL /* 390 */, 0xDBC295D8E35B6C61L /* 391 */,
+ 0x60B369D302032B19L /* 392 */, 0xCE42685FDCE44132L /* 393 */,
+ 0x06F3DDB9DDF65610L /* 394 */, 0x8EA4D21DB5E148F0L /* 395 */,
+ 0x20B0FCE62FCD496FL /* 396 */, 0x2C1B912358B0EE31L /* 397 */,
+ 0xB28317B818F5A308L /* 398 */, 0xA89C1E189CA6D2CFL /* 399 */,
+ 0x0C6B18576AAADBC8L /* 400 */, 0xB65DEAA91299FAE3L /* 401 */,
+ 0xFB2B794B7F1027E7L /* 402 */, 0x04E4317F443B5BEBL /* 403 */,
+ 0x4B852D325939D0A6L /* 404 */, 0xD5AE6BEEFB207FFCL /* 405 */,
+ 0x309682B281C7D374L /* 406 */, 0xBAE309A194C3B475L /* 407 */,
+ 0x8CC3F97B13B49F05L /* 408 */, 0x98A9422FF8293967L /* 409 */,
+ 0x244B16B01076FF7CL /* 410 */, 0xF8BF571C663D67EEL /* 411 */,
+ 0x1F0D6758EEE30DA1L /* 412 */, 0xC9B611D97ADEB9B7L /* 413 */,
+ 0xB7AFD5887B6C57A2L /* 414 */, 0x6290AE846B984FE1L /* 415 */,
+ 0x94DF4CDEACC1A5FDL /* 416 */, 0x058A5BD1C5483AFFL /* 417 */,
+ 0x63166CC142BA3C37L /* 418 */, 0x8DB8526EB2F76F40L /* 419 */,
+ 0xE10880036F0D6D4EL /* 420 */, 0x9E0523C9971D311DL /* 421 */,
+ 0x45EC2824CC7CD691L /* 422 */, 0x575B8359E62382C9L /* 423 */,
+ 0xFA9E400DC4889995L /* 424 */, 0xD1823ECB45721568L /* 425 */,
+ 0xDAFD983B8206082FL /* 426 */, 0xAA7D29082386A8CBL /* 427 */,
+ 0x269FCD4403B87588L /* 428 */, 0x1B91F5F728BDD1E0L /* 429 */,
+ 0xE4669F39040201F6L /* 430 */, 0x7A1D7C218CF04ADEL /* 431 */,
+ 0x65623C29D79CE5CEL /* 432 */, 0x2368449096C00BB1L /* 433 */,
+ 0xAB9BF1879DA503BAL /* 434 */, 0xBC23ECB1A458058EL /* 435 */,
+ 0x9A58DF01BB401ECCL /* 436 */, 0xA070E868A85F143DL /* 437 */,
+ 0x4FF188307DF2239EL /* 438 */, 0x14D565B41A641183L /* 439 */,
+ 0xEE13337452701602L /* 440 */, 0x950E3DCF3F285E09L /* 441 */,
+ 0x59930254B9C80953L /* 442 */, 0x3BF299408930DA6DL /* 443 */,
+ 0xA955943F53691387L /* 444 */, 0xA15EDECAA9CB8784L /* 445 */,
+ 0x29142127352BE9A0L /* 446 */, 0x76F0371FFF4E7AFBL /* 447 */,
+ 0x0239F450274F2228L /* 448 */, 0xBB073AF01D5E868BL /* 449 */,
+ 0xBFC80571C10E96C1L /* 450 */, 0xD267088568222E23L /* 451 */,
+ 0x9671A3D48E80B5B0L /* 452 */, 0x55B5D38AE193BB81L /* 453 */,
+ 0x693AE2D0A18B04B8L /* 454 */, 0x5C48B4ECADD5335FL /* 455 */,
+ 0xFD743B194916A1CAL /* 456 */, 0x2577018134BE98C4L /* 457 */,
+ 0xE77987E83C54A4ADL /* 458 */, 0x28E11014DA33E1B9L /* 459 */,
+ 0x270CC59E226AA213L /* 460 */, 0x71495F756D1A5F60L /* 461 */,
+ 0x9BE853FB60AFEF77L /* 462 */, 0xADC786A7F7443DBFL /* 463 */,
+ 0x0904456173B29A82L /* 464 */, 0x58BC7A66C232BD5EL /* 465 */,
+ 0xF306558C673AC8B2L /* 466 */, 0x41F639C6B6C9772AL /* 467 */,
+ 0x216DEFE99FDA35DAL /* 468 */, 0x11640CC71C7BE615L /* 469 */,
+ 0x93C43694565C5527L /* 470 */, 0xEA038E6246777839L /* 471 */,
+ 0xF9ABF3CE5A3E2469L /* 472 */, 0x741E768D0FD312D2L /* 473 */,
+ 0x0144B883CED652C6L /* 474 */, 0xC20B5A5BA33F8552L /* 475 */,
+ 0x1AE69633C3435A9DL /* 476 */, 0x97A28CA4088CFDECL /* 477 */,
+ 0x8824A43C1E96F420L /* 478 */, 0x37612FA66EEEA746L /* 479 */,
+ 0x6B4CB165F9CF0E5AL /* 480 */, 0x43AA1C06A0ABFB4AL /* 481 */,
+ 0x7F4DC26FF162796BL /* 482 */, 0x6CBACC8E54ED9B0FL /* 483 */,
+ 0xA6B7FFEFD2BB253EL /* 484 */, 0x2E25BC95B0A29D4FL /* 485 */,
+ 0x86D6A58BDEF1388CL /* 486 */, 0xDED74AC576B6F054L /* 487 */,
+ 0x8030BDBC2B45805DL /* 488 */, 0x3C81AF70E94D9289L /* 489 */,
+ 0x3EFF6DDA9E3100DBL /* 490 */, 0xB38DC39FDFCC8847L /* 491 */,
+ 0x123885528D17B87EL /* 492 */, 0xF2DA0ED240B1B642L /* 493 */,
+ 0x44CEFADCD54BF9A9L /* 494 */, 0x1312200E433C7EE6L /* 495 */,
+ 0x9FFCC84F3A78C748L /* 496 */, 0xF0CD1F72248576BBL /* 497 */,
+ 0xEC6974053638CFE4L /* 498 */, 0x2BA7B67C0CEC4E4CL /* 499 */,
+ 0xAC2F4DF3E5CE32EDL /* 500 */, 0xCB33D14326EA4C11L /* 501 */,
+ 0xA4E9044CC77E58BCL /* 502 */, 0x5F513293D934FCEFL /* 503 */,
+ 0x5DC9645506E55444L /* 504 */, 0x50DE418F317DE40AL /* 505 */,
+ 0x388CB31A69DDE259L /* 506 */, 0x2DB4A83455820A86L /* 507 */,
+ 0x9010A91E84711AE9L /* 508 */, 0x4DF7F0B7B1498371L /* 509 */,
+ 0xD62A2EABC0977179L /* 510 */, 0x22FAC097AA8D5C0EL /* 511 */,
+ };
+
+ private static final long[] t3 = {
+ 0xF49FCC2FF1DAF39BL /* 512 */, 0x487FD5C66FF29281L /* 513 */,
+ 0xE8A30667FCDCA83FL /* 514 */, 0x2C9B4BE3D2FCCE63L /* 515 */,
+ 0xDA3FF74B93FBBBC2L /* 516 */, 0x2FA165D2FE70BA66L /* 517 */,
+ 0xA103E279970E93D4L /* 518 */, 0xBECDEC77B0E45E71L /* 519 */,
+ 0xCFB41E723985E497L /* 520 */, 0xB70AAA025EF75017L /* 521 */,
+ 0xD42309F03840B8E0L /* 522 */, 0x8EFC1AD035898579L /* 523 */,
+ 0x96C6920BE2B2ABC5L /* 524 */, 0x66AF4163375A9172L /* 525 */,
+ 0x2174ABDCCA7127FBL /* 526 */, 0xB33CCEA64A72FF41L /* 527 */,
+ 0xF04A4933083066A5L /* 528 */, 0x8D970ACDD7289AF5L /* 529 */,
+ 0x8F96E8E031C8C25EL /* 530 */, 0xF3FEC02276875D47L /* 531 */,
+ 0xEC7BF310056190DDL /* 532 */, 0xF5ADB0AEBB0F1491L /* 533 */,
+ 0x9B50F8850FD58892L /* 534 */, 0x4975488358B74DE8L /* 535 */,
+ 0xA3354FF691531C61L /* 536 */, 0x0702BBE481D2C6EEL /* 537 */,
+ 0x89FB24057DEDED98L /* 538 */, 0xAC3075138596E902L /* 539 */,
+ 0x1D2D3580172772EDL /* 540 */, 0xEB738FC28E6BC30DL /* 541 */,
+ 0x5854EF8F63044326L /* 542 */, 0x9E5C52325ADD3BBEL /* 543 */,
+ 0x90AA53CF325C4623L /* 544 */, 0xC1D24D51349DD067L /* 545 */,
+ 0x2051CFEEA69EA624L /* 546 */, 0x13220F0A862E7E4FL /* 547 */,
+ 0xCE39399404E04864L /* 548 */, 0xD9C42CA47086FCB7L /* 549 */,
+ 0x685AD2238A03E7CCL /* 550 */, 0x066484B2AB2FF1DBL /* 551 */,
+ 0xFE9D5D70EFBF79ECL /* 552 */, 0x5B13B9DD9C481854L /* 553 */,
+ 0x15F0D475ED1509ADL /* 554 */, 0x0BEBCD060EC79851L /* 555 */,
+ 0xD58C6791183AB7F8L /* 556 */, 0xD1187C5052F3EEE4L /* 557 */,
+ 0xC95D1192E54E82FFL /* 558 */, 0x86EEA14CB9AC6CA2L /* 559 */,
+ 0x3485BEB153677D5DL /* 560 */, 0xDD191D781F8C492AL /* 561 */,
+ 0xF60866BAA784EBF9L /* 562 */, 0x518F643BA2D08C74L /* 563 */,
+ 0x8852E956E1087C22L /* 564 */, 0xA768CB8DC410AE8DL /* 565 */,
+ 0x38047726BFEC8E1AL /* 566 */, 0xA67738B4CD3B45AAL /* 567 */,
+ 0xAD16691CEC0DDE19L /* 568 */, 0xC6D4319380462E07L /* 569 */,
+ 0xC5A5876D0BA61938L /* 570 */, 0x16B9FA1FA58FD840L /* 571 */,
+ 0x188AB1173CA74F18L /* 572 */, 0xABDA2F98C99C021FL /* 573 */,
+ 0x3E0580AB134AE816L /* 574 */, 0x5F3B05B773645ABBL /* 575 */,
+ 0x2501A2BE5575F2F6L /* 576 */, 0x1B2F74004E7E8BA9L /* 577 */,
+ 0x1CD7580371E8D953L /* 578 */, 0x7F6ED89562764E30L /* 579 */,
+ 0xB15926FF596F003DL /* 580 */, 0x9F65293DA8C5D6B9L /* 581 */,
+ 0x6ECEF04DD690F84CL /* 582 */, 0x4782275FFF33AF88L /* 583 */,
+ 0xE41433083F820801L /* 584 */, 0xFD0DFE409A1AF9B5L /* 585 */,
+ 0x4325A3342CDB396BL /* 586 */, 0x8AE77E62B301B252L /* 587 */,
+ 0xC36F9E9F6655615AL /* 588 */, 0x85455A2D92D32C09L /* 589 */,
+ 0xF2C7DEA949477485L /* 590 */, 0x63CFB4C133A39EBAL /* 591 */,
+ 0x83B040CC6EBC5462L /* 592 */, 0x3B9454C8FDB326B0L /* 593 */,
+ 0x56F56A9E87FFD78CL /* 594 */, 0x2DC2940D99F42BC6L /* 595 */,
+ 0x98F7DF096B096E2DL /* 596 */, 0x19A6E01E3AD852BFL /* 597 */,
+ 0x42A99CCBDBD4B40BL /* 598 */, 0xA59998AF45E9C559L /* 599 */,
+ 0x366295E807D93186L /* 600 */, 0x6B48181BFAA1F773L /* 601 */,
+ 0x1FEC57E2157A0A1DL /* 602 */, 0x4667446AF6201AD5L /* 603 */,
+ 0xE615EBCACFB0F075L /* 604 */, 0xB8F31F4F68290778L /* 605 */,
+ 0x22713ED6CE22D11EL /* 606 */, 0x3057C1A72EC3C93BL /* 607 */,
+ 0xCB46ACC37C3F1F2FL /* 608 */, 0xDBB893FD02AAF50EL /* 609 */,
+ 0x331FD92E600B9FCFL /* 610 */, 0xA498F96148EA3AD6L /* 611 */,
+ 0xA8D8426E8B6A83EAL /* 612 */, 0xA089B274B7735CDCL /* 613 */,
+ 0x87F6B3731E524A11L /* 614 */, 0x118808E5CBC96749L /* 615 */,
+ 0x9906E4C7B19BD394L /* 616 */, 0xAFED7F7E9B24A20CL /* 617 */,
+ 0x6509EADEEB3644A7L /* 618 */, 0x6C1EF1D3E8EF0EDEL /* 619 */,
+ 0xB9C97D43E9798FB4L /* 620 */, 0xA2F2D784740C28A3L /* 621 */,
+ 0x7B8496476197566FL /* 622 */, 0x7A5BE3E6B65F069DL /* 623 */,
+ 0xF96330ED78BE6F10L /* 624 */, 0xEEE60DE77A076A15L /* 625 */,
+ 0x2B4BEE4AA08B9BD0L /* 626 */, 0x6A56A63EC7B8894EL /* 627 */,
+ 0x02121359BA34FEF4L /* 628 */, 0x4CBF99F8283703FCL /* 629 */,
+ 0x398071350CAF30C8L /* 630 */, 0xD0A77A89F017687AL /* 631 */,
+ 0xF1C1A9EB9E423569L /* 632 */, 0x8C7976282DEE8199L /* 633 */,
+ 0x5D1737A5DD1F7ABDL /* 634 */, 0x4F53433C09A9FA80L /* 635 */,
+ 0xFA8B0C53DF7CA1D9L /* 636 */, 0x3FD9DCBC886CCB77L /* 637 */,
+ 0xC040917CA91B4720L /* 638 */, 0x7DD00142F9D1DCDFL /* 639 */,
+ 0x8476FC1D4F387B58L /* 640 */, 0x23F8E7C5F3316503L /* 641 */,
+ 0x032A2244E7E37339L /* 642 */, 0x5C87A5D750F5A74BL /* 643 */,
+ 0x082B4CC43698992EL /* 644 */, 0xDF917BECB858F63CL /* 645 */,
+ 0x3270B8FC5BF86DDAL /* 646 */, 0x10AE72BB29B5DD76L /* 647 */,
+ 0x576AC94E7700362BL /* 648 */, 0x1AD112DAC61EFB8FL /* 649 */,
+ 0x691BC30EC5FAA427L /* 650 */, 0xFF246311CC327143L /* 651 */,
+ 0x3142368E30E53206L /* 652 */, 0x71380E31E02CA396L /* 653 */,
+ 0x958D5C960AAD76F1L /* 654 */, 0xF8D6F430C16DA536L /* 655 */,
+ 0xC8FFD13F1BE7E1D2L /* 656 */, 0x7578AE66004DDBE1L /* 657 */,
+ 0x05833F01067BE646L /* 658 */, 0xBB34B5AD3BFE586DL /* 659 */,
+ 0x095F34C9A12B97F0L /* 660 */, 0x247AB64525D60CA8L /* 661 */,
+ 0xDCDBC6F3017477D1L /* 662 */, 0x4A2E14D4DECAD24DL /* 663 */,
+ 0xBDB5E6D9BE0A1EEBL /* 664 */, 0x2A7E70F7794301ABL /* 665 */,
+ 0xDEF42D8A270540FDL /* 666 */, 0x01078EC0A34C22C1L /* 667 */,
+ 0xE5DE511AF4C16387L /* 668 */, 0x7EBB3A52BD9A330AL /* 669 */,
+ 0x77697857AA7D6435L /* 670 */, 0x004E831603AE4C32L /* 671 */,
+ 0xE7A21020AD78E312L /* 672 */, 0x9D41A70C6AB420F2L /* 673 */,
+ 0x28E06C18EA1141E6L /* 674 */, 0xD2B28CBD984F6B28L /* 675 */,
+ 0x26B75F6C446E9D83L /* 676 */, 0xBA47568C4D418D7FL /* 677 */,
+ 0xD80BADBFE6183D8EL /* 678 */, 0x0E206D7F5F166044L /* 679 */,
+ 0xE258A43911CBCA3EL /* 680 */, 0x723A1746B21DC0BCL /* 681 */,
+ 0xC7CAA854F5D7CDD3L /* 682 */, 0x7CAC32883D261D9CL /* 683 */,
+ 0x7690C26423BA942CL /* 684 */, 0x17E55524478042B8L /* 685 */,
+ 0xE0BE477656A2389FL /* 686 */, 0x4D289B5E67AB2DA0L /* 687 */,
+ 0x44862B9C8FBBFD31L /* 688 */, 0xB47CC8049D141365L /* 689 */,
+ 0x822C1B362B91C793L /* 690 */, 0x4EB14655FB13DFD8L /* 691 */,
+ 0x1ECBBA0714E2A97BL /* 692 */, 0x6143459D5CDE5F14L /* 693 */,
+ 0x53A8FBF1D5F0AC89L /* 694 */, 0x97EA04D81C5E5B00L /* 695 */,
+ 0x622181A8D4FDB3F3L /* 696 */, 0xE9BCD341572A1208L /* 697 */,
+ 0x1411258643CCE58AL /* 698 */, 0x9144C5FEA4C6E0A4L /* 699 */,
+ 0x0D33D06565CF620FL /* 700 */, 0x54A48D489F219CA1L /* 701 */,
+ 0xC43E5EAC6D63C821L /* 702 */, 0xA9728B3A72770DAFL /* 703 */,
+ 0xD7934E7B20DF87EFL /* 704 */, 0xE35503B61A3E86E5L /* 705 */,
+ 0xCAE321FBC819D504L /* 706 */, 0x129A50B3AC60BFA6L /* 707 */,
+ 0xCD5E68EA7E9FB6C3L /* 708 */, 0xB01C90199483B1C7L /* 709 */,
+ 0x3DE93CD5C295376CL /* 710 */, 0xAED52EDF2AB9AD13L /* 711 */,
+ 0x2E60F512C0A07884L /* 712 */, 0xBC3D86A3E36210C9L /* 713 */,
+ 0x35269D9B163951CEL /* 714 */, 0x0C7D6E2AD0CDB5FAL /* 715 */,
+ 0x59E86297D87F5733L /* 716 */, 0x298EF221898DB0E7L /* 717 */,
+ 0x55000029D1A5AA7EL /* 718 */, 0x8BC08AE1B5061B45L /* 719 */,
+ 0xC2C31C2B6C92703AL /* 720 */, 0x94CC596BAF25EF42L /* 721 */,
+ 0x0A1D73DB22540456L /* 722 */, 0x04B6A0F9D9C4179AL /* 723 */,
+ 0xEFFDAFA2AE3D3C60L /* 724 */, 0xF7C8075BB49496C4L /* 725 */,
+ 0x9CC5C7141D1CD4E3L /* 726 */, 0x78BD1638218E5534L /* 727 */,
+ 0xB2F11568F850246AL /* 728 */, 0xEDFABCFA9502BC29L /* 729 */,
+ 0x796CE5F2DA23051BL /* 730 */, 0xAAE128B0DC93537CL /* 731 */,
+ 0x3A493DA0EE4B29AEL /* 732 */, 0xB5DF6B2C416895D7L /* 733 */,
+ 0xFCABBD25122D7F37L /* 734 */, 0x70810B58105DC4B1L /* 735 */,
+ 0xE10FDD37F7882A90L /* 736 */, 0x524DCAB5518A3F5CL /* 737 */,
+ 0x3C9E85878451255BL /* 738 */, 0x4029828119BD34E2L /* 739 */,
+ 0x74A05B6F5D3CECCBL /* 740 */, 0xB610021542E13ECAL /* 741 */,
+ 0x0FF979D12F59E2ACL /* 742 */, 0x6037DA27E4F9CC50L /* 743 */,
+ 0x5E92975A0DF1847DL /* 744 */, 0xD66DE190D3E623FEL /* 745 */,
+ 0x5032D6B87B568048L /* 746 */, 0x9A36B7CE8235216EL /* 747 */,
+ 0x80272A7A24F64B4AL /* 748 */, 0x93EFED8B8C6916F7L /* 749 */,
+ 0x37DDBFF44CCE1555L /* 750 */, 0x4B95DB5D4B99BD25L /* 751 */,
+ 0x92D3FDA169812FC0L /* 752 */, 0xFB1A4A9A90660BB6L /* 753 */,
+ 0x730C196946A4B9B2L /* 754 */, 0x81E289AA7F49DA68L /* 755 */,
+ 0x64669A0F83B1A05FL /* 756 */, 0x27B3FF7D9644F48BL /* 757 */,
+ 0xCC6B615C8DB675B3L /* 758 */, 0x674F20B9BCEBBE95L /* 759 */,
+ 0x6F31238275655982L /* 760 */, 0x5AE488713E45CF05L /* 761 */,
+ 0xBF619F9954C21157L /* 762 */, 0xEABAC46040A8EAE9L /* 763 */,
+ 0x454C6FE9F2C0C1CDL /* 764 */, 0x419CF6496412691CL /* 765 */,
+ 0xD3DC3BEF265B0F70L /* 766 */, 0x6D0E60F5C3578A9EL /* 767 */,
+ };
+
+ private static final long[] t4 = {
+ 0x5B0E608526323C55L /* 768 */, 0x1A46C1A9FA1B59F5L /* 769 */,
+ 0xA9E245A17C4C8FFAL /* 770 */, 0x65CA5159DB2955D7L /* 771 */,
+ 0x05DB0A76CE35AFC2L /* 772 */, 0x81EAC77EA9113D45L /* 773 */,
+ 0x528EF88AB6AC0A0DL /* 774 */, 0xA09EA253597BE3FFL /* 775 */,
+ 0x430DDFB3AC48CD56L /* 776 */, 0xC4B3A67AF45CE46FL /* 777 */,
+ 0x4ECECFD8FBE2D05EL /* 778 */, 0x3EF56F10B39935F0L /* 779 */,
+ 0x0B22D6829CD619C6L /* 780 */, 0x17FD460A74DF2069L /* 781 */,
+ 0x6CF8CC8E8510ED40L /* 782 */, 0xD6C824BF3A6ECAA7L /* 783 */,
+ 0x61243D581A817049L /* 784 */, 0x048BACB6BBC163A2L /* 785 */,
+ 0xD9A38AC27D44CC32L /* 786 */, 0x7FDDFF5BAAF410ABL /* 787 */,
+ 0xAD6D495AA804824BL /* 788 */, 0xE1A6A74F2D8C9F94L /* 789 */,
+ 0xD4F7851235DEE8E3L /* 790 */, 0xFD4B7F886540D893L /* 791 */,
+ 0x247C20042AA4BFDAL /* 792 */, 0x096EA1C517D1327CL /* 793 */,
+ 0xD56966B4361A6685L /* 794 */, 0x277DA5C31221057DL /* 795 */,
+ 0x94D59893A43ACFF7L /* 796 */, 0x64F0C51CCDC02281L /* 797 */,
+ 0x3D33BCC4FF6189DBL /* 798 */, 0xE005CB184CE66AF1L /* 799 */,
+ 0xFF5CCD1D1DB99BEAL /* 800 */, 0xB0B854A7FE42980FL /* 801 */,
+ 0x7BD46A6A718D4B9FL /* 802 */, 0xD10FA8CC22A5FD8CL /* 803 */,
+ 0xD31484952BE4BD31L /* 804 */, 0xC7FA975FCB243847L /* 805 */,
+ 0x4886ED1E5846C407L /* 806 */, 0x28CDDB791EB70B04L /* 807 */,
+ 0xC2B00BE2F573417FL /* 808 */, 0x5C9590452180F877L /* 809 */,
+ 0x7A6BDDFFF370EB00L /* 810 */, 0xCE509E38D6D9D6A4L /* 811 */,
+ 0xEBEB0F00647FA702L /* 812 */, 0x1DCC06CF76606F06L /* 813 */,
+ 0xE4D9F28BA286FF0AL /* 814 */, 0xD85A305DC918C262L /* 815 */,
+ 0x475B1D8732225F54L /* 816 */, 0x2D4FB51668CCB5FEL /* 817 */,
+ 0xA679B9D9D72BBA20L /* 818 */, 0x53841C0D912D43A5L /* 819 */,
+ 0x3B7EAA48BF12A4E8L /* 820 */, 0x781E0E47F22F1DDFL /* 821 */,
+ 0xEFF20CE60AB50973L /* 822 */, 0x20D261D19DFFB742L /* 823 */,
+ 0x16A12B03062A2E39L /* 824 */, 0x1960EB2239650495L /* 825 */,
+ 0x251C16FED50EB8B8L /* 826 */, 0x9AC0C330F826016EL /* 827 */,
+ 0xED152665953E7671L /* 828 */, 0x02D63194A6369570L /* 829 */,
+ 0x5074F08394B1C987L /* 830 */, 0x70BA598C90B25CE1L /* 831 */,
+ 0x794A15810B9742F6L /* 832 */, 0x0D5925E9FCAF8C6CL /* 833 */,
+ 0x3067716CD868744EL /* 834 */, 0x910AB077E8D7731BL /* 835 */,
+ 0x6A61BBDB5AC42F61L /* 836 */, 0x93513EFBF0851567L /* 837 */,
+ 0xF494724B9E83E9D5L /* 838 */, 0xE887E1985C09648DL /* 839 */,
+ 0x34B1D3C675370CFDL /* 840 */, 0xDC35E433BC0D255DL /* 841 */,
+ 0xD0AAB84234131BE0L /* 842 */, 0x08042A50B48B7EAFL /* 843 */,
+ 0x9997C4EE44A3AB35L /* 844 */, 0x829A7B49201799D0L /* 845 */,
+ 0x263B8307B7C54441L /* 846 */, 0x752F95F4FD6A6CA6L /* 847 */,
+ 0x927217402C08C6E5L /* 848 */, 0x2A8AB754A795D9EEL /* 849 */,
+ 0xA442F7552F72943DL /* 850 */, 0x2C31334E19781208L /* 851 */,
+ 0x4FA98D7CEAEE6291L /* 852 */, 0x55C3862F665DB309L /* 853 */,
+ 0xBD0610175D53B1F3L /* 854 */, 0x46FE6CB840413F27L /* 855 */,
+ 0x3FE03792DF0CFA59L /* 856 */, 0xCFE700372EB85E8FL /* 857 */,
+ 0xA7BE29E7ADBCE118L /* 858 */, 0xE544EE5CDE8431DDL /* 859 */,
+ 0x8A781B1B41F1873EL /* 860 */, 0xA5C94C78A0D2F0E7L /* 861 */,
+ 0x39412E2877B60728L /* 862 */, 0xA1265EF3AFC9A62CL /* 863 */,
+ 0xBCC2770C6A2506C5L /* 864 */, 0x3AB66DD5DCE1CE12L /* 865 */,
+ 0xE65499D04A675B37L /* 866 */, 0x7D8F523481BFD216L /* 867 */,
+ 0x0F6F64FCEC15F389L /* 868 */, 0x74EFBE618B5B13C8L /* 869 */,
+ 0xACDC82B714273E1DL /* 870 */, 0xDD40BFE003199D17L /* 871 */,
+ 0x37E99257E7E061F8L /* 872 */, 0xFA52626904775AAAL /* 873 */,
+ 0x8BBBF63A463D56F9L /* 874 */, 0xF0013F1543A26E64L /* 875 */,
+ 0xA8307E9F879EC898L /* 876 */, 0xCC4C27A4150177CCL /* 877 */,
+ 0x1B432F2CCA1D3348L /* 878 */, 0xDE1D1F8F9F6FA013L /* 879 */,
+ 0x606602A047A7DDD6L /* 880 */, 0xD237AB64CC1CB2C7L /* 881 */,
+ 0x9B938E7225FCD1D3L /* 882 */, 0xEC4E03708E0FF476L /* 883 */,
+ 0xFEB2FBDA3D03C12DL /* 884 */, 0xAE0BCED2EE43889AL /* 885 */,
+ 0x22CB8923EBFB4F43L /* 886 */, 0x69360D013CF7396DL /* 887 */,
+ 0x855E3602D2D4E022L /* 888 */, 0x073805BAD01F784CL /* 889 */,
+ 0x33E17A133852F546L /* 890 */, 0xDF4874058AC7B638L /* 891 */,
+ 0xBA92B29C678AA14AL /* 892 */, 0x0CE89FC76CFAADCDL /* 893 */,
+ 0x5F9D4E0908339E34L /* 894 */, 0xF1AFE9291F5923B9L /* 895 */,
+ 0x6E3480F60F4A265FL /* 896 */, 0xEEBF3A2AB29B841CL /* 897 */,
+ 0xE21938A88F91B4ADL /* 898 */, 0x57DFEFF845C6D3C3L /* 899 */,
+ 0x2F006B0BF62CAAF2L /* 900 */, 0x62F479EF6F75EE78L /* 901 */,
+ 0x11A55AD41C8916A9L /* 902 */, 0xF229D29084FED453L /* 903 */,
+ 0x42F1C27B16B000E6L /* 904 */, 0x2B1F76749823C074L /* 905 */,
+ 0x4B76ECA3C2745360L /* 906 */, 0x8C98F463B91691BDL /* 907 */,
+ 0x14BCC93CF1ADE66AL /* 908 */, 0x8885213E6D458397L /* 909 */,
+ 0x8E177DF0274D4711L /* 910 */, 0xB49B73B5503F2951L /* 911 */,
+ 0x10168168C3F96B6BL /* 912 */, 0x0E3D963B63CAB0AEL /* 913 */,
+ 0x8DFC4B5655A1DB14L /* 914 */, 0xF789F1356E14DE5CL /* 915 */,
+ 0x683E68AF4E51DAC1L /* 916 */, 0xC9A84F9D8D4B0FD9L /* 917 */,
+ 0x3691E03F52A0F9D1L /* 918 */, 0x5ED86E46E1878E80L /* 919 */,
+ 0x3C711A0E99D07150L /* 920 */, 0x5A0865B20C4E9310L /* 921 */,
+ 0x56FBFC1FE4F0682EL /* 922 */, 0xEA8D5DE3105EDF9BL /* 923 */,
+ 0x71ABFDB12379187AL /* 924 */, 0x2EB99DE1BEE77B9CL /* 925 */,
+ 0x21ECC0EA33CF4523L /* 926 */, 0x59A4D7521805C7A1L /* 927 */,
+ 0x3896F5EB56AE7C72L /* 928 */, 0xAA638F3DB18F75DCL /* 929 */,
+ 0x9F39358DABE9808EL /* 930 */, 0xB7DEFA91C00B72ACL /* 931 */,
+ 0x6B5541FD62492D92L /* 932 */, 0x6DC6DEE8F92E4D5BL /* 933 */,
+ 0x353F57ABC4BEEA7EL /* 934 */, 0x735769D6DA5690CEL /* 935 */,
+ 0x0A234AA642391484L /* 936 */, 0xF6F9508028F80D9DL /* 937 */,
+ 0xB8E319A27AB3F215L /* 938 */, 0x31AD9C1151341A4DL /* 939 */,
+ 0x773C22A57BEF5805L /* 940 */, 0x45C7561A07968633L /* 941 */,
+ 0xF913DA9E249DBE36L /* 942 */, 0xDA652D9B78A64C68L /* 943 */,
+ 0x4C27A97F3BC334EFL /* 944 */, 0x76621220E66B17F4L /* 945 */,
+ 0x967743899ACD7D0BL /* 946 */, 0xF3EE5BCAE0ED6782L /* 947 */,
+ 0x409F753600C879FCL /* 948 */, 0x06D09A39B5926DB6L /* 949 */,
+ 0x6F83AEB0317AC588L /* 950 */, 0x01E6CA4A86381F21L /* 951 */,
+ 0x66FF3462D19F3025L /* 952 */, 0x72207C24DDFD3BFBL /* 953 */,
+ 0x4AF6B6D3E2ECE2EBL /* 954 */, 0x9C994DBEC7EA08DEL /* 955 */,
+ 0x49ACE597B09A8BC4L /* 956 */, 0xB38C4766CF0797BAL /* 957 */,
+ 0x131B9373C57C2A75L /* 958 */, 0xB1822CCE61931E58L /* 959 */,
+ 0x9D7555B909BA1C0CL /* 960 */, 0x127FAFDD937D11D2L /* 961 */,
+ 0x29DA3BADC66D92E4L /* 962 */, 0xA2C1D57154C2ECBCL /* 963 */,
+ 0x58C5134D82F6FE24L /* 964 */, 0x1C3AE3515B62274FL /* 965 */,
+ 0xE907C82E01CB8126L /* 966 */, 0xF8ED091913E37FCBL /* 967 */,
+ 0x3249D8F9C80046C9L /* 968 */, 0x80CF9BEDE388FB63L /* 969 */,
+ 0x1881539A116CF19EL /* 970 */, 0x5103F3F76BD52457L /* 971 */,
+ 0x15B7E6F5AE47F7A8L /* 972 */, 0xDBD7C6DED47E9CCFL /* 973 */,
+ 0x44E55C410228BB1AL /* 974 */, 0xB647D4255EDB4E99L /* 975 */,
+ 0x5D11882BB8AAFC30L /* 976 */, 0xF5098BBB29D3212AL /* 977 */,
+ 0x8FB5EA14E90296B3L /* 978 */, 0x677B942157DD025AL /* 979 */,
+ 0xFB58E7C0A390ACB5L /* 980 */, 0x89D3674C83BD4A01L /* 981 */,
+ 0x9E2DA4DF4BF3B93BL /* 982 */, 0xFCC41E328CAB4829L /* 983 */,
+ 0x03F38C96BA582C52L /* 984 */, 0xCAD1BDBD7FD85DB2L /* 985 */,
+ 0xBBB442C16082AE83L /* 986 */, 0xB95FE86BA5DA9AB0L /* 987 */,
+ 0xB22E04673771A93FL /* 988 */, 0x845358C9493152D8L /* 989 */,
+ 0xBE2A488697B4541EL /* 990 */, 0x95A2DC2DD38E6966L /* 991 */,
+ 0xC02C11AC923C852BL /* 992 */, 0x2388B1990DF2A87BL /* 993 */,
+ 0x7C8008FA1B4F37BEL /* 994 */, 0x1F70D0C84D54E503L /* 995 */,
+ 0x5490ADEC7ECE57D4L /* 996 */, 0x002B3C27D9063A3AL /* 997 */,
+ 0x7EAEA3848030A2BFL /* 998 */, 0xC602326DED2003C0L /* 999 */,
+ 0x83A7287D69A94086L /* 1000 */, 0xC57A5FCB30F57A8AL /* 1001 */,
+ 0xB56844E479EBE779L /* 1002 */, 0xA373B40F05DCBCE9L /* 1003 */,
+ 0xD71A786E88570EE2L /* 1004 */, 0x879CBACDBDE8F6A0L /* 1005 */,
+ 0x976AD1BCC164A32FL /* 1006 */, 0xAB21E25E9666D78BL /* 1007 */,
+ 0x901063AAE5E5C33CL /* 1008 */, 0x9818B34448698D90L /* 1009 */,
+ 0xE36487AE3E1E8ABBL /* 1010 */, 0xAFBDF931893BDCB4L /* 1011 */,
+ 0x6345A0DC5FBBD519L /* 1012 */, 0x8628FE269B9465CAL /* 1013 */,
+ 0x1E5D01603F9C51ECL /* 1014 */, 0x4DE44006A15049B7L /* 1015 */,
+ 0xBF6C70E5F776CBB1L /* 1016 */, 0x411218F2EF552BEDL /* 1017 */,
+ 0xCB0C0708705A36A3L /* 1018 */, 0xE74D14754F986044L /* 1019 */,
+ 0xCD56D9430EA8280EL /* 1020 */, 0xC12591D7535F5065L /* 1021 */,
+ 0xC83223F1720AEF96L /* 1022 */, 0xC3A0396F7363A51FL /* 1023 */
+ };
+
+ private static final int DIGEST_LENGTH = 24;
+
+ //
+ // registers
+ //
+ private long a, b, c;
+ private long byteCount;
+
+ //
+ // buffers
+ //
+ private byte[] buf = new byte[8];
+ private int bOff = 0;
+
+ private long[] x = new long[8];
+ private int xOff = 0;
+
+ /**
+ * Standard constructor
+ */
+ public TigerDigest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public TigerDigest(TigerDigest t)
+ {
+ this.reset(t);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Tiger";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ private void processWord(
+ byte[] b,
+ int off)
+ {
+ x[xOff++] = ((long)(b[off + 7] & 0xff) << 56)
+ | ((long)(b[off + 6] & 0xff) << 48)
+ | ((long)(b[off + 5] & 0xff) << 40)
+ | ((long)(b[off + 4] & 0xff) << 32)
+ | ((long)(b[off + 3] & 0xff) << 24)
+ | ((long)(b[off + 2] & 0xff) << 16)
+ | ((long)(b[off + 1] & 0xff) << 8)
+ | ((b[off + 0] & 0xff));
+
+ if (xOff == x.length)
+ {
+ processBlock();
+ }
+
+ bOff = 0;
+ }
+
+ public void update(
+ byte in)
+ {
+ buf[bOff++] = in;
+
+ if (bOff == buf.length)
+ {
+ processWord(buf, 0);
+ }
+
+ byteCount++;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((bOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > 8)
+ {
+ processWord(in, inOff);
+
+ inOff += 8;
+ len -= 8;
+ byteCount += 8;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+ }
+
+ private void roundABC(
+ long x,
+ long mul)
+ {
+ c ^= x ;
+ a -= t1[(int)c & 0xff] ^ t2[(int)(c >> 16) & 0xff]
+ ^ t3[(int)(c >> 32) & 0xff] ^ t4[(int)(c >> 48) & 0xff];
+ b += t4[(int)(c >> 8) & 0xff] ^ t3[(int)(c >> 24) & 0xff]
+ ^ t2[(int)(c >> 40) & 0xff] ^ t1[(int)(c >> 56) & 0xff];
+ b *= mul;
+ }
+
+ private void roundBCA(
+ long x,
+ long mul)
+ {
+ a ^= x ;
+ b -= t1[(int)a & 0xff] ^ t2[(int)(a >> 16) & 0xff]
+ ^ t3[(int)(a >> 32) & 0xff] ^ t4[(int)(a >> 48) & 0xff];
+ c += t4[(int)(a >> 8) & 0xff] ^ t3[(int)(a >> 24) & 0xff]
+ ^ t2[(int)(a >> 40) & 0xff] ^ t1[(int)(a >> 56) & 0xff];
+ c *= mul;
+ }
+
+ private void roundCAB(
+ long x,
+ long mul)
+ {
+ b ^= x ;
+ c -= t1[(int)b & 0xff] ^ t2[(int)(b >> 16) & 0xff]
+ ^ t3[(int)(b >> 32) & 0xff] ^ t4[(int)(b >> 48) & 0xff];
+ a += t4[(int)(b >> 8) & 0xff] ^ t3[(int)(b >> 24) & 0xff]
+ ^ t2[(int)(b >> 40) & 0xff] ^ t1[(int)(b >> 56) & 0xff];
+ a *= mul;
+ }
+
+ private void keySchedule()
+ {
+ x[0] -= x[7] ^ 0xA5A5A5A5A5A5A5A5L;
+ x[1] ^= x[0];
+ x[2] += x[1];
+ x[3] -= x[2] ^ ((~x[1]) << 19);
+ x[4] ^= x[3];
+ x[5] += x[4];
+ x[6] -= x[5] ^ ((~x[4]) >>> 23);
+ x[7] ^= x[6];
+ x[0] += x[7];
+ x[1] -= x[0] ^ ((~x[7]) << 19);
+ x[2] ^= x[1];
+ x[3] += x[2];
+ x[4] -= x[3] ^ ((~x[2]) >>> 23);
+ x[5] ^= x[4];
+ x[6] += x[5];
+ x[7] -= x[6] ^ 0x0123456789ABCDEFL;
+ }
+
+ private void processBlock()
+ {
+ //
+ // save abc
+ //
+ long aa = a;
+ long bb = b;
+ long cc = c;
+
+ //
+ // rounds and schedule
+ //
+ roundABC(x[0], 5);
+ roundBCA(x[1], 5);
+ roundCAB(x[2], 5);
+ roundABC(x[3], 5);
+ roundBCA(x[4], 5);
+ roundCAB(x[5], 5);
+ roundABC(x[6], 5);
+ roundBCA(x[7], 5);
+
+ keySchedule();
+
+ roundCAB(x[0], 7);
+ roundABC(x[1], 7);
+ roundBCA(x[2], 7);
+ roundCAB(x[3], 7);
+ roundABC(x[4], 7);
+ roundBCA(x[5], 7);
+ roundCAB(x[6], 7);
+ roundABC(x[7], 7);
+
+ keySchedule();
+
+ roundBCA(x[0], 9);
+ roundCAB(x[1], 9);
+ roundABC(x[2], 9);
+ roundBCA(x[3], 9);
+ roundCAB(x[4], 9);
+ roundABC(x[5], 9);
+ roundBCA(x[6], 9);
+ roundCAB(x[7], 9);
+
+ //
+ // feed forward
+ //
+ a ^= aa;
+ b -= bb;
+ c += cc;
+
+ //
+ // clear the x buffer
+ //
+ xOff = 0;
+ for (int i = 0; i != x.length; i++)
+ {
+ x[i] = 0;
+ }
+ }
+
+ public void unpackWord(
+ long r,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 7] = (byte)(r >> 56);
+ out[outOff + 6] = (byte)(r >> 48);
+ out[outOff + 5] = (byte)(r >> 40);
+ out[outOff + 4] = (byte)(r >> 32);
+ out[outOff + 3] = (byte)(r >> 24);
+ out[outOff + 2] = (byte)(r >> 16);
+ out[outOff + 1] = (byte)(r >> 8);
+ out[outOff] = (byte)r;
+ }
+
+ private void processLength(
+ long bitLength)
+ {
+ x[7] = bitLength;
+ }
+
+ private void finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ update((byte)0x01);
+
+ while (bOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processLength(bitLength);
+
+ processBlock();
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(a, out, outOff);
+ unpackWord(b, out, outOff + 8);
+ unpackWord(c, out, outOff + 16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ a = 0x0123456789ABCDEFL;
+ b = 0xFEDCBA9876543210L;
+ c = 0xF096A5B4C3B2E187L;
+
+ xOff = 0;
+ for (int i = 0; i != x.length; i++)
+ {
+ x[i] = 0;
+ }
+
+ bOff = 0;
+ for (int i = 0; i != buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ byteCount = 0;
+ }
+
+ public int getByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+
+ public Memoable copy()
+ {
+ return new TigerDigest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ TigerDigest t = (TigerDigest)other;
+
+ a = t.a;
+ b = t.b;
+ c = t.c;
+
+ System.arraycopy(t.x, 0, x, 0, t.x.length);
+ xOff = t.xOff;
+
+ System.arraycopy(t.buf, 0, buf, 0, t.buf.length);
+ bOff = t.bOff;
+
+ byteCount = t.byteCount;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/WhirlpoolDigest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/WhirlpoolDigest.java
new file mode 100644
index 000000000..8385f736c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/digests/WhirlpoolDigest.java
@@ -0,0 +1,409 @@
+package org.spongycastle.crypto.digests;
+
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Memoable;
+
+
+/**
+ * Implementation of WhirlpoolDigest, based on Java source published by Barreto
+ * and Rijmen.
+ *
+ */
+public final class WhirlpoolDigest
+ implements ExtendedDigest, Memoable
+{
+ private static final int BYTE_LENGTH = 64;
+
+ private static final int DIGEST_LENGTH_BYTES = 512 / 8;
+ private static final int ROUNDS = 10;
+ private static final int REDUCTION_POLYNOMIAL = 0x011d; // 2^8 + 2^4 + 2^3 + 2 + 1;
+
+ private static final int[] SBOX = {
+ 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52,
+ 0x60, 0xbc, 0x9b, 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, 0xfe, 0x57,
+ 0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85,
+ 0xbd, 0x5d, 0x10, 0xf4, 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, 0xd8,
+ 0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33,
+ 0x63, 0x02, 0xaa, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xf2, 0xe3, 0x5b, 0x88, 0x9a, 0x26, 0x32, 0xb0,
+ 0xe9, 0x0f, 0xd5, 0x80, 0xbe, 0xcd, 0x34, 0x48, 0xff, 0x7a, 0x90, 0x5f, 0x20, 0x68, 0x1a, 0xae,
+ 0xb4, 0x54, 0x93, 0x22, 0x64, 0xf1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xec, 0xdb, 0xa1, 0x8d, 0x3d,
+ 0x97, 0x00, 0xcf, 0x2b, 0x76, 0x82, 0xd6, 0x1b, 0xb5, 0xaf, 0x6a, 0x50, 0x45, 0xf3, 0x30, 0xef,
+ 0x3f, 0x55, 0xa2, 0xea, 0x65, 0xba, 0x2f, 0xc0, 0xde, 0x1c, 0xfd, 0x4d, 0x92, 0x75, 0x06, 0x8a,
+ 0xb2, 0xe6, 0x0e, 0x1f, 0x62, 0xd4, 0xa8, 0x96, 0xf9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c,
+ 0x5e, 0x78, 0x38, 0x8c, 0xd1, 0xa5, 0xe2, 0x61, 0xb3, 0x21, 0x9c, 0x1e, 0x43, 0xc7, 0xfc, 0x04,
+ 0x51, 0x99, 0x6d, 0x0d, 0xfa, 0xdf, 0x7e, 0x24, 0x3b, 0xab, 0xce, 0x11, 0x8f, 0x4e, 0xb7, 0xeb,
+ 0x3c, 0x81, 0x94, 0xf7, 0xb9, 0x13, 0x2c, 0xd3, 0xe7, 0x6e, 0xc4, 0x03, 0x56, 0x44, 0x7f, 0xa9,
+ 0x2a, 0xbb, 0xc1, 0x53, 0xdc, 0x0b, 0x9d, 0x6c, 0x31, 0x74, 0xf6, 0x46, 0xac, 0x89, 0x14, 0xe1,
+ 0x16, 0x3a, 0x69, 0x09, 0x70, 0xb6, 0xd0, 0xed, 0xcc, 0x42, 0x98, 0xa4, 0x28, 0x5c, 0xf8, 0x86
+ };
+
+ private static final long[] C0 = new long[256];
+ private static final long[] C1 = new long[256];
+ private static final long[] C2 = new long[256];
+ private static final long[] C3 = new long[256];
+ private static final long[] C4 = new long[256];
+ private static final long[] C5 = new long[256];
+ private static final long[] C6 = new long[256];
+ private static final long[] C7 = new long[256];
+
+ private final long[] _rc = new long[ROUNDS + 1];
+
+ public WhirlpoolDigest()
+ {
+ for (int i = 0; i < 256; i++)
+ {
+ int v1 = SBOX[i];
+ int v2 = maskWithReductionPolynomial(v1 << 1);
+ int v4 = maskWithReductionPolynomial(v2 << 1);
+ int v5 = v4 ^ v1;
+ int v8 = maskWithReductionPolynomial(v4 << 1);
+ int v9 = v8 ^ v1;
+
+ C0[i] = packIntoLong(v1, v1, v4, v1, v8, v5, v2, v9);
+ C1[i] = packIntoLong(v9, v1, v1, v4, v1, v8, v5, v2);
+ C2[i] = packIntoLong(v2, v9, v1, v1, v4, v1, v8, v5);
+ C3[i] = packIntoLong(v5, v2, v9, v1, v1, v4, v1, v8);
+ C4[i] = packIntoLong(v8, v5, v2, v9, v1, v1, v4, v1);
+ C5[i] = packIntoLong(v1, v8, v5, v2, v9, v1, v1, v4);
+ C6[i] = packIntoLong(v4, v1, v8, v5, v2, v9, v1, v1);
+ C7[i] = packIntoLong(v1, v4, v1, v8, v5, v2, v9, v1);
+
+ }
+
+ _rc[0] = 0L;
+ for (int r = 1; r <= ROUNDS; r++)
+ {
+ int i = 8 * (r - 1);
+ _rc[r] = (C0[i ] & 0xff00000000000000L) ^
+ (C1[i + 1] & 0x00ff000000000000L) ^
+ (C2[i + 2] & 0x0000ff0000000000L) ^
+ (C3[i + 3] & 0x000000ff00000000L) ^
+ (C4[i + 4] & 0x00000000ff000000L) ^
+ (C5[i + 5] & 0x0000000000ff0000L) ^
+ (C6[i + 6] & 0x000000000000ff00L) ^
+ (C7[i + 7] & 0x00000000000000ffL);
+ }
+
+ }
+
+ private long packIntoLong(int b7, int b6, int b5, int b4, int b3, int b2, int b1, int b0)
+ {
+ return
+ ((long)b7 << 56) ^
+ ((long)b6 << 48) ^
+ ((long)b5 << 40) ^
+ ((long)b4 << 32) ^
+ ((long)b3 << 24) ^
+ ((long)b2 << 16) ^
+ ((long)b1 << 8) ^
+ b0;
+ }
+
+ /*
+ * int's are used to prevent sign extension. The values that are really being used are
+ * actually just 0..255
+ */
+ private int maskWithReductionPolynomial(int input)
+ {
+ int rv = input;
+ if (rv >= 0x100L) // high bit set
+ {
+ rv ^= REDUCTION_POLYNOMIAL; // reduced by the polynomial
+ }
+ return rv;
+ }
+
+ // --------------------------------------------------------------------------------------//
+
+ // -- buffer information --
+ private static final int BITCOUNT_ARRAY_SIZE = 32;
+ private byte[] _buffer = new byte[64];
+ private int _bufferPos = 0;
+ private short[] _bitCount = new short[BITCOUNT_ARRAY_SIZE];
+
+ // -- internal hash state --
+ private long[] _hash = new long[8];
+ private long[] _K = new long[8]; // the round key
+ private long[] _L = new long[8];
+ private long[] _block = new long[8]; // mu (buffer)
+ private long[] _state = new long[8]; // the current "cipher" state
+
+
+
+ /**
+ * Copy constructor. This will copy the state of the provided message
+ * digest.
+ */
+ public WhirlpoolDigest(WhirlpoolDigest originalDigest)
+ {
+ reset(originalDigest);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Whirlpool";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH_BYTES;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ // sets out[outOff] .. out[outOff+DIGEST_LENGTH_BYTES]
+ finish();
+
+ for (int i = 0; i < 8; i++)
+ {
+ convertLongToByteArray(_hash[i], out, outOff + (i * 8));
+ }
+
+ reset();
+ return getDigestSize();
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ // set variables to null, blank, whatever
+ _bufferPos = 0;
+ Arrays.fill(_bitCount, (short)0);
+ Arrays.fill(_buffer, (byte)0);
+ Arrays.fill(_hash, 0);
+ Arrays.fill(_K, 0);
+ Arrays.fill(_L, 0);
+ Arrays.fill(_block, 0);
+ Arrays.fill(_state, 0);
+ }
+
+ // this takes a buffer of information and fills the block
+ private void processFilledBuffer(byte[] in, int inOff)
+ {
+ // copies into the block...
+ for (int i = 0; i < _state.length; i++)
+ {
+ _block[i] = bytesToLongFromBuffer(_buffer, i * 8);
+ }
+ processBlock();
+ _bufferPos = 0;
+ Arrays.fill(_buffer, (byte)0);
+ }
+
+ private long bytesToLongFromBuffer(byte[] buffer, int startPos)
+ {
+ long rv = (((buffer[startPos + 0] & 0xffL) << 56) |
+ ((buffer[startPos + 1] & 0xffL) << 48) |
+ ((buffer[startPos + 2] & 0xffL) << 40) |
+ ((buffer[startPos + 3] & 0xffL) << 32) |
+ ((buffer[startPos + 4] & 0xffL) << 24) |
+ ((buffer[startPos + 5] & 0xffL) << 16) |
+ ((buffer[startPos + 6] & 0xffL) << 8) |
+ ((buffer[startPos + 7]) & 0xffL));
+
+ return rv;
+ }
+
+ private void convertLongToByteArray(long inputLong, byte[] outputArray, int offSet)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ outputArray[offSet + i] = (byte)((inputLong >> (56 - (i * 8))) & 0xff);
+ }
+ }
+
+ protected void processBlock()
+ {
+ // buffer contents have been transferred to the _block[] array via
+ // processFilledBuffer
+
+ // compute and apply K^0
+ for (int i = 0; i < 8; i++)
+ {
+ _state[i] = _block[i] ^ (_K[i] = _hash[i]);
+ }
+
+ // iterate over the rounds
+ for (int round = 1; round <= ROUNDS; round++)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ _L[i] = 0;
+ _L[i] ^= C0[(int)(_K[(i - 0) & 7] >>> 56) & 0xff];
+ _L[i] ^= C1[(int)(_K[(i - 1) & 7] >>> 48) & 0xff];
+ _L[i] ^= C2[(int)(_K[(i - 2) & 7] >>> 40) & 0xff];
+ _L[i] ^= C3[(int)(_K[(i - 3) & 7] >>> 32) & 0xff];
+ _L[i] ^= C4[(int)(_K[(i - 4) & 7] >>> 24) & 0xff];
+ _L[i] ^= C5[(int)(_K[(i - 5) & 7] >>> 16) & 0xff];
+ _L[i] ^= C6[(int)(_K[(i - 6) & 7] >>> 8) & 0xff];
+ _L[i] ^= C7[(int)(_K[(i - 7) & 7]) & 0xff];
+ }
+
+ System.arraycopy(_L, 0, _K, 0, _K.length);
+
+ _K[0] ^= _rc[round];
+
+ // apply the round transformation
+ for (int i = 0; i < 8; i++)
+ {
+ _L[i] = _K[i];
+
+ _L[i] ^= C0[(int)(_state[(i - 0) & 7] >>> 56) & 0xff];
+ _L[i] ^= C1[(int)(_state[(i - 1) & 7] >>> 48) & 0xff];
+ _L[i] ^= C2[(int)(_state[(i - 2) & 7] >>> 40) & 0xff];
+ _L[i] ^= C3[(int)(_state[(i - 3) & 7] >>> 32) & 0xff];
+ _L[i] ^= C4[(int)(_state[(i - 4) & 7] >>> 24) & 0xff];
+ _L[i] ^= C5[(int)(_state[(i - 5) & 7] >>> 16) & 0xff];
+ _L[i] ^= C6[(int)(_state[(i - 6) & 7] >>> 8) & 0xff];
+ _L[i] ^= C7[(int)(_state[(i - 7) & 7]) & 0xff];
+ }
+
+ // save the current state
+ System.arraycopy(_L, 0, _state, 0, _state.length);
+ }
+
+ // apply Miuaguchi-Preneel compression
+ for (int i = 0; i < 8; i++)
+ {
+ _hash[i] ^= _state[i] ^ _block[i];
+ }
+
+ }
+
+ public void update(byte in)
+ {
+ _buffer[_bufferPos] = in;
+
+ //System.out.println("adding to buffer = "+_buffer[_bufferPos]);
+
+ ++_bufferPos;
+
+ if (_bufferPos == _buffer.length)
+ {
+ processFilledBuffer(_buffer, 0);
+ }
+
+ increment();
+ }
+
+ /*
+ * increment() can be implemented in this way using 2 arrays or
+ * by having some temporary variables that are used to set the
+ * value provided by EIGHT[i] and carry within the loop.
+ *
+ * not having done any timing, this seems likely to be faster
+ * at the slight expense of 32*(sizeof short) bytes
+ */
+ private static final short[] EIGHT = new short[BITCOUNT_ARRAY_SIZE];
+ static
+ {
+ EIGHT[BITCOUNT_ARRAY_SIZE - 1] = 8;
+ }
+
+ private void increment()
+ {
+ int carry = 0;
+ for (int i = _bitCount.length - 1; i >= 0; i--)
+ {
+ int sum = (_bitCount[i] & 0xff) + EIGHT[i] + carry;
+
+ carry = sum >>> 8;
+ _bitCount[i] = (short)(sum & 0xff);
+ }
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ while (len > 0)
+ {
+ update(in[inOff]);
+ ++inOff;
+ --len;
+ }
+
+ }
+
+ private void finish()
+ {
+ /*
+ * this makes a copy of the current bit length. at the expense of an
+ * object creation of 32 bytes rather than providing a _stopCounting
+ * boolean which was the alternative I could think of.
+ */
+ byte[] bitLength = copyBitLength();
+
+ _buffer[_bufferPos++] |= 0x80;
+
+ if (_bufferPos == _buffer.length)
+ {
+ processFilledBuffer(_buffer, 0);
+ }
+
+ /*
+ * Final block contains
+ * [ ... data .... ][0][0][0][ length ]
+ *
+ * if [ length ] cannot fit. Need to create a new block.
+ */
+ if (_bufferPos > 32)
+ {
+ while (_bufferPos != 0)
+ {
+ update((byte)0);
+ }
+ }
+
+ while (_bufferPos <= 32)
+ {
+ update((byte)0);
+ }
+
+ // copy the length information to the final 32 bytes of the
+ // 64 byte block....
+ System.arraycopy(bitLength, 0, _buffer, 32, bitLength.length);
+
+ processFilledBuffer(_buffer, 0);
+ }
+
+ private byte[] copyBitLength()
+ {
+ byte[] rv = new byte[BITCOUNT_ARRAY_SIZE];
+ for (int i = 0; i < rv.length; i++)
+ {
+ rv[i] = (byte)(_bitCount[i] & 0xff);
+ }
+ return rv;
+ }
+
+ public int getByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+
+ public Memoable copy()
+ {
+ return new WhirlpoolDigest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ WhirlpoolDigest originalDigest = (WhirlpoolDigest)other;
+
+ System.arraycopy(originalDigest._rc, 0, _rc, 0, _rc.length);
+
+ System.arraycopy(originalDigest._buffer, 0, _buffer, 0, _buffer.length);
+
+ this._bufferPos = originalDigest._bufferPos;
+ System.arraycopy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.length);
+
+ // -- internal hash state --
+ System.arraycopy(originalDigest._hash, 0, _hash, 0, _hash.length);
+ System.arraycopy(originalDigest._K, 0, _K, 0, _K.length);
+ System.arraycopy(originalDigest._L, 0, _L, 0, _L.length);
+ System.arraycopy(originalDigest._block, 0, _block, 0, _block.length);
+ System.arraycopy(originalDigest._state, 0, _state, 0, _state.length);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECDecryptor.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECDecryptor.java
new file mode 100644
index 000000000..23bf6f802
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECDecryptor.java
@@ -0,0 +1,11 @@
+package org.spongycastle.crypto.ec;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.math.ec.ECPoint;
+
+public interface ECDecryptor
+{
+ void init(CipherParameters params);
+
+ ECPoint decrypt(ECPair cipherText);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECElGamalDecryptor.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECElGamalDecryptor.java
new file mode 100644
index 000000000..31bd6381c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECElGamalDecryptor.java
@@ -0,0 +1,48 @@
+package org.spongycastle.crypto.ec;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.math.ec.ECPoint;
+
+/**
+ * this does your basic decryption ElGamal style using EC
+ */
+public class ECElGamalDecryptor
+ implements ECDecryptor
+{
+ private ECPrivateKeyParameters key;
+
+ /**
+ * initialise the decryptor.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (!(param instanceof ECPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPrivateKeyParameters are required for decryption.");
+ }
+
+ this.key = (ECPrivateKeyParameters)param;
+ }
+
+ /**
+ * Decrypt an EC pair producing the original EC point.
+ *
+ * @param pair the EC point pair to process.
+ * @return the result of the Elgamal process.
+ */
+ public ECPoint decrypt(ECPair pair)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECElGamalDecryptor not initialised");
+ }
+
+ ECPoint tmp = pair.getX().multiply(key.getD());
+
+ return pair.getY().add(tmp.negate()).normalize();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECElGamalEncryptor.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECElGamalEncryptor.java
new file mode 100644
index 000000000..c31a4308b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECElGamalEncryptor.java
@@ -0,0 +1,73 @@
+package org.spongycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.math.ec.ECPoint;
+
+/**
+ * this does your basic ElGamal encryption algorithm using EC
+ */
+public class ECElGamalEncryptor
+ implements ECEncryptor
+{
+ private ECPublicKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the encryptor.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ if (!(p.getParameters() instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for encryption.");
+ }
+ this.key = (ECPublicKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for encryption.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Process a single EC point using the basic ElGamal algorithm.
+ *
+ * @param point the EC point to process.
+ * @return the result of the Elgamal process.
+ */
+ public ECPair encrypt(ECPoint point)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECElGamalEncryptor not initialised");
+ }
+
+ BigInteger n = key.getParameters().getN();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(point);
+
+ return new ECPair(gamma.normalize(), phi.normalize());
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECEncryptor.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECEncryptor.java
new file mode 100644
index 000000000..ee63720b2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECEncryptor.java
@@ -0,0 +1,11 @@
+package org.spongycastle.crypto.ec;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.math.ec.ECPoint;
+
+public interface ECEncryptor
+{
+ void init(CipherParameters params);
+
+ ECPair encrypt(ECPoint point);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECFixedTransform.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECFixedTransform.java
new file mode 100644
index 000000000..7b35f1a10
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECFixedTransform.java
@@ -0,0 +1,71 @@
+package org.spongycastle.crypto.ec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.math.ec.ECPoint;
+
+/**
+ * this transforms the original randomness used for an ElGamal encryption by a fixed value.
+ */
+public class ECFixedTransform
+ implements ECPairFactorTransform
+{
+ private ECPublicKeyParameters key;
+
+ private BigInteger k;
+
+ public ECFixedTransform(BigInteger k)
+ {
+ this.k = k;
+ }
+
+ /**
+ * initialise the underlying EC ElGamal engine.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for fixed transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ }
+
+ /**
+ * Transform an existing cipher test pair using the ElGamal algorithm. Note: it is assumed this
+ * transform has been initialised with the same public key that was used to create the original
+ * cipher text.
+ *
+ * @param cipherText the EC point to process.
+ * @return returns a new ECPair representing the result of the process.
+ */
+ public ECPair transform(ECPair cipherText)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECFixedTransform not initialised");
+ }
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+
+ return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+ }
+
+ /**
+ * Return the last transform value used by the transform
+ *
+ * @return a BigInteger representing k value.
+ */
+ public BigInteger getTransformValue()
+ {
+ return k;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECNewPublicKeyTransform.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECNewPublicKeyTransform.java
new file mode 100644
index 000000000..d8b1203b5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECNewPublicKeyTransform.java
@@ -0,0 +1,74 @@
+package org.spongycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.math.ec.ECPoint;
+
+/**
+ * this does your basic Elgamal encryption algorithm using EC
+ */
+public class ECNewPublicKeyTransform
+ implements ECPairTransform
+{
+ private ECPublicKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the EC Elgamal engine.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ if (!(p.getParameters() instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new public key transform.");
+ }
+ this.key = (ECPublicKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new public key transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Transform an existing cipher test pair using the ElGamal algorithm. Note: the input cipherText will
+ * need to be preserved in order to complete the transformation to the new public key.
+ *
+ * @param cipherText the EC point to process.
+ * @return returns a new ECPair representing the result of the process.
+ */
+ public ECPair transform(ECPair cipherText)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECNewPublicKeyTransform not initialised");
+ }
+
+ BigInteger n = key.getParameters().getN();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+
+ return new ECPair(gamma.normalize(), phi.normalize());
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECNewRandomnessTransform.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECNewRandomnessTransform.java
new file mode 100644
index 000000000..9b70fc467
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECNewRandomnessTransform.java
@@ -0,0 +1,90 @@
+package org.spongycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.math.ec.ECPoint;
+
+/**
+ * this transforms the original randomness used for an ElGamal encryption.
+ */
+public class ECNewRandomnessTransform
+ implements ECPairFactorTransform
+{
+ private ECPublicKeyParameters key;
+ private SecureRandom random;
+
+ private BigInteger lastK;
+
+ /**
+ * initialise the underlying EC ElGamal engine.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ if (!(p.getParameters() instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new randomness transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new randomness transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Transform an existing cipher test pair using the ElGamal algorithm. Note: it is assumed this
+ * transform has been initialised with the same public key that was used to create the original
+ * cipher text.
+ *
+ * @param cipherText the EC point to process.
+ * @return returns a new ECPair representing the result of the process.
+ */
+ public ECPair transform(ECPair cipherText)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECNewRandomnessTransform not initialised");
+ }
+
+ BigInteger n = key.getParameters().getN();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+
+ lastK = k;
+
+ return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+ }
+
+ /**
+ * Return the last random value generated for a transform
+ *
+ * @return a BigInteger representing the last random value.
+ */
+ public BigInteger getTransformValue()
+ {
+ return lastK;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPair.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPair.java
new file mode 100644
index 000000000..1b8d247db
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPair.java
@@ -0,0 +1,40 @@
+package org.spongycastle.crypto.ec;
+
+import org.spongycastle.math.ec.ECPoint;
+
+public class ECPair
+{
+ private final ECPoint x;
+ private final ECPoint y;
+
+ public ECPair(ECPoint x, ECPoint y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public ECPoint getX()
+ {
+ return x;
+ }
+
+ public ECPoint getY()
+ {
+ return y;
+ }
+
+ public boolean equals(ECPair other)
+ {
+ return other.getX().equals(getX()) && other.getY().equals(getY());
+ }
+
+ public boolean equals(Object other)
+ {
+ return other instanceof ECPair ? equals((ECPair)other) : false;
+ }
+
+ public int hashCode()
+ {
+ return x.hashCode() + 37 * y.hashCode();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPairFactorTransform.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPairFactorTransform.java
new file mode 100644
index 000000000..db1c8ce5a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPairFactorTransform.java
@@ -0,0 +1,14 @@
+package org.spongycastle.crypto.ec;
+
+import java.math.BigInteger;
+
+public interface ECPairFactorTransform
+ extends ECPairTransform
+{
+ /**
+ * Return the last value used to calculated a transform.
+ *
+ * @return a BigInteger representing the last transform value used.
+ */
+ BigInteger getTransformValue();
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPairTransform.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPairTransform.java
new file mode 100644
index 000000000..209937cd9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECPairTransform.java
@@ -0,0 +1,10 @@
+package org.spongycastle.crypto.ec;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public interface ECPairTransform
+{
+ void init(CipherParameters params);
+
+ ECPair transform(ECPair cipherText);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECUtil.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECUtil.java
new file mode 100644
index 000000000..00f9a2b79
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/ec/ECUtil.java
@@ -0,0 +1,22 @@
+package org.spongycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.math.ec.ECConstants;
+
+class ECUtil
+{
+ static BigInteger generateK(BigInteger n, SecureRandom random)
+ {
+ int nBitLength = n.bitLength();
+ BigInteger k = new BigInteger(nBitLength, random);
+
+ while (k.equals(ECConstants.ZERO) || (k.compareTo(n) >= 0))
+ {
+ k = new BigInteger(nBitLength, random);
+ }
+
+ return k;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/ISO9796d1Encoding.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/ISO9796d1Encoding.java
new file mode 100644
index 000000000..0fdc0593a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/ISO9796d1Encoding.java
@@ -0,0 +1,287 @@
+package org.spongycastle.crypto.encodings;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+
+/**
+ * ISO 9796-1 padding. Note in the light of recent results you should
+ * only use this with RSA (rather than the "simpler" Rabin keys) and you
+ * should never use it with anything other than a hash (ie. even if the
+ * message is small don't sign the message, sign it's hash) or some "random"
+ * value. See your favorite search engine for details.
+ */
+public class ISO9796d1Encoding
+ implements AsymmetricBlockCipher
+{
+ private static final BigInteger SIXTEEN = BigInteger.valueOf(16L);
+ private static final BigInteger SIX = BigInteger.valueOf(6L);
+
+ private static byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf,
+ 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 };
+ private static byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc,
+ 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 };
+
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private int bitSize;
+ private int padBits = 0;
+ private BigInteger modulus;
+
+ public ISO9796d1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ RSAKeyParameters kParam = null;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ kParam = (RSAKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ kParam = (RSAKeyParameters)param;
+ }
+
+ engine.init(forEncryption, param);
+
+ modulus = kParam.getModulus();
+ bitSize = modulus.bitLength();
+
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * return the input block size. The largest message we can process
+ * is (key_size_in_bits + 3)/16, which in our world comes to
+ * key_size_in_bytes / 2.
+ */
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ /**
+ * return the maximum possible size for the output.
+ */
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ }
+
+ /**
+ * set the number of bits in the next message to be treated as
+ * pad bits.
+ */
+ public void setPadBits(
+ int padBits)
+ {
+ if (padBits > 7)
+ {
+ throw new IllegalArgumentException("padBits > 7");
+ }
+
+ this.padBits = padBits;
+ }
+
+ /**
+ * retrieve the number of pad bits in the last decoded message.
+ */
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[(bitSize + 7) / 8];
+ int r = padBits + 1;
+ int z = inLen;
+ int t = (bitSize + 13) / 16;
+
+ for (int i = 0; i < t; i += z)
+ {
+ if (i > t - z)
+ {
+ System.arraycopy(in, inOff + inLen - (t - i),
+ block, block.length - t, t - i);
+ }
+ else
+ {
+ System.arraycopy(in, inOff, block, block.length - (i + z), z);
+ }
+ }
+
+ for (int i = block.length - 2 * t; i != block.length; i += 2)
+ {
+ byte val = block[block.length - t + i / 2];
+
+ block[i] = (byte)((shadows[(val & 0xff) >>> 4] << 4)
+ | shadows[val & 0x0f]);
+ block[i + 1] = val;
+ }
+
+ block[block.length - 2 * z] ^= r;
+ block[block.length - 1] = (byte)((block[block.length - 1] << 4) | 0x06);
+
+ int maxBit = (8 - (bitSize - 1) % 8);
+ int offSet = 0;
+
+ if (maxBit != 8)
+ {
+ block[0] &= 0xff >>> maxBit;
+ block[0] |= 0x80 >>> maxBit;
+ }
+ else
+ {
+ block[0] = 0x00;
+ block[1] |= 0x80;
+ offSet = 1;
+ }
+
+ return engine.processBlock(block, offSet, block.length - offSet);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = engine.processBlock(in, inOff, inLen);
+ int r = 1;
+ int t = (bitSize + 13) / 16;
+
+ BigInteger iS = new BigInteger(1, block);
+ BigInteger iR;
+ if (iS.mod(SIXTEEN).equals(SIX))
+ {
+ iR = iS;
+ }
+ else if ((modulus.subtract(iS)).mod(SIXTEEN).equals(SIX))
+ {
+ iR = modulus.subtract(iS);
+ }
+ else
+ {
+ throw new InvalidCipherTextException("resulting integer iS or (modulus - iS) is not congruent to 6 mod 16");
+ }
+
+ block = convertOutputDecryptOnly(iR);
+
+ if ((block[block.length - 1] & 0x0f) != 0x6 )
+ {
+ throw new InvalidCipherTextException("invalid forcing byte in block");
+ }
+
+ block[block.length - 1] = (byte)(((block[block.length - 1] & 0xff) >>> 4) | ((inverse[(block[block.length - 2] & 0xff) >> 4]) << 4));
+ block[0] = (byte)((shadows[(block[1] & 0xff) >>> 4] << 4)
+ | shadows[block[1] & 0x0f]);
+
+ boolean boundaryFound = false;
+ int boundary = 0;
+
+ for (int i = block.length - 1; i >= block.length - 2 * t; i -= 2)
+ {
+ int val = ((shadows[(block[i] & 0xff) >>> 4] << 4)
+ | shadows[block[i] & 0x0f]);
+
+ if (((block[i - 1] ^ val) & 0xff) != 0)
+ {
+ if (!boundaryFound)
+ {
+ boundaryFound = true;
+ r = (block[i - 1] ^ val) & 0xff;
+ boundary = i - 1;
+ }
+ else
+ {
+ throw new InvalidCipherTextException("invalid tsums in block");
+ }
+ }
+ }
+
+ block[boundary] = 0;
+
+ byte[] nblock = new byte[(block.length - boundary) / 2];
+
+ for (int i = 0; i < nblock.length; i++)
+ {
+ nblock[i] = block[2 * i + boundary + 1];
+ }
+
+ padBits = r - 1;
+
+ return nblock;
+ }
+
+ private static byte[] convertOutputDecryptOnly(BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+ if (output[0] == 0) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+ return tmp;
+ }
+ return output;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/OAEPEncoding.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/OAEPEncoding.java
new file mode 100644
index 000000000..11796fcec
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/OAEPEncoding.java
@@ -0,0 +1,357 @@
+package org.spongycastle.crypto.encodings;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
+ */
+public class OAEPEncoding
+ implements AsymmetricBlockCipher
+{
+ private byte[] defHash;
+ private Digest mgf1Hash;
+
+ private AsymmetricBlockCipher engine;
+ private SecureRandom random;
+ private boolean forEncryption;
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this(cipher, new SHA1Digest(), null);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash)
+ {
+ this(cipher, hash, null);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash,
+ byte[] encodingParams)
+ {
+ this(cipher, hash, hash, encodingParams);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash,
+ Digest mgf1Hash,
+ byte[] encodingParams)
+ {
+ this.engine = cipher;
+ this.mgf1Hash = mgf1Hash;
+ this.defHash = new byte[hash.getDigestSize()];
+
+ hash.reset();
+
+ if (encodingParams != null)
+ {
+ hash.update(encodingParams, 0, encodingParams.length);
+ }
+
+ hash.doFinal(defHash, 0);
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ engine.init(forEncryption, param);
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - 1 - 2 * defHash.length;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - 1 - 2 * defHash.length;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ public byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[getInputBlockSize() + 1 + 2 * defHash.length];
+
+ //
+ // copy in the message
+ //
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ //
+ // add sentinel
+ //
+ block[block.length - inLen - 1] = 0x01;
+
+ //
+ // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
+ //
+
+ //
+ // add the hash of the encoding params.
+ //
+ System.arraycopy(defHash, 0, block, defHash.length, defHash.length);
+
+ //
+ // generate the seed.
+ //
+ byte[] seed = new byte[defHash.length];
+
+ random.nextBytes(seed);
+
+ //
+ // mask the message block.
+ //
+ byte[] mask = maskGeneratorFunction1(seed, 0, seed.length, block.length - defHash.length);
+
+ for (int i = defHash.length; i != block.length; i++)
+ {
+ block[i] ^= mask[i - defHash.length];
+ }
+
+ //
+ // add in the seed
+ //
+ System.arraycopy(seed, 0, block, 0, defHash.length);
+
+ //
+ // mask the seed.
+ //
+ mask = maskGeneratorFunction1(
+ block, defHash.length, block.length - defHash.length, defHash.length);
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block turns out to
+ * be badly formatted.
+ */
+ public byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] data = engine.processBlock(in, inOff, inLen);
+ byte[] block;
+
+ //
+ // as we may have zeros in our leading bytes for the block we produced
+ // on encryption, we need to make sure our decrypted block comes back
+ // the same size.
+ //
+ if (data.length < engine.getOutputBlockSize())
+ {
+ block = new byte[engine.getOutputBlockSize()];
+
+ System.arraycopy(data, 0, block, block.length - data.length, data.length);
+ }
+ else
+ {
+ block = data;
+ }
+
+ if (block.length < (2 * defHash.length) + 1)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+
+ //
+ // unmask the seed.
+ //
+ byte[] mask = maskGeneratorFunction1(
+ block, defHash.length, block.length - defHash.length, defHash.length);
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ //
+ // unmask the message block.
+ //
+ mask = maskGeneratorFunction1(block, 0, defHash.length, block.length - defHash.length);
+
+ for (int i = defHash.length; i != block.length; i++)
+ {
+ block[i] ^= mask[i - defHash.length];
+ }
+
+ //
+ // check the hash of the encoding params.
+ // long check to try to avoid this been a source of a timing attack.
+ //
+ boolean defHashWrong = false;
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ if (defHash[i] != block[defHash.length + i])
+ {
+ defHashWrong = true;
+ }
+ }
+
+ if (defHashWrong)
+ {
+ throw new InvalidCipherTextException("data hash wrong");
+ }
+
+ //
+ // find the data block
+ //
+ int start;
+
+ for (start = 2 * defHash.length; start != block.length; start++)
+ {
+ if (block[start] != 0)
+ {
+ break;
+ }
+ }
+
+ if (start >= (block.length - 1) || block[start] != 1)
+ {
+ throw new InvalidCipherTextException("data start wrong " + start);
+ }
+
+ start++;
+
+ //
+ // extract the data block
+ //
+ byte[] output = new byte[block.length - start];
+
+ System.arraycopy(block, start, output, 0, output.length);
+
+ return output;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * mask generator function, as described in PKCS1v2.
+ */
+ private byte[] maskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[mgf1Hash.getDigestSize()];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ mgf1Hash.reset();
+
+ while (counter < (length / hashBuf.length))
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.update(Z, zOff, zLen);
+ mgf1Hash.update(C, 0, C.length);
+ mgf1Hash.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length);
+
+ counter++;
+ }
+
+ if ((counter * hashBuf.length) < length)
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.update(Z, zOff, zLen);
+ mgf1Hash.update(C, 0, C.length);
+ mgf1Hash.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length));
+ }
+
+ return mask;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/PKCS1Encoding.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/PKCS1Encoding.java
new file mode 100644
index 000000000..eb79fc068
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/encodings/PKCS1Encoding.java
@@ -0,0 +1,257 @@
+package org.spongycastle.crypto.encodings;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+public class PKCS1Encoding
+ implements AsymmetricBlockCipher
+{
+ /**
+ * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
+ * work with one of these set the system property org.spongycastle.pkcs1.strict to false.
+ * + * The system property is checked during construction of the encoding object, it is set to + * true by default. + *
+ */ + public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.spongycastle.pkcs1.strict"; + + private static final int HEADER_LENGTH = 10; + + private SecureRandom random; + private AsymmetricBlockCipher engine; + private boolean forEncryption; + private boolean forPrivateKey; + private boolean useStrictLength; + + /** + * Basic constructor. + * @param cipher + */ + public PKCS1Encoding( + AsymmetricBlockCipher cipher) + { + this.engine = cipher; + this.useStrictLength = useStrict(); + } + + // + // for J2ME compatibility + // + private boolean useStrict() + { + // required if security manager has been installed. + String strict = (String)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY); + } + }); + + return strict == null || strict.equals("true"); + } + + public AsymmetricBlockCipher getUnderlyingCipher() + { + return engine; + } + + public void init( + boolean forEncryption, + CipherParameters param) + { + AsymmetricKeyParameter kParam; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + this.random = rParam.getRandom(); + kParam = (AsymmetricKeyParameter)rParam.getParameters(); + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)param; + } + + engine.init(forEncryption, param); + + this.forPrivateKey = kParam.isPrivate(); + this.forEncryption = forEncryption; + } + + public int getInputBlockSize() + { + int baseBlockSize = engine.getInputBlockSize(); + + if (forEncryption) + { + return baseBlockSize - HEADER_LENGTH; + } + else + { + return baseBlockSize; + } + } + + public int getOutputBlockSize() + { + int baseBlockSize = engine.getOutputBlockSize(); + + if (forEncryption) + { + return baseBlockSize; + } + else + { + return baseBlockSize - HEADER_LENGTH; + } + } + + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forEncryption) + { + return encodeBlock(in, inOff, inLen); + } + else + { + return decodeBlock(in, inOff, inLen); + } + } + + private byte[] encodeBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (inLen > getInputBlockSize()) + { + throw new IllegalArgumentException("input data too large"); + } + + byte[] block = new byte[engine.getInputBlockSize()]; + + if (forPrivateKey) + { + block[0] = 0x01; // type code 1 + + for (int i = 1; i != block.length - inLen - 1; i++) + { + block[i] = (byte)0xFF; + } + } + else + { + random.nextBytes(block); // random fill + + block[0] = 0x02; // type code 2 + + // + // a zero byte marks the end of the padding, so all + // the pad bytes must be non-zero. + // + for (int i = 1; i != block.length - inLen - 1; i++) + { + while (block[i] == 0) + { + block[i] = (byte)random.nextInt(); + } + } + } + + block[block.length - inLen - 1] = 0x00; // mark the end of the padding + System.arraycopy(in, inOff, block, block.length - inLen, inLen); + + return engine.processBlock(block, 0, block.length); + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format. + */ + private byte[] decodeBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] block = engine.processBlock(in, inOff, inLen); + + if (block.length < getOutputBlockSize()) + { + throw new InvalidCipherTextException("block truncated"); + } + + byte type = block[0]; + + if (forPrivateKey) + { + if (type != 2) + { + throw new InvalidCipherTextException("unknown block type"); + } + } + else + { + if (type != 1) + { + throw new InvalidCipherTextException("unknown block type"); + } + } + + if (useStrictLength && block.length != engine.getOutputBlockSize()) + { + throw new InvalidCipherTextException("block incorrect size"); + } + + // + // find and extract the message block. + // + int start; + + for (start = 1; start != block.length; start++) + { + byte pad = block[start]; + + if (pad == 0) + { + break; + } + if (type == 1 && pad != (byte)0xff) + { + throw new InvalidCipherTextException("block padding incorrect"); + } + } + + start++; // data should start at the next byte + + if (start > block.length || start < HEADER_LENGTH) + { + throw new InvalidCipherTextException("no data in block"); + } + + byte[] result = new byte[block.length - start]; + + System.arraycopy(block, start, result, 0, result.length); + + return result; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java new file mode 100644 index 000000000..9a7dbe84d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java @@ -0,0 +1,546 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * an implementation of the AES (Rijndael), from FIPS-197. + *+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first. + * + * The slowest version uses no static tables at all and computes the values in each round. + *
+ * This file contains the middle performance version with 2Kbytes of static tables for round precomputation. + * + */ +public class AESEngine + implements BlockCipher +{ + // The S box + private static final byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static final byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static final int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + // precomputation tables of calculations for rounds + private static final int[] T0 = + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c}; + +private static final int[] Tinv0 = + { + 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, + 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, + 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, + 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, + 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, + 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, + 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, + 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, + 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, + 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, + 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, + 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, + 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, + 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, + 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, + 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, + 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, + 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, + 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, + 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, + 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, + 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, + 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, + 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, + 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, + 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, + 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, + 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, + 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, + 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, + 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, + 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, + 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, + 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, + 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, + 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, + 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, + 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, + 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, + 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, + 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, + 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, + 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, + 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, + 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, + 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, + 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, + 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, + 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, + 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, + 0x4257b8d0}; + + private static int shift(int r, int shift) + { + return (r >>> shift) | (r << -shift); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private static final int m1 = 0x80808080; + private static final int m2 = 0x7f7f7f7f; + private static final int m3 = 0x0000001b; + + private static int FFmulX(int x) + { + return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3)); + } + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private static int inv_mcol(int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24); + } + + private static int subWord(int x) + { + return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[][] generateWorkingKey( + byte[] key, + boolean forEncryption) + { + int KC = key.length / 4; // key length in words + int t; + + if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) + { + throw new IllegalArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + int[][] W = new int[ROUNDS+1][4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + int i = 0; + while (i < key.length) + { + W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + t++; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (i = KC; (i < k); i++) + { + int temp = W[(i-1)>>2][(i-1)&3]; + if ((i % KC) == 0) + { + temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1]; + } + else if ((KC > 6) && ((i % KC) == 4)) + { + temp = subWord(temp); + } + + W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp; + } + + if (!forEncryption) + { + for (int j = 1; j < ROUNDS; j++) + { + for (i = 0; i < 4; i++) + { + W[j][i] = inv_mcol(W[j][i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[][] WorkingKey = null; + private int C0, C1, C2, C3; + private boolean forEncryption; + + private static final int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AESEngine() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption); + this.forEncryption = forEncryption; + return; + } + + throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "AES"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (WorkingKey == null) + { + throw new IllegalStateException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (forEncryption) + { + unpackBlock(in, inOff); + encryptBlock(WorkingKey); + packBlock(out, outOff); + } + else + { + unpackBlock(in, inOff); + decryptBlock(WorkingKey); + packBlock(out, outOff); + } + + return BLOCK_SIZE; + } + + public void reset() + { + } + + private void unpackBlock( + byte[] bytes, + int off) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private void packBlock( + byte[] bytes, + int off) + { + int index = off; + + bytes[index++] = (byte)C0; + bytes[index++] = (byte)(C0 >> 8); + bytes[index++] = (byte)(C0 >> 16); + bytes[index++] = (byte)(C0 >> 24); + + bytes[index++] = (byte)C1; + bytes[index++] = (byte)(C1 >> 8); + bytes[index++] = (byte)(C1 >> 16); + bytes[index++] = (byte)(C1 >> 24); + + bytes[index++] = (byte)C2; + bytes[index++] = (byte)(C2 >> 8); + bytes[index++] = (byte)(C2 >> 16); + bytes[index++] = (byte)(C2 >> 24); + + bytes[index++] = (byte)C3; + bytes[index++] = (byte)(C3 >> 8); + bytes[index++] = (byte)(C3 >> 16); + bytes[index++] = (byte)(C3 >> 24); + } + + + private void encryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[0][0]; + C1 ^= KW[0][1]; + C2 ^= KW[0][2]; + C3 ^= KW[0][3]; + + r = 1; + + while (r < ROUNDS - 1) + { + r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255],16) ^ shift(T0[(C3>>24)&255],8) ^ KW[r][0]; + r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1]; + r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2]; + r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3]; + C0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0]; + C1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1]; + C2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2]; + C3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3]; + } + + r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255], 16) ^ shift(T0[(C3>>24)&255], 8) ^ KW[r][0]; + r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1]; + r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2]; + r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3]; + + // the final round's table is a simple function of S so we don't use a whole other four tables for it + + C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0]; + C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1]; + C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2]; + C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3]; + + } + + private void decryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[ROUNDS][0]; + C1 ^= KW[ROUNDS][1]; + C2 ^= KW[ROUNDS][2]; + C3 ^= KW[ROUNDS][3]; + + r = ROUNDS-1; + + while (r>1) + { + r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0]; + r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1]; + r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2]; + r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r--][3]; + C0 = Tinv0[r0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(r2>>16)&255], 16) ^ shift(Tinv0[(r1>>24)&255], 8) ^ KW[r][0]; + C1 = Tinv0[r1&255] ^ shift(Tinv0[(r0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(r2>>24)&255], 8) ^ KW[r][1]; + C2 = Tinv0[r2&255] ^ shift(Tinv0[(r1>>8)&255], 24) ^ shift(Tinv0[(r0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2]; + C3 = Tinv0[r3&255] ^ shift(Tinv0[(r2>>8)&255], 24) ^ shift(Tinv0[(r1>>16)&255], 16) ^ shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--][3]; + } + + r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0]; + r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1]; + r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2]; + r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r][3]; + + // the final round's table is a simple function of Si so we don't use a whole other four tables for it + + C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0]; + C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1]; + C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2]; + C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3]; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java new file mode 100644 index 000000000..34f33fd67 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java @@ -0,0 +1,875 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * an implementation of the AES (Rijndael), from FIPS-197. + *
+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first + * + * The slowest version uses no static tables at all and computes the values in each round + *
+ * This file contains the fast version with 8Kbytes of static tables for round precomputation + * + */ +public class AESFastEngine + implements BlockCipher +{ + // The S box + private static final byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static final byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static final int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + // precomputation tables of calculations for rounds + private static final int[] T0 = + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c}; + + private static final int[] T1 = + { + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, + 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, + 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, + 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, + 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, + 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, + 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, + 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, + 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, + 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, + 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, + 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, + 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, + 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, + 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, + 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, + 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, + 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, + 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, + 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, + 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, + 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, + 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, + 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, + 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, + 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, + 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, + 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, + 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, + 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, + 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, + 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, + 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, + 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, + 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, + 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, + 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, + 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, + 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, + 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, + 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, + 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, + 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, + 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, + 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, + 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, + 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, + 0x16162c3a}; + + private static final int[] T2 = + { + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, + 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, + 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, + 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, + 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, + 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, + 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, + 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, + 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, + 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, + 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, + 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, + 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, + 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, + 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, + 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, + 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, + 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, + 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, + 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, + 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, + 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, + 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, + 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, + 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, + 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, + 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, + 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, + 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, + 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, + 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, + 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, + 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, + 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, + 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, + 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, + 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, + 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, + 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, + 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, + 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, + 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, + 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, + 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, + 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, + 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, + 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, + 0x162c3a16}; + + private static final int[] T3 = + { + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, + 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, + 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, + 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, + 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, + 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, + 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, + 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, + 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, + 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, + 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, + 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, + 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, + 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, + 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, + 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, + 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, + 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, + 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, + 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, + 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, + 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, + 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, + 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, + 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, + 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, + 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, + 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, + 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, + 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, + 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, + 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, + 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, + 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, + 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, + 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, + 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, + 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, + 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, + 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, + 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, + 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, + 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, + 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, + 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, + 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, + 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, + 0x2c3a1616}; + + private static final int[] Tinv0 = + { + 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, + 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, + 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, + 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, + 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, + 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, + 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, + 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, + 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, + 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, + 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, + 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, + 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, + 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, + 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, + 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, + 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, + 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, + 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, + 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, + 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, + 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, + 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, + 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, + 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, + 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, + 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, + 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, + 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, + 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, + 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, + 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, + 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, + 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, + 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, + 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, + 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, + 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, + 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, + 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, + 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, + 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, + 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, + 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, + 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, + 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, + 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, + 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, + 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, + 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, + 0x4257b8d0}; + + private static final int[] Tinv1 = + { + 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, + 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, + 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, + 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, + 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, + 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, + 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, + 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, + 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, + 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, + 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, + 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, + 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, + 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, + 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, + 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, + 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, + 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, + 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, + 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, + 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, + 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, + 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, + 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, + 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b, + 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, + 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, + 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, + 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, + 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, + 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, + 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, + 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, + 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, + 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, + 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, + 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, + 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, + 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4, + 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, + 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, + 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, + 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, + 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, + 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, + 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, + 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, + 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, + 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, + 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de, + 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, + 0x57b8d042}; + + private static final int[] Tinv2 = + { + 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, + 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, + 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, + 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, + 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, + 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, + 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, + 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, + 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, + 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, + 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, + 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, + 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, + 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be, + 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, + 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, + 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, + 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, + 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, + 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, + 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff, + 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, + 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296, + 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, + 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d, + 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, + 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b, + 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, + 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, + 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, + 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, + 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, + 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, + 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, + 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, + 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, + 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, + 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, + 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, + 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, + 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98, + 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, + 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, + 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, + 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, + 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, + 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, + 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, + 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, + 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3, + 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, + 0xb8d04257}; + + private static final int[] Tinv3 = + { + 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, + 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, + 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, + 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, + 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f, + 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, + 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, + 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, + 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, + 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, + 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, + 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, + 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, + 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, + 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, + 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, + 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, + 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, + 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, + 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, + 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, + 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, + 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee, + 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, + 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, + 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, + 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, + 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, + 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, + 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, + 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, + 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, + 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, + 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, + 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, + 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, + 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, + 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, + 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, + 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, + 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, + 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, + 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, + 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, + 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, + 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, + 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, + 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, + 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, + 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c, + 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, + 0xd04257b8}; + + private static int shift(int r, int shift) + { + return (r >>> shift) | (r << -shift); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private static final int m1 = 0x80808080; + private static final int m2 = 0x7f7f7f7f; + private static final int m3 = 0x0000001b; + + private static int FFmulX(int x) + { + return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3)); + } + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private static int inv_mcol(int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24); + } + + + private static int subWord(int x) + { + return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[][] generateWorkingKey( + byte[] key, + boolean forEncryption) + { + int KC = key.length / 4; // key length in words + int t; + + if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) + { + throw new IllegalArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + int[][] W = new int[ROUNDS+1][4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + int i = 0; + while (i < key.length) + { + W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + t++; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (i = KC; (i < k); i++) + { + int temp = W[(i - 1) >> 2][(i - 1) & 3]; + if ((i % KC) == 0) + { + temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1]; + } + else if ((KC > 6) && ((i % KC) == 4)) + { + temp = subWord(temp); + } + + W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp; + } + + if (!forEncryption) + { + for (int j = 1; j < ROUNDS; j++) + { + for (i = 0; i < 4; i++) + { + W[j][i] = inv_mcol(W[j][i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[][] WorkingKey = null; + private int C0, C1, C2, C3; + private boolean forEncryption; + + private static final int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AESFastEngine() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption); + this.forEncryption = forEncryption; + return; + } + + throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "AES"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (WorkingKey == null) + { + throw new IllegalStateException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (forEncryption) + { + unpackBlock(in, inOff); + encryptBlock(WorkingKey); + packBlock(out, outOff); + } + else + { + unpackBlock(in, inOff); + decryptBlock(WorkingKey); + packBlock(out, outOff); + } + + return BLOCK_SIZE; + } + + public void reset() + { + } + + private void unpackBlock( + byte[] bytes, + int off) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private void packBlock( + byte[] bytes, + int off) + { + int index = off; + + bytes[index++] = (byte)C0; + bytes[index++] = (byte)(C0 >> 8); + bytes[index++] = (byte)(C0 >> 16); + bytes[index++] = (byte)(C0 >> 24); + + bytes[index++] = (byte)C1; + bytes[index++] = (byte)(C1 >> 8); + bytes[index++] = (byte)(C1 >> 16); + bytes[index++] = (byte)(C1 >> 24); + + bytes[index++] = (byte)C2; + bytes[index++] = (byte)(C2 >> 8); + bytes[index++] = (byte)(C2 >> 16); + bytes[index++] = (byte)(C2 >> 24); + + bytes[index++] = (byte)C3; + bytes[index++] = (byte)(C3 >> 8); + bytes[index++] = (byte)(C3 >> 16); + bytes[index++] = (byte)(C3 >> 24); + } + + private void encryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[0][0]; + C1 ^= KW[0][1]; + C2 ^= KW[0][2]; + C3 ^= KW[0][3]; + + r = 1; + while (r < ROUNDS - 1) + { + r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r][0]; + r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r][1]; + r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r][2]; + r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++][3]; + C0 = T0[r0&255] ^ T1[(r1>>8)&255] ^ T2[(r2>>16)&255] ^ T3[(r3>>24)&255] ^ KW[r][0]; + C1 = T0[r1&255] ^ T1[(r2>>8)&255] ^ T2[(r3>>16)&255] ^ T3[(r0>>24)&255] ^ KW[r][1]; + C2 = T0[r2&255] ^ T1[(r3>>8)&255] ^ T2[(r0>>16)&255] ^ T3[(r1>>24)&255] ^ KW[r][2]; + C3 = T0[r3&255] ^ T1[(r0>>8)&255] ^ T2[(r1>>16)&255] ^ T3[(r2>>24)&255] ^ KW[r++][3]; + } + + r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r][0]; + r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r][1]; + r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r][2]; + r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++][3]; + + // the final round's table is a simple function of S so we don't use a whole other four tables for it + + C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0]; + C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1]; + C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2]; + C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3]; + + } + + private void decryptBlock(int[][] KW) + { + int r0, r1, r2, r3; + + C0 ^= KW[ROUNDS][0]; + C1 ^= KW[ROUNDS][1]; + C2 ^= KW[ROUNDS][2]; + C3 ^= KW[ROUNDS][3]; + + int r = ROUNDS-1; + + while (r>1) + { + r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0]; + r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1]; + r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2]; + r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r--][3]; + C0 = Tinv0[r0&255] ^ Tinv1[(r3>>8)&255] ^ Tinv2[(r2>>16)&255] ^ Tinv3[(r1>>24)&255] ^ KW[r][0]; + C1 = Tinv0[r1&255] ^ Tinv1[(r0>>8)&255] ^ Tinv2[(r3>>16)&255] ^ Tinv3[(r2>>24)&255] ^ KW[r][1]; + C2 = Tinv0[r2&255] ^ Tinv1[(r1>>8)&255] ^ Tinv2[(r0>>16)&255] ^ Tinv3[(r3>>24)&255] ^ KW[r][2]; + C3 = Tinv0[r3&255] ^ Tinv1[(r2>>8)&255] ^ Tinv2[(r1>>16)&255] ^ Tinv3[(r0>>24)&255] ^ KW[r--][3]; + } + + r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0]; + r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1]; + r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2]; + r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r][3]; + + // the final round's table is a simple function of Si so we don't use a whole other four tables for it + + C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0]; + C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1]; + C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2]; + C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3]; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java new file mode 100644 index 000000000..4baa93a99 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java @@ -0,0 +1,439 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * an implementation of the AES (Rijndael), from FIPS-197. + *
+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first + * + * The slowest version uses no static tables at all and computes the values + * in each round. + *
+ * This file contains the slowest performance version with no static tables + * for round precomputation, but it has the smallest foot print. + * + */ +public class AESLightEngine + implements BlockCipher +{ + // The S box + private static final byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static final byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static final int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + private static int shift(int r, int shift) + { + return (r >>> shift) | (r << -shift); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private static final int m1 = 0x80808080; + private static final int m2 = 0x7f7f7f7f; + private static final int m3 = 0x0000001b; + + private static int FFmulX(int x) + { + return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3)); + } + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private static int mcol(int x) + { + int f2 = FFmulX(x); + return f2 ^ shift(x ^ f2, 8) ^ shift(x, 16) ^ shift(x, 24); + } + + private static int inv_mcol(int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24); + } + + + private static int subWord(int x) + { + return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[][] generateWorkingKey( + byte[] key, + boolean forEncryption) + { + int KC = key.length / 4; // key length in words + int t; + + if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) + { + throw new IllegalArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + int[][] W = new int[ROUNDS+1][4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + int i = 0; + while (i < key.length) + { + W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + t++; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (i = KC; (i < k); i++) + { + int temp = W[(i-1)>>2][(i-1)&3]; + if ((i % KC) == 0) + { + temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1]; + } + else if ((KC > 6) && ((i % KC) == 4)) + { + temp = subWord(temp); + } + + W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp; + } + + if (!forEncryption) + { + for (int j = 1; j < ROUNDS; j++) + { + for (i = 0; i < 4; i++) + { + W[j][i] = inv_mcol(W[j][i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[][] WorkingKey = null; + private int C0, C1, C2, C3; + private boolean forEncryption; + + private static final int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AESLightEngine() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption); + this.forEncryption = forEncryption; + return; + } + + throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "AES"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (WorkingKey == null) + { + throw new IllegalStateException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (forEncryption) + { + unpackBlock(in, inOff); + encryptBlock(WorkingKey); + packBlock(out, outOff); + } + else + { + unpackBlock(in, inOff); + decryptBlock(WorkingKey); + packBlock(out, outOff); + } + + return BLOCK_SIZE; + } + + public void reset() + { + } + + private void unpackBlock( + byte[] bytes, + int off) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private void packBlock( + byte[] bytes, + int off) + { + int index = off; + + bytes[index++] = (byte)C0; + bytes[index++] = (byte)(C0 >> 8); + bytes[index++] = (byte)(C0 >> 16); + bytes[index++] = (byte)(C0 >> 24); + + bytes[index++] = (byte)C1; + bytes[index++] = (byte)(C1 >> 8); + bytes[index++] = (byte)(C1 >> 16); + bytes[index++] = (byte)(C1 >> 24); + + bytes[index++] = (byte)C2; + bytes[index++] = (byte)(C2 >> 8); + bytes[index++] = (byte)(C2 >> 16); + bytes[index++] = (byte)(C2 >> 24); + + bytes[index++] = (byte)C3; + bytes[index++] = (byte)(C3 >> 8); + bytes[index++] = (byte)(C3 >> 16); + bytes[index++] = (byte)(C3 >> 24); + } + + private void encryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[0][0]; + C1 ^= KW[0][1]; + C2 ^= KW[0][2]; + C3 ^= KW[0][3]; + + for (r = 1; r < ROUNDS - 1;) + { + r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0]; + r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1]; + r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2]; + r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3]; + C0 = mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0]; + C1 = mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r][1]; + C2 = mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r][2]; + C3 = mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++][3]; + } + + r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0]; + r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1]; + r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2]; + r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3]; + + // the final round is a simple function of S + + C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0]; + C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1]; + C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2]; + C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3]; + + } + + private void decryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[ROUNDS][0]; + C1 ^= KW[ROUNDS][1]; + C2 ^= KW[ROUNDS][2]; + C3 ^= KW[ROUNDS][3]; + + for (r = ROUNDS-1; r>1;) + { + r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0]; + r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1]; + r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2]; + r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r--][3]; + C0 = inv_mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r][0]; + C1 = inv_mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r][1]; + C2 = inv_mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2]; + C3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--][3]; + } + + r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0]; + r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1]; + r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2]; + r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r][3]; + + // the final round's table is a simple function of Si + + C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0]; + C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1]; + C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2]; + C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3]; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java new file mode 100644 index 000000000..d2977948b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java @@ -0,0 +1,16 @@ +package org.spongycastle.crypto.engines; + +/** + * an implementation of the AES Key Wrapper from the NIST Key Wrap + * Specification. + *
+ * For further details see: http://csrc.nist.gov/encryption/kms/key-wrap.pdf. + */ +public class AESWrapEngine + extends RFC3394WrapEngine +{ + public AESWrapEngine() + { + super(new AESEngine()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java new file mode 100644 index 000000000..e2f047796 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java @@ -0,0 +1,577 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * A class that provides Blowfish key encryption operations, + * such as encoding data and generating keys. + * All the algorithms herein are from Applied Cryptography + * and implement a simplified cryptography interface. + */ +public final class BlowfishEngine +implements BlockCipher +{ + private final static int[] + KP = { + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, + 0x9216D5D9, 0x8979FB1B + }, + + KS0 = { + 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, + 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, + 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, + 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, + 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, + 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, + 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, + 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, + 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, + 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, + 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, + 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, + 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, + 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, + 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, + 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, + 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, + 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, + 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, + 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, + 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, + 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, + 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, + 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, + 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, + 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, + 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, + 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, + 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, + 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, + 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, + 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, + 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, + 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, + 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, + 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, + 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, + 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, + 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, + 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, + 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, + 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, + 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, + 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, + 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, + 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, + 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, + 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, + 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, + 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, + 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, + 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, + 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, + 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, + 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, + 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, + 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, + 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, + 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, + 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, + 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, + 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, + 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, + 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A + }, + + KS1 = { + 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, + 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, + 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, + 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, + 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, + 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, + 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, + 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, + 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, + 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, + 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, + 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, + 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, + 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, + 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, + 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, + 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, + 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, + 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, + 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, + 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, + 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, + 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, + 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, + 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, + 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, + 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, + 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, + 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, + 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, + 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, + 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, + 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, + 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, + 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, + 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, + 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, + 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, + 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, + 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, + 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, + 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, + 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, + 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, + 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, + 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, + 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, + 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, + 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, + 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, + 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, + 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, + 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, + 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, + 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, + 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, + 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, + 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, + 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, + 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, + 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, + 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, + 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, + 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 + }, + + KS2 = { + 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, + 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, + 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, + 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, + 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, + 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, + 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, + 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, + 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, + 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, + 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, + 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, + 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, + 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, + 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, + 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, + 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, + 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, + 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, + 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, + 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, + 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, + 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, + 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, + 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, + 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, + 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, + 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, + 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, + 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, + 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, + 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, + 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, + 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, + 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, + 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, + 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, + 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, + 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, + 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, + 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, + 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, + 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, + 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, + 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, + 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, + 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, + 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, + 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, + 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, + 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, + 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, + 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, + 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, + 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, + 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, + 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, + 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, + 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, + 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, + 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, + 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, + 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, + 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 + }, + + KS3 = { + 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, + 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, + 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, + 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, + 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, + 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, + 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, + 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, + 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, + 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, + 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, + 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, + 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, + 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, + 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, + 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, + 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, + 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, + 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, + 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, + 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, + 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, + 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, + 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, + 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, + 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, + 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, + 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, + 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, + 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, + 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, + 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, + 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, + 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, + 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, + 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, + 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, + 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, + 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, + 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, + 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, + 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, + 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, + 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, + 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, + 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, + 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, + 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, + 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, + 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, + 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, + 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, + 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, + 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, + 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, + 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, + 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, + 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, + 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, + 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, + 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, + 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, + 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, + 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 + }; + + //==================================== + // Useful constants + //==================================== + + private static final int ROUNDS = 16; + private static final int BLOCK_SIZE = 8; // bytes = 64 bits + private static final int SBOX_SK = 256; + private static final int P_SZ = ROUNDS+2; + + private final int[] S0, S1, S2, S3; // the s-boxes + private final int[] P; // the p-array + + private boolean encrypting = false; + + private byte[] workingKey = null; + + public BlowfishEngine() + { + S0 = new int[SBOX_SK]; + S1 = new int[SBOX_SK]; + S2 = new int[SBOX_SK]; + S3 = new int[SBOX_SK]; + P = new int[P_SZ]; + } + + /** + * initialise a Blowfish cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + this.encrypting = encrypting; + this.workingKey = ((KeyParameter)params).getKey(); + setKey(this.workingKey); + + return; + } + + throw new IllegalArgumentException("invalid parameter passed to Blowfish init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "Blowfish"; + } + + public final int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("Blowfish 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() + { + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + private int F(int x) + { + return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) + ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]); + } + + /** + * apply the encryption cycle to each value pair in the table. + */ + private void processTable( + int xl, + int xr, + int[] table) + { + int size = table.length; + + for (int s = 0; s < size; s += 2) + { + xl ^= P[0]; + + for (int i = 1; i < ROUNDS; i += 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i + 1]; + } + + xr ^= P[ROUNDS + 1]; + + table[s] = xr; + table[s + 1] = xl; + + xr = xl; // end of cycle swap + xl = table[s]; + } + } + + private void setKey(byte[] key) + { + /* + * - comments are from _Applied Crypto_, Schneier, p338 + * please be careful comparing the two, AC numbers the + * arrays from 1, the enclosed code from 0. + * + * (1) + * Initialise the S-boxes and the P-array, with a fixed string + * This string contains the hexadecimal digits of pi (3.141...) + */ + System.arraycopy(KS0, 0, S0, 0, SBOX_SK); + System.arraycopy(KS1, 0, S1, 0, SBOX_SK); + System.arraycopy(KS2, 0, S2, 0, SBOX_SK); + System.arraycopy(KS3, 0, S3, 0, SBOX_SK); + + System.arraycopy(KP, 0, P, 0, P_SZ); + + /* + * (2) + * Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with the + * second 32-bits of the key, and so on for all bits of the key + * (up to P[17]). Repeatedly cycle through the key bits until the + * entire P-array has been XOR-ed with the key bits + */ + int keyLength = key.length; + int keyIndex = 0; + + for (int i=0; i < P_SZ; i++) + { + // get the 32 bits of the key, in 4 * 8 bit chunks + int data = 0x0000000; + for (int j=0; j < 4; j++) + { + // create a 32 bit block + data = (data << 8) | (key[keyIndex++] & 0xff); + + // wrap when we get to the end of the key + if (keyIndex >= keyLength) + { + keyIndex = 0; + } + } + // XOR the newly created 32 bit chunk onto the P-array + P[i] ^= data; + } + + /* + * (3) + * Encrypt the all-zero string with the Blowfish algorithm, using + * the subkeys described in (1) and (2) + * + * (4) + * Replace P1 and P2 with the output of step (3) + * + * (5) + * Encrypt the output of step(3) using the Blowfish algorithm, + * with the modified subkeys. + * + * (6) + * Replace P3 and P4 with the output of step (5) + * + * (7) + * Continue the process, replacing all elements of the P-array + * and then all four S-boxes in order, with the output of the + * continuously changing Blowfish algorithm + */ + + processTable(0, 0, P); + processTable(P[P_SZ - 2], P[P_SZ - 1], S0); + processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1); + processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2); + processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3); + } + + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void encryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int xl = BytesTo32bits(src, srcIndex); + int xr = BytesTo32bits(src, srcIndex+4); + + xl ^= P[0]; + + for (int i = 1; i < ROUNDS; i += 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i + 1]; + } + + xr ^= P[ROUNDS + 1]; + + Bits32ToBytes(xr, dst, dstIndex); + Bits32ToBytes(xl, dst, dstIndex + 4); + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void decryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int xl = BytesTo32bits(src, srcIndex); + int xr = BytesTo32bits(src, srcIndex + 4); + + xl ^= P[ROUNDS + 1]; + + for (int i = ROUNDS; i > 0 ; i -= 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i - 1]; + } + + xr ^= P[0]; + + Bits32ToBytes(xr, dst, dstIndex); + Bits32ToBytes(xl, dst, dstIndex+4); + } + + private int BytesTo32bits(byte[] b, int i) + { + return ((b[i] & 0xff) << 24) | + ((b[i+1] & 0xff) << 16) | + ((b[i+2] & 0xff) << 8) | + ((b[i+3] & 0xff)); + } + + private void Bits32ToBytes(int in, byte[] b, int offset) + { + b[offset + 3] = (byte)in; + b[offset + 2] = (byte)(in >> 8); + b[offset + 1] = (byte)(in >> 16); + b[offset] = (byte)(in >> 24); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java new file mode 100644 index 000000000..7b51fa48b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java @@ -0,0 +1,831 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * A class that provides CAST key encryption operations, + * such as encoding data and generating keys. + * + * All the algorithms herein are from the Internet RFC's + * + * RFC2144 - CAST5 (64bit block, 40-128bit key) + * RFC2612 - CAST6 (128bit block, 128-256bit key) + * + * and implement a simplified cryptography interface. + */ +public class CAST5Engine + implements BlockCipher +{ + protected final static int M32 = 0xffffffff; + + protected final static int[] + S1 = { +0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, +0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, +0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, +0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, +0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, +0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, +0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, +0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, +0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, +0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, +0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, +0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, +0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, +0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, +0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, +0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, +0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, +0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, +0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, +0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, +0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, +0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, +0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, +0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, +0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, +0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, +0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, +0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, +0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, +0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, +0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, +0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf + }, + S2 = { +0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, +0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, +0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, +0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, +0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, +0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, +0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, +0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, +0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, +0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, +0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, +0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, +0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, +0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, +0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, +0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, +0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, +0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, +0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, +0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, +0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, +0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, +0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, +0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, +0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, +0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, +0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, +0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, +0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, +0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, +0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, +0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1 + }, + S3 = { +0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, +0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, +0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, +0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, +0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, +0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, +0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, +0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, +0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, +0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, +0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, +0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, +0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, +0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, +0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, +0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, +0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, +0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, +0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, +0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, +0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, +0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, +0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, +0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, +0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, +0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, +0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, +0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, +0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, +0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, +0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, +0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783 + }, + S4 = { +0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, +0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, +0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, +0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, +0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, +0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, +0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, +0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, +0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, +0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, +0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, +0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, +0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, +0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, +0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, +0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, +0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, +0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, +0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, +0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, +0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, +0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, +0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, +0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, +0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, +0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, +0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, +0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, +0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, +0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, +0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, +0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2 + }, + S5 = { +0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, +0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, +0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, +0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, +0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, +0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, +0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, +0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, +0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, +0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, +0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, +0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, +0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, +0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, +0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, +0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, +0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, +0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, +0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, +0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, +0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, +0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, +0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, +0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, +0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, +0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, +0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, +0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, +0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, +0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, +0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, +0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4 + }, + S6 = { +0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, +0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, +0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, +0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, +0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, +0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, +0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, +0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, +0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, +0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, +0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, +0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, +0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, +0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, +0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, +0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, +0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, +0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, +0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, +0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, +0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, +0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, +0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, +0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, +0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, +0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, +0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, +0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, +0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, +0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, +0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, +0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f + }, + S7 = { +0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, +0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, +0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, +0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, +0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, +0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, +0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, +0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, +0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, +0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, +0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, +0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, +0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, +0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, +0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, +0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, +0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, +0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, +0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, +0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, +0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, +0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, +0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, +0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, +0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, +0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, +0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, +0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, +0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, +0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, +0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, +0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3 + }, + S8 = { +0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, +0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, +0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, +0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, +0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, +0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, +0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, +0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, +0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, +0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, +0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, +0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, +0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, +0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, +0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, +0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, +0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, +0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, +0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, +0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, +0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, +0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, +0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, +0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, +0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, +0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, +0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, +0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, +0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, +0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, +0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, +0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e + }; + + //==================================== + // Useful constants + //==================================== + + protected static final int MAX_ROUNDS = 16; + protected static final int RED_ROUNDS = 12; + + protected static final int BLOCK_SIZE = 8; // bytes = 64 bits + + protected int _Kr[] = new int[17]; // the rotating round key + protected int _Km[] = new int[17]; // the masking round key + + private boolean _encrypting = false; + + private byte[] _workingKey = null; + private int _rounds = MAX_ROUNDS; + + public CAST5Engine() + { + } + + /** + * initialise a CAST cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + _encrypting = encrypting; + _workingKey = ((KeyParameter)params).getKey(); + + setKey(_workingKey); + + return; + } + + throw new IllegalArgumentException("Invalid parameter passed to "+getAlgorithmName()+" init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "CAST5"; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (_workingKey == null) + { + throw new IllegalStateException(getAlgorithmName()+" not initialised"); + } + + int blockSize = getBlockSize(); + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("Input buffer too short"); + } + + if ((outOff + blockSize) > out.length) + { + throw new OutputLengthException("Output buffer too short"); + } + + if (_encrypting) + { + return encryptBlock(in, inOff, out, outOff); + } + else + { + return decryptBlock(in, inOff, out, outOff); + } + } + + public void reset() + { + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + /* + * Creates the subkeys using the same nomenclature + * as described in RFC2144. + * + * See section 2.4 + */ + protected void setKey(byte[] key) + { + /* + * Determine the key size here, if required + * + * if keysize <= 80bits, use 12 rounds instead of 16 + * if keysize < 128bits, pad with 0 + * + * Typical key sizes => 40, 64, 80, 128 + */ + + if (key.length < 11) + { + _rounds = RED_ROUNDS; + } + + int z[] = new int[16]; + int x[] = new int[16]; + + int z03, z47, z8B, zCF; + int x03, x47, x8B, xCF; + + /* copy the key into x */ + for (int i=0; i< key.length; i++) + { + x[i] = key[i] & 0xff; + } + + /* + * This will look different because the selection of + * bytes from the input key I've already chosen the + * correct int. + */ + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Km[ 1]= S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]; + _Km[ 2]= S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]; + _Km[ 3]= S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]; + _Km[ 4]= S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Km[ 5]= S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]; + _Km[ 6]= S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]; + _Km[ 7]= S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]; + _Km[ 8]= S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Km[ 9]= S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]; + _Km[10]= S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]; + _Km[11]= S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]; + _Km[12]= S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Km[13]= S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]; + _Km[14]= S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]; + _Km[15]= S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]; + _Km[16]= S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Kr[ 1]=(S5[z[0x8]]^S6[z[0x9]]^S7[z[0x7]]^S8[z[0x6]] ^ S5[z[0x2]])&0x1f; + _Kr[ 2]=(S5[z[0xA]]^S6[z[0xB]]^S7[z[0x5]]^S8[z[0x4]] ^ S6[z[0x6]])&0x1f; + _Kr[ 3]=(S5[z[0xC]]^S6[z[0xD]]^S7[z[0x3]]^S8[z[0x2]] ^ S7[z[0x9]])&0x1f; + _Kr[ 4]=(S5[z[0xE]]^S6[z[0xF]]^S7[z[0x1]]^S8[z[0x0]] ^ S8[z[0xC]])&0x1f; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Kr[ 5]=(S5[x[0x3]]^S6[x[0x2]]^S7[x[0xC]]^S8[x[0xD]]^S5[x[0x8]])&0x1f; + _Kr[ 6]=(S5[x[0x1]]^S6[x[0x0]]^S7[x[0xE]]^S8[x[0xF]]^S6[x[0xD]])&0x1f; + _Kr[ 7]=(S5[x[0x7]]^S6[x[0x6]]^S7[x[0x8]]^S8[x[0x9]]^S7[x[0x3]])&0x1f; + _Kr[ 8]=(S5[x[0x5]]^S6[x[0x4]]^S7[x[0xA]]^S8[x[0xB]]^S8[x[0x7]])&0x1f; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Kr[ 9]=(S5[z[0x3]]^S6[z[0x2]]^S7[z[0xC]]^S8[z[0xD]]^S5[z[0x9]])&0x1f; + _Kr[10]=(S5[z[0x1]]^S6[z[0x0]]^S7[z[0xE]]^S8[z[0xF]]^S6[z[0xc]])&0x1f; + _Kr[11]=(S5[z[0x7]]^S6[z[0x6]]^S7[z[0x8]]^S8[z[0x9]]^S7[z[0x2]])&0x1f; + _Kr[12]=(S5[z[0x5]]^S6[z[0x4]]^S7[z[0xA]]^S8[z[0xB]]^S8[z[0x6]])&0x1f; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Kr[13]=(S5[x[0x8]]^S6[x[0x9]]^S7[x[0x7]]^S8[x[0x6]]^S5[x[0x3]])&0x1f; + _Kr[14]=(S5[x[0xA]]^S6[x[0xB]]^S7[x[0x5]]^S8[x[0x4]]^S6[x[0x7]])&0x1f; + _Kr[15]=(S5[x[0xC]]^S6[x[0xD]]^S7[x[0x3]]^S8[x[0x2]]^S7[x[0x8]])&0x1f; + _Kr[16]=(S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f; + } + + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + protected int encryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + + int result[] = new int[2]; + + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + + int L0 = BytesTo32bits(src, srcIndex); + int R0 = BytesTo32bits(src, srcIndex + 4); + + CAST_Encipher(L0, R0, result); + + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex + 4); + + return BLOCK_SIZE; + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + protected int decryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int result[] = new int[2]; + + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + int L16 = BytesTo32bits(src, srcIndex); + int R16 = BytesTo32bits(src, srcIndex+4); + + CAST_Decipher(L16, R16, result); + + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex+4); + + return BLOCK_SIZE; + } + + /** + * The first of the three processing functions for the + * encryption and decryption. + * + * @param D the input to be processed + * @param Kmi the mask to be used from Km[n] + * @param Kri the rotation value to be used + * + */ + protected final int F1(int D, int Kmi, int Kri) + { + int I = Kmi + D; + I = I << Kri | I >>> (32-Kri); + return ((S1[(I>>>24)&0xff]^S2[(I>>>16)&0xff])-S3[(I>>> 8)&0xff])+ + S4[I & 0xff]; + } + + /** + * The second of the three processing functions for the + * encryption and decryption. + * + * @param D the input to be processed + * @param Kmi the mask to be used from Km[n] + * @param Kri the rotation value to be used + * + */ + protected final int F2(int D, int Kmi, int Kri) + { + int I = Kmi ^ D; + I = I << Kri | I >>> (32-Kri); + return ((S1[(I>>>24)&0xff]-S2[(I>>>16)&0xff])+S3[(I>>> 8)&0xff])^ + S4[I & 0xff]; + } + + /** + * The third of the three processing functions for the + * encryption and decryption. + * + * @param D the input to be processed + * @param Kmi the mask to be used from Km[n] + * @param Kri the rotation value to be used + * + */ + protected final int F3(int D, int Kmi, int Kri) + { + int I = Kmi - D; + I = I << Kri | I >>> (32-Kri); + return ((S1[(I>>>24)&0xff]+S2[(I>>>16)&0xff])^S3[(I>>> 8)&0xff])- + S4[I & 0xff]; + } + + /** + * Does the 16 rounds to encrypt the block. + * + * @param L0 the LH-32bits of the plaintext block + * @param R0 the RH-32bits of the plaintext block + */ + protected final void CAST_Encipher(int L0, int R0, int result[]) + { + int Lp = L0; // the previous value, equiv to L[i-1] + int Rp = R0; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + int Li = L0, Ri = R0; + + for (int i = 1; i<=_rounds ; i++) + { + Lp = Li; + Rp = Ri; + + Li = Rp; + switch (i) + { + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); + break; + } + } + + result[0] = Ri; + result[1] = Li; + + return; + } + + protected final void CAST_Decipher(int L16, int R16, int result[]) + { + int Lp = L16; // the previous value, equiv to L[i-1] + int Rp = R16; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + int Li = L16, Ri = R16; + + for (int i = _rounds; i > 0; i--) + { + Lp = Li; + Rp = Ri; + + Li = Rp; + switch (i) + { + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); + break; + } + } + + result[0] = Ri; + result[1] = Li; + + return; + } + + protected final void Bits32ToInts(int in, int[] b, int offset) + { + b[offset + 3] = (in & 0xff); + b[offset + 2] = ((in >>> 8) & 0xff); + b[offset + 1] = ((in >>> 16) & 0xff); + b[offset] = ((in >>> 24) & 0xff); + } + + protected final int IntsTo32bits(int[] b, int i) + { + int rv = 0; + + rv = ((b[i] & 0xff) << 24) | + ((b[i+1] & 0xff) << 16) | + ((b[i+2] & 0xff) << 8) | + ((b[i+3] & 0xff)); + + return rv; + } + + protected final void Bits32ToBytes(int in, byte[] b, int offset) + { + b[offset + 3] = (byte)in; + b[offset + 2] = (byte)(in >>> 8); + b[offset + 1] = (byte)(in >>> 16); + b[offset] = (byte)(in >>> 24); + } + + protected final int BytesTo32bits(byte[] b, int i) + { + return ((b[i] & 0xff) << 24) | + ((b[i+1] & 0xff) << 16) | + ((b[i+2] & 0xff) << 8) | + ((b[i+3] & 0xff)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java new file mode 100644 index 000000000..7a9907563 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java @@ -0,0 +1,296 @@ +package org.spongycastle.crypto.engines; + + +/** + * A class that provides CAST6 key encryption operations, + * such as encoding data and generating keys. + * + * All the algorithms herein are from the Internet RFC + * + * RFC2612 - CAST6 (128bit block, 128-256bit key) + * + * and implement a simplified cryptography interface. + */ +public final class CAST6Engine extends CAST5Engine +{ + //==================================== + // Useful constants + //==================================== + + protected static final int ROUNDS = 12; + + protected static final int BLOCK_SIZE = 16; // bytes = 128 bits + + /* + * Put the round and mask keys into an array. + * Kr0[i] => _Kr[i*4 + 0] + */ + protected int _Kr[] = new int[ROUNDS*4]; // the rotating round key(s) + protected int _Km[] = new int[ROUNDS*4]; // the masking round key(s) + + /* + * Key setup + */ + protected int _Tr[] = new int[24 * 8]; + protected int _Tm[] = new int[24 * 8]; + + private int[] _workingKey = new int[8]; + + public CAST6Engine() + { + } + + public String getAlgorithmName() + { + return "CAST6"; + } + + public void reset() + { + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + /* + * Creates the subkeys using the same nomenclature + * as described in RFC2612. + * + * See section 2.4 + */ + protected void setKey(byte[] key) + { + int Cm = 0x5a827999; + int Mm = 0x6ed9eba1; + int Cr = 19; + int Mr = 17; + + /* + * Determine the key size here, if required + * + * if keysize < 256 bytes, pad with 0 + * + * Typical key sizes => 128, 160, 192, 224, 256 + */ + for (int i=0; i< 24; i++) + { + for (int j=0; j< 8; j++) + { + _Tm[i*8 + j] = Cm; + Cm = (Cm + Mm); // mod 2^32; + + _Tr[i*8 + j] = Cr; + Cr = (Cr + Mr) & 0x1f; // mod 32 + } + } + + byte[] tmpKey = new byte[64]; + int length = key.length; + System.arraycopy(key, 0, tmpKey, 0, length); + + // now create ABCDEFGH + for (int i=0; i< 8; i++) + { + _workingKey[i] = BytesTo32bits(tmpKey, i*4); + } + + // Generate the key schedule + for (int i=0; i< 12; i++) + { + // KAPPA <- W2i(KAPPA) + int i2 = i*2 *8; + _workingKey[6] ^= F1(_workingKey[7], _Tm[i2 ], _Tr[i2 ]); + _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]); + _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]); + _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]); + _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]); + _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]); + _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]); + _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]); + + // KAPPA <- W2i+1(KAPPA) + i2 = (i*2 + 1)*8; + _workingKey[6] ^= F1(_workingKey[7], _Tm[i2 ], _Tr[i2 ]); + _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]); + _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]); + _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]); + _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]); + _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]); + _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]); + _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]); + + // Kr_(i) <- KAPPA + _Kr[i*4 ] = _workingKey[0] & 0x1f; + _Kr[i*4 + 1] = _workingKey[2] & 0x1f; + _Kr[i*4 + 2] = _workingKey[4] & 0x1f; + _Kr[i*4 + 3] = _workingKey[6] & 0x1f; + + + // Km_(i) <- KAPPA + _Km[i*4 ] = _workingKey[7]; + _Km[i*4 + 1] = _workingKey[5]; + _Km[i*4 + 2] = _workingKey[3]; + _Km[i*4 + 3] = _workingKey[1]; + } + + } + + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + protected int encryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + + int result[] = new int[4]; + + // process the input block + // batch the units up into 4x32 bit chunks and go for it + + int A = BytesTo32bits(src, srcIndex); + int B = BytesTo32bits(src, srcIndex + 4); + int C = BytesTo32bits(src, srcIndex + 8); + int D = BytesTo32bits(src, srcIndex + 12); + + CAST_Encipher(A, B, C, D, result); + + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex + 4); + Bits32ToBytes(result[2], dst, dstIndex + 8); + Bits32ToBytes(result[3], dst, dstIndex + 12); + + return BLOCK_SIZE; + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + protected int decryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int result[] = new int[4]; + + // process the input block + // batch the units up into 4x32 bit chunks and go for it + int A = BytesTo32bits(src, srcIndex); + int B = BytesTo32bits(src, srcIndex + 4); + int C = BytesTo32bits(src, srcIndex + 8); + int D = BytesTo32bits(src, srcIndex + 12); + + CAST_Decipher(A, B, C, D, result); + + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex + 4); + Bits32ToBytes(result[2], dst, dstIndex + 8); + Bits32ToBytes(result[3], dst, dstIndex + 12); + + return BLOCK_SIZE; + } + + /** + * Does the 12 quad rounds rounds to encrypt the block. + * + * @param A the 00-31 bits of the plaintext block + * @param B the 32-63 bits of the plaintext block + * @param C the 64-95 bits of the plaintext block + * @param D the 96-127 bits of the plaintext block + * @param result the resulting ciphertext + */ + protected final void CAST_Encipher(int A, int B, int C, int D,int result[]) + { + int x; + for (int i=0; i< 6; i++) + { + x = i*4; + // BETA <- Qi(BETA) + C ^= F1(D, _Km[x], _Kr[x]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + + } + + for (int i=6; i<12; i++) + { + x = i*4; + // BETA <- QBARi(BETA) + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + C ^= F1(D, _Km[x], _Kr[x]); + + } + + result[0] = A; + result[1] = B; + result[2] = C; + result[3] = D; + } + + /** + * Does the 12 quad rounds rounds to decrypt the block. + * + * @param A the 00-31 bits of the ciphertext block + * @param B the 32-63 bits of the ciphertext block + * @param C the 64-95 bits of the ciphertext block + * @param D the 96-127 bits of the ciphertext block + * @param result the resulting plaintext + */ + protected final void CAST_Decipher(int A, int B, int C, int D,int result[]) + { + int x; + for (int i=0; i< 6; i++) + { + x = (11-i)*4; + // BETA <- Qi(BETA) + C ^= F1(D, _Km[x], _Kr[x]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + + } + + for (int i=6; i<12; i++) + { + x = (11-i)*4; + // BETA <- QBARi(BETA) + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + C ^= F1(D, _Km[x], _Kr[x]); + + } + + result[0] = A; + result[1] = B; + result[2] = C; + result[3] = D; + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java new file mode 100644 index 000000000..b82af072c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java @@ -0,0 +1,684 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * Camellia - based on RFC 3713. + */ +public class CamelliaEngine + implements BlockCipher +{ + private boolean initialised = false; + private boolean _keyIs128; + + private static final int BLOCK_SIZE = 16; + private static final int MASK8 = 0xff; + + private int[] subkey = new int[24 * 4]; + private int[] kw = new int[4 * 2]; // for whitening + private int[] ke = new int[6 * 2]; // for FL and FL^(-1) + private int[] state = new int[4]; // for encryption and decryption + + private static final int SIGMA[] = { + 0xa09e667f, 0x3bcc908b, + 0xb67ae858, 0x4caa73b2, + 0xc6ef372f, 0xe94f82be, + 0x54ff53a5, 0xf1d36f1c, + 0x10e527fa, 0xde682d1d, + 0xb05688c2, 0xb3e6c1fd + }; + + /* + * + * S-box data + * + */ + private static final int SBOX1_1110[] = { + 0x70707000, 0x82828200, 0x2c2c2c00, 0xececec00, 0xb3b3b300, 0x27272700, + 0xc0c0c000, 0xe5e5e500, 0xe4e4e400, 0x85858500, 0x57575700, 0x35353500, + 0xeaeaea00, 0x0c0c0c00, 0xaeaeae00, 0x41414100, 0x23232300, 0xefefef00, + 0x6b6b6b00, 0x93939300, 0x45454500, 0x19191900, 0xa5a5a500, 0x21212100, + 0xededed00, 0x0e0e0e00, 0x4f4f4f00, 0x4e4e4e00, 0x1d1d1d00, 0x65656500, + 0x92929200, 0xbdbdbd00, 0x86868600, 0xb8b8b800, 0xafafaf00, 0x8f8f8f00, + 0x7c7c7c00, 0xebebeb00, 0x1f1f1f00, 0xcecece00, 0x3e3e3e00, 0x30303000, + 0xdcdcdc00, 0x5f5f5f00, 0x5e5e5e00, 0xc5c5c500, 0x0b0b0b00, 0x1a1a1a00, + 0xa6a6a600, 0xe1e1e100, 0x39393900, 0xcacaca00, 0xd5d5d500, 0x47474700, + 0x5d5d5d00, 0x3d3d3d00, 0xd9d9d900, 0x01010100, 0x5a5a5a00, 0xd6d6d600, + 0x51515100, 0x56565600, 0x6c6c6c00, 0x4d4d4d00, 0x8b8b8b00, 0x0d0d0d00, + 0x9a9a9a00, 0x66666600, 0xfbfbfb00, 0xcccccc00, 0xb0b0b000, 0x2d2d2d00, + 0x74747400, 0x12121200, 0x2b2b2b00, 0x20202000, 0xf0f0f000, 0xb1b1b100, + 0x84848400, 0x99999900, 0xdfdfdf00, 0x4c4c4c00, 0xcbcbcb00, 0xc2c2c200, + 0x34343400, 0x7e7e7e00, 0x76767600, 0x05050500, 0x6d6d6d00, 0xb7b7b700, + 0xa9a9a900, 0x31313100, 0xd1d1d100, 0x17171700, 0x04040400, 0xd7d7d700, + 0x14141400, 0x58585800, 0x3a3a3a00, 0x61616100, 0xdedede00, 0x1b1b1b00, + 0x11111100, 0x1c1c1c00, 0x32323200, 0x0f0f0f00, 0x9c9c9c00, 0x16161600, + 0x53535300, 0x18181800, 0xf2f2f200, 0x22222200, 0xfefefe00, 0x44444400, + 0xcfcfcf00, 0xb2b2b200, 0xc3c3c300, 0xb5b5b500, 0x7a7a7a00, 0x91919100, + 0x24242400, 0x08080800, 0xe8e8e800, 0xa8a8a800, 0x60606000, 0xfcfcfc00, + 0x69696900, 0x50505000, 0xaaaaaa00, 0xd0d0d000, 0xa0a0a000, 0x7d7d7d00, + 0xa1a1a100, 0x89898900, 0x62626200, 0x97979700, 0x54545400, 0x5b5b5b00, + 0x1e1e1e00, 0x95959500, 0xe0e0e000, 0xffffff00, 0x64646400, 0xd2d2d200, + 0x10101000, 0xc4c4c400, 0x00000000, 0x48484800, 0xa3a3a300, 0xf7f7f700, + 0x75757500, 0xdbdbdb00, 0x8a8a8a00, 0x03030300, 0xe6e6e600, 0xdadada00, + 0x09090900, 0x3f3f3f00, 0xdddddd00, 0x94949400, 0x87878700, 0x5c5c5c00, + 0x83838300, 0x02020200, 0xcdcdcd00, 0x4a4a4a00, 0x90909000, 0x33333300, + 0x73737300, 0x67676700, 0xf6f6f600, 0xf3f3f300, 0x9d9d9d00, 0x7f7f7f00, + 0xbfbfbf00, 0xe2e2e200, 0x52525200, 0x9b9b9b00, 0xd8d8d800, 0x26262600, + 0xc8c8c800, 0x37373700, 0xc6c6c600, 0x3b3b3b00, 0x81818100, 0x96969600, + 0x6f6f6f00, 0x4b4b4b00, 0x13131300, 0xbebebe00, 0x63636300, 0x2e2e2e00, + 0xe9e9e900, 0x79797900, 0xa7a7a700, 0x8c8c8c00, 0x9f9f9f00, 0x6e6e6e00, + 0xbcbcbc00, 0x8e8e8e00, 0x29292900, 0xf5f5f500, 0xf9f9f900, 0xb6b6b600, + 0x2f2f2f00, 0xfdfdfd00, 0xb4b4b400, 0x59595900, 0x78787800, 0x98989800, + 0x06060600, 0x6a6a6a00, 0xe7e7e700, 0x46464600, 0x71717100, 0xbababa00, + 0xd4d4d400, 0x25252500, 0xababab00, 0x42424200, 0x88888800, 0xa2a2a200, + 0x8d8d8d00, 0xfafafa00, 0x72727200, 0x07070700, 0xb9b9b900, 0x55555500, + 0xf8f8f800, 0xeeeeee00, 0xacacac00, 0x0a0a0a00, 0x36363600, 0x49494900, + 0x2a2a2a00, 0x68686800, 0x3c3c3c00, 0x38383800, 0xf1f1f100, 0xa4a4a400, + 0x40404000, 0x28282800, 0xd3d3d300, 0x7b7b7b00, 0xbbbbbb00, 0xc9c9c900, + 0x43434300, 0xc1c1c100, 0x15151500, 0xe3e3e300, 0xadadad00, 0xf4f4f400, + 0x77777700, 0xc7c7c700, 0x80808000, 0x9e9e9e00 + }; + + private static final int SBOX4_4404[] = { + 0x70700070, 0x2c2c002c, 0xb3b300b3, 0xc0c000c0, 0xe4e400e4, 0x57570057, + 0xeaea00ea, 0xaeae00ae, 0x23230023, 0x6b6b006b, 0x45450045, 0xa5a500a5, + 0xeded00ed, 0x4f4f004f, 0x1d1d001d, 0x92920092, 0x86860086, 0xafaf00af, + 0x7c7c007c, 0x1f1f001f, 0x3e3e003e, 0xdcdc00dc, 0x5e5e005e, 0x0b0b000b, + 0xa6a600a6, 0x39390039, 0xd5d500d5, 0x5d5d005d, 0xd9d900d9, 0x5a5a005a, + 0x51510051, 0x6c6c006c, 0x8b8b008b, 0x9a9a009a, 0xfbfb00fb, 0xb0b000b0, + 0x74740074, 0x2b2b002b, 0xf0f000f0, 0x84840084, 0xdfdf00df, 0xcbcb00cb, + 0x34340034, 0x76760076, 0x6d6d006d, 0xa9a900a9, 0xd1d100d1, 0x04040004, + 0x14140014, 0x3a3a003a, 0xdede00de, 0x11110011, 0x32320032, 0x9c9c009c, + 0x53530053, 0xf2f200f2, 0xfefe00fe, 0xcfcf00cf, 0xc3c300c3, 0x7a7a007a, + 0x24240024, 0xe8e800e8, 0x60600060, 0x69690069, 0xaaaa00aa, 0xa0a000a0, + 0xa1a100a1, 0x62620062, 0x54540054, 0x1e1e001e, 0xe0e000e0, 0x64640064, + 0x10100010, 0x00000000, 0xa3a300a3, 0x75750075, 0x8a8a008a, 0xe6e600e6, + 0x09090009, 0xdddd00dd, 0x87870087, 0x83830083, 0xcdcd00cd, 0x90900090, + 0x73730073, 0xf6f600f6, 0x9d9d009d, 0xbfbf00bf, 0x52520052, 0xd8d800d8, + 0xc8c800c8, 0xc6c600c6, 0x81810081, 0x6f6f006f, 0x13130013, 0x63630063, + 0xe9e900e9, 0xa7a700a7, 0x9f9f009f, 0xbcbc00bc, 0x29290029, 0xf9f900f9, + 0x2f2f002f, 0xb4b400b4, 0x78780078, 0x06060006, 0xe7e700e7, 0x71710071, + 0xd4d400d4, 0xabab00ab, 0x88880088, 0x8d8d008d, 0x72720072, 0xb9b900b9, + 0xf8f800f8, 0xacac00ac, 0x36360036, 0x2a2a002a, 0x3c3c003c, 0xf1f100f1, + 0x40400040, 0xd3d300d3, 0xbbbb00bb, 0x43430043, 0x15150015, 0xadad00ad, + 0x77770077, 0x80800080, 0x82820082, 0xecec00ec, 0x27270027, 0xe5e500e5, + 0x85850085, 0x35350035, 0x0c0c000c, 0x41410041, 0xefef00ef, 0x93930093, + 0x19190019, 0x21210021, 0x0e0e000e, 0x4e4e004e, 0x65650065, 0xbdbd00bd, + 0xb8b800b8, 0x8f8f008f, 0xebeb00eb, 0xcece00ce, 0x30300030, 0x5f5f005f, + 0xc5c500c5, 0x1a1a001a, 0xe1e100e1, 0xcaca00ca, 0x47470047, 0x3d3d003d, + 0x01010001, 0xd6d600d6, 0x56560056, 0x4d4d004d, 0x0d0d000d, 0x66660066, + 0xcccc00cc, 0x2d2d002d, 0x12120012, 0x20200020, 0xb1b100b1, 0x99990099, + 0x4c4c004c, 0xc2c200c2, 0x7e7e007e, 0x05050005, 0xb7b700b7, 0x31310031, + 0x17170017, 0xd7d700d7, 0x58580058, 0x61610061, 0x1b1b001b, 0x1c1c001c, + 0x0f0f000f, 0x16160016, 0x18180018, 0x22220022, 0x44440044, 0xb2b200b2, + 0xb5b500b5, 0x91910091, 0x08080008, 0xa8a800a8, 0xfcfc00fc, 0x50500050, + 0xd0d000d0, 0x7d7d007d, 0x89890089, 0x97970097, 0x5b5b005b, 0x95950095, + 0xffff00ff, 0xd2d200d2, 0xc4c400c4, 0x48480048, 0xf7f700f7, 0xdbdb00db, + 0x03030003, 0xdada00da, 0x3f3f003f, 0x94940094, 0x5c5c005c, 0x02020002, + 0x4a4a004a, 0x33330033, 0x67670067, 0xf3f300f3, 0x7f7f007f, 0xe2e200e2, + 0x9b9b009b, 0x26260026, 0x37370037, 0x3b3b003b, 0x96960096, 0x4b4b004b, + 0xbebe00be, 0x2e2e002e, 0x79790079, 0x8c8c008c, 0x6e6e006e, 0x8e8e008e, + 0xf5f500f5, 0xb6b600b6, 0xfdfd00fd, 0x59590059, 0x98980098, 0x6a6a006a, + 0x46460046, 0xbaba00ba, 0x25250025, 0x42420042, 0xa2a200a2, 0xfafa00fa, + 0x07070007, 0x55550055, 0xeeee00ee, 0x0a0a000a, 0x49490049, 0x68680068, + 0x38380038, 0xa4a400a4, 0x28280028, 0x7b7b007b, 0xc9c900c9, 0xc1c100c1, + 0xe3e300e3, 0xf4f400f4, 0xc7c700c7, 0x9e9e009e + }; + + private static final int SBOX2_0222[] = { + 0x00e0e0e0, 0x00050505, 0x00585858, 0x00d9d9d9, 0x00676767, 0x004e4e4e, + 0x00818181, 0x00cbcbcb, 0x00c9c9c9, 0x000b0b0b, 0x00aeaeae, 0x006a6a6a, + 0x00d5d5d5, 0x00181818, 0x005d5d5d, 0x00828282, 0x00464646, 0x00dfdfdf, + 0x00d6d6d6, 0x00272727, 0x008a8a8a, 0x00323232, 0x004b4b4b, 0x00424242, + 0x00dbdbdb, 0x001c1c1c, 0x009e9e9e, 0x009c9c9c, 0x003a3a3a, 0x00cacaca, + 0x00252525, 0x007b7b7b, 0x000d0d0d, 0x00717171, 0x005f5f5f, 0x001f1f1f, + 0x00f8f8f8, 0x00d7d7d7, 0x003e3e3e, 0x009d9d9d, 0x007c7c7c, 0x00606060, + 0x00b9b9b9, 0x00bebebe, 0x00bcbcbc, 0x008b8b8b, 0x00161616, 0x00343434, + 0x004d4d4d, 0x00c3c3c3, 0x00727272, 0x00959595, 0x00ababab, 0x008e8e8e, + 0x00bababa, 0x007a7a7a, 0x00b3b3b3, 0x00020202, 0x00b4b4b4, 0x00adadad, + 0x00a2a2a2, 0x00acacac, 0x00d8d8d8, 0x009a9a9a, 0x00171717, 0x001a1a1a, + 0x00353535, 0x00cccccc, 0x00f7f7f7, 0x00999999, 0x00616161, 0x005a5a5a, + 0x00e8e8e8, 0x00242424, 0x00565656, 0x00404040, 0x00e1e1e1, 0x00636363, + 0x00090909, 0x00333333, 0x00bfbfbf, 0x00989898, 0x00979797, 0x00858585, + 0x00686868, 0x00fcfcfc, 0x00ececec, 0x000a0a0a, 0x00dadada, 0x006f6f6f, + 0x00535353, 0x00626262, 0x00a3a3a3, 0x002e2e2e, 0x00080808, 0x00afafaf, + 0x00282828, 0x00b0b0b0, 0x00747474, 0x00c2c2c2, 0x00bdbdbd, 0x00363636, + 0x00222222, 0x00383838, 0x00646464, 0x001e1e1e, 0x00393939, 0x002c2c2c, + 0x00a6a6a6, 0x00303030, 0x00e5e5e5, 0x00444444, 0x00fdfdfd, 0x00888888, + 0x009f9f9f, 0x00656565, 0x00878787, 0x006b6b6b, 0x00f4f4f4, 0x00232323, + 0x00484848, 0x00101010, 0x00d1d1d1, 0x00515151, 0x00c0c0c0, 0x00f9f9f9, + 0x00d2d2d2, 0x00a0a0a0, 0x00555555, 0x00a1a1a1, 0x00414141, 0x00fafafa, + 0x00434343, 0x00131313, 0x00c4c4c4, 0x002f2f2f, 0x00a8a8a8, 0x00b6b6b6, + 0x003c3c3c, 0x002b2b2b, 0x00c1c1c1, 0x00ffffff, 0x00c8c8c8, 0x00a5a5a5, + 0x00202020, 0x00898989, 0x00000000, 0x00909090, 0x00474747, 0x00efefef, + 0x00eaeaea, 0x00b7b7b7, 0x00151515, 0x00060606, 0x00cdcdcd, 0x00b5b5b5, + 0x00121212, 0x007e7e7e, 0x00bbbbbb, 0x00292929, 0x000f0f0f, 0x00b8b8b8, + 0x00070707, 0x00040404, 0x009b9b9b, 0x00949494, 0x00212121, 0x00666666, + 0x00e6e6e6, 0x00cecece, 0x00ededed, 0x00e7e7e7, 0x003b3b3b, 0x00fefefe, + 0x007f7f7f, 0x00c5c5c5, 0x00a4a4a4, 0x00373737, 0x00b1b1b1, 0x004c4c4c, + 0x00919191, 0x006e6e6e, 0x008d8d8d, 0x00767676, 0x00030303, 0x002d2d2d, + 0x00dedede, 0x00969696, 0x00262626, 0x007d7d7d, 0x00c6c6c6, 0x005c5c5c, + 0x00d3d3d3, 0x00f2f2f2, 0x004f4f4f, 0x00191919, 0x003f3f3f, 0x00dcdcdc, + 0x00797979, 0x001d1d1d, 0x00525252, 0x00ebebeb, 0x00f3f3f3, 0x006d6d6d, + 0x005e5e5e, 0x00fbfbfb, 0x00696969, 0x00b2b2b2, 0x00f0f0f0, 0x00313131, + 0x000c0c0c, 0x00d4d4d4, 0x00cfcfcf, 0x008c8c8c, 0x00e2e2e2, 0x00757575, + 0x00a9a9a9, 0x004a4a4a, 0x00575757, 0x00848484, 0x00111111, 0x00454545, + 0x001b1b1b, 0x00f5f5f5, 0x00e4e4e4, 0x000e0e0e, 0x00737373, 0x00aaaaaa, + 0x00f1f1f1, 0x00dddddd, 0x00595959, 0x00141414, 0x006c6c6c, 0x00929292, + 0x00545454, 0x00d0d0d0, 0x00787878, 0x00707070, 0x00e3e3e3, 0x00494949, + 0x00808080, 0x00505050, 0x00a7a7a7, 0x00f6f6f6, 0x00777777, 0x00939393, + 0x00868686, 0x00838383, 0x002a2a2a, 0x00c7c7c7, 0x005b5b5b, 0x00e9e9e9, + 0x00eeeeee, 0x008f8f8f, 0x00010101, 0x003d3d3d + }; + + private static final int SBOX3_3033[] = { + 0x38003838, 0x41004141, 0x16001616, 0x76007676, 0xd900d9d9, 0x93009393, + 0x60006060, 0xf200f2f2, 0x72007272, 0xc200c2c2, 0xab00abab, 0x9a009a9a, + 0x75007575, 0x06000606, 0x57005757, 0xa000a0a0, 0x91009191, 0xf700f7f7, + 0xb500b5b5, 0xc900c9c9, 0xa200a2a2, 0x8c008c8c, 0xd200d2d2, 0x90009090, + 0xf600f6f6, 0x07000707, 0xa700a7a7, 0x27002727, 0x8e008e8e, 0xb200b2b2, + 0x49004949, 0xde00dede, 0x43004343, 0x5c005c5c, 0xd700d7d7, 0xc700c7c7, + 0x3e003e3e, 0xf500f5f5, 0x8f008f8f, 0x67006767, 0x1f001f1f, 0x18001818, + 0x6e006e6e, 0xaf00afaf, 0x2f002f2f, 0xe200e2e2, 0x85008585, 0x0d000d0d, + 0x53005353, 0xf000f0f0, 0x9c009c9c, 0x65006565, 0xea00eaea, 0xa300a3a3, + 0xae00aeae, 0x9e009e9e, 0xec00ecec, 0x80008080, 0x2d002d2d, 0x6b006b6b, + 0xa800a8a8, 0x2b002b2b, 0x36003636, 0xa600a6a6, 0xc500c5c5, 0x86008686, + 0x4d004d4d, 0x33003333, 0xfd00fdfd, 0x66006666, 0x58005858, 0x96009696, + 0x3a003a3a, 0x09000909, 0x95009595, 0x10001010, 0x78007878, 0xd800d8d8, + 0x42004242, 0xcc00cccc, 0xef00efef, 0x26002626, 0xe500e5e5, 0x61006161, + 0x1a001a1a, 0x3f003f3f, 0x3b003b3b, 0x82008282, 0xb600b6b6, 0xdb00dbdb, + 0xd400d4d4, 0x98009898, 0xe800e8e8, 0x8b008b8b, 0x02000202, 0xeb00ebeb, + 0x0a000a0a, 0x2c002c2c, 0x1d001d1d, 0xb000b0b0, 0x6f006f6f, 0x8d008d8d, + 0x88008888, 0x0e000e0e, 0x19001919, 0x87008787, 0x4e004e4e, 0x0b000b0b, + 0xa900a9a9, 0x0c000c0c, 0x79007979, 0x11001111, 0x7f007f7f, 0x22002222, + 0xe700e7e7, 0x59005959, 0xe100e1e1, 0xda00dada, 0x3d003d3d, 0xc800c8c8, + 0x12001212, 0x04000404, 0x74007474, 0x54005454, 0x30003030, 0x7e007e7e, + 0xb400b4b4, 0x28002828, 0x55005555, 0x68006868, 0x50005050, 0xbe00bebe, + 0xd000d0d0, 0xc400c4c4, 0x31003131, 0xcb00cbcb, 0x2a002a2a, 0xad00adad, + 0x0f000f0f, 0xca00caca, 0x70007070, 0xff00ffff, 0x32003232, 0x69006969, + 0x08000808, 0x62006262, 0x00000000, 0x24002424, 0xd100d1d1, 0xfb00fbfb, + 0xba00baba, 0xed00eded, 0x45004545, 0x81008181, 0x73007373, 0x6d006d6d, + 0x84008484, 0x9f009f9f, 0xee00eeee, 0x4a004a4a, 0xc300c3c3, 0x2e002e2e, + 0xc100c1c1, 0x01000101, 0xe600e6e6, 0x25002525, 0x48004848, 0x99009999, + 0xb900b9b9, 0xb300b3b3, 0x7b007b7b, 0xf900f9f9, 0xce00cece, 0xbf00bfbf, + 0xdf00dfdf, 0x71007171, 0x29002929, 0xcd00cdcd, 0x6c006c6c, 0x13001313, + 0x64006464, 0x9b009b9b, 0x63006363, 0x9d009d9d, 0xc000c0c0, 0x4b004b4b, + 0xb700b7b7, 0xa500a5a5, 0x89008989, 0x5f005f5f, 0xb100b1b1, 0x17001717, + 0xf400f4f4, 0xbc00bcbc, 0xd300d3d3, 0x46004646, 0xcf00cfcf, 0x37003737, + 0x5e005e5e, 0x47004747, 0x94009494, 0xfa00fafa, 0xfc00fcfc, 0x5b005b5b, + 0x97009797, 0xfe00fefe, 0x5a005a5a, 0xac00acac, 0x3c003c3c, 0x4c004c4c, + 0x03000303, 0x35003535, 0xf300f3f3, 0x23002323, 0xb800b8b8, 0x5d005d5d, + 0x6a006a6a, 0x92009292, 0xd500d5d5, 0x21002121, 0x44004444, 0x51005151, + 0xc600c6c6, 0x7d007d7d, 0x39003939, 0x83008383, 0xdc00dcdc, 0xaa00aaaa, + 0x7c007c7c, 0x77007777, 0x56005656, 0x05000505, 0x1b001b1b, 0xa400a4a4, + 0x15001515, 0x34003434, 0x1e001e1e, 0x1c001c1c, 0xf800f8f8, 0x52005252, + 0x20002020, 0x14001414, 0xe900e9e9, 0xbd00bdbd, 0xdd00dddd, 0xe400e4e4, + 0xa100a1a1, 0xe000e0e0, 0x8a008a8a, 0xf100f1f1, 0xd600d6d6, 0x7a007a7a, + 0xbb00bbbb, 0xe300e3e3, 0x40004040, 0x4f004f4f + }; + + private static int rightRotate(int x, int s) + { + return (((x) >>> (s)) + ((x) << (32 - s))); + } + + private static int leftRotate(int x, int s) + { + return ((x) << (s)) + ((x) >>> (32 - s)); + } + + private static void roldq(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot)); + ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot)); + ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot)); + ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot)); + ki[0 + ioff] = ko[0 + ooff]; + ki[1 + ioff] = ko[1 + ooff]; + ki[2 + ioff] = ko[2 + ooff]; + ki[3 + ioff] = ko[3 + ooff]; + } + + private static void decroldq(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot)); + ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot)); + ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot)); + ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot)); + ki[0 + ioff] = ko[2 + ooff]; + ki[1 + ioff] = ko[3 + ooff]; + ki[2 + ioff] = ko[0 + ooff]; + ki[3 + ioff] = ko[1 + ooff]; + } + + private static void roldqo32(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot)); + ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot)); + ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot)); + ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot)); + ki[0 + ioff] = ko[0 + ooff]; + ki[1 + ioff] = ko[1 + ooff]; + ki[2 + ioff] = ko[2 + ooff]; + ki[3 + ioff] = ko[3 + ooff]; + } + + private static void decroldqo32(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot)); + ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot)); + ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot)); + ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot)); + ki[0 + ioff] = ko[2 + ooff]; + ki[1 + ioff] = ko[3 + ooff]; + ki[2 + ioff] = ko[0 + ooff]; + ki[3 + ioff] = ko[1 + ooff]; + } + + private int bytes2int(byte[] src, int offset) + { + int word = 0; + + for (int i = 0; i < 4; i++) + { + word = (word << 8) + (src[i + offset] & MASK8); + } + return word; + } + + private void int2bytes(int word, byte[] dst, int offset) + { + for (int i = 0; i < 4; i++) + { + dst[(3 - i) + offset] = (byte)word; + word >>>= 8; + } + } + + private void camelliaF2(int[] s, int[] skey, int keyoff) + { + int t1, t2, u, v; + + t1 = s[0] ^ skey[0 + keyoff]; + u = SBOX4_4404[t1 & MASK8]; + u ^= SBOX3_3033[(t1 >>> 8) & MASK8]; + u ^= SBOX2_0222[(t1 >>> 16) & MASK8]; + u ^= SBOX1_1110[(t1 >>> 24) & MASK8]; + t2 = s[1] ^ skey[1 + keyoff]; + v = SBOX1_1110[t2 & MASK8]; + v ^= SBOX4_4404[(t2 >>> 8) & MASK8]; + v ^= SBOX3_3033[(t2 >>> 16) & MASK8]; + v ^= SBOX2_0222[(t2 >>> 24) & MASK8]; + + s[2] ^= u ^ v; + s[3] ^= u ^ v ^ rightRotate(u, 8); + + t1 = s[2] ^ skey[2 + keyoff]; + u = SBOX4_4404[t1 & MASK8]; + u ^= SBOX3_3033[(t1 >>> 8) & MASK8]; + u ^= SBOX2_0222[(t1 >>> 16) & MASK8]; + u ^= SBOX1_1110[(t1 >>> 24) & MASK8]; + t2 = s[3] ^ skey[3 + keyoff]; + v = SBOX1_1110[t2 & MASK8]; + v ^= SBOX4_4404[(t2 >>> 8) & MASK8]; + v ^= SBOX3_3033[(t2 >>> 16) & MASK8]; + v ^= SBOX2_0222[(t2 >>> 24) & MASK8]; + + s[0] ^= u ^ v; + s[1] ^= u ^ v ^ rightRotate(u, 8); + } + + private void camelliaFLs(int[] s, int[] fkey, int keyoff) + { + + s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1); + s[0] ^= fkey[1 + keyoff] | s[1]; + + s[2] ^= fkey[3 + keyoff] | s[3]; + s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1); + } + + private void setKey(boolean forEncryption, byte[] key) + { + int[] k = new int[8]; + int[] ka = new int[4]; + int[] kb = new int[4]; + int[] t = new int[4]; + + switch (key.length) + { + case 16: + _keyIs128 = true; + k[0] = bytes2int(key, 0); + k[1] = bytes2int(key, 4); + k[2] = bytes2int(key, 8); + k[3] = bytes2int(key, 12); + k[4] = k[5] = k[6] = k[7] = 0; + break; + case 24: + k[0] = bytes2int(key, 0); + k[1] = bytes2int(key, 4); + k[2] = bytes2int(key, 8); + k[3] = bytes2int(key, 12); + k[4] = bytes2int(key, 16); + k[5] = bytes2int(key, 20); + k[6] = ~k[4]; + k[7] = ~k[5]; + _keyIs128 = false; + break; + case 32: + k[0] = bytes2int(key, 0); + k[1] = bytes2int(key, 4); + k[2] = bytes2int(key, 8); + k[3] = bytes2int(key, 12); + k[4] = bytes2int(key, 16); + k[5] = bytes2int(key, 20); + k[6] = bytes2int(key, 24); + k[7] = bytes2int(key, 28); + _keyIs128 = false; + break; + default: + throw new + IllegalArgumentException("key sizes are only 16/24/32 bytes."); + } + + for (int i = 0; i < 4; i++) + { + ka[i] = k[i] ^ k[i + 4]; + } + /* compute KA */ + camelliaF2(ka, SIGMA, 0); + for (int i = 0; i < 4; i++) + { + ka[i] ^= k[i]; + } + camelliaF2(ka, SIGMA, 4); + + if (_keyIs128) + { + if (forEncryption) + { + /* KL dependant keys */ + kw[0] = k[0]; + kw[1] = k[1]; + kw[2] = k[2]; + kw[3] = k[3]; + roldq(15, k, 0, subkey, 4); + roldq(30, k, 0, subkey, 12); + roldq(15, k, 0, t, 0); + subkey[18] = t[2]; + subkey[19] = t[3]; + roldq(17, k, 0, ke, 4); + roldq(17, k, 0, subkey, 24); + roldq(17, k, 0, subkey, 32); + /* KA dependant keys */ + subkey[0] = ka[0]; + subkey[1] = ka[1]; + subkey[2] = ka[2]; + subkey[3] = ka[3]; + roldq(15, ka, 0, subkey, 8); + roldq(15, ka, 0, ke, 0); + roldq(15, ka, 0, t, 0); + subkey[16] = t[0]; + subkey[17] = t[1]; + roldq(15, ka, 0, subkey, 20); + roldqo32(34, ka, 0, subkey, 28); + roldq(17, ka, 0, kw, 4); + + } + else + { // decryption + /* KL dependant keys */ + kw[4] = k[0]; + kw[5] = k[1]; + kw[6] = k[2]; + kw[7] = k[3]; + decroldq(15, k, 0, subkey, 28); + decroldq(30, k, 0, subkey, 20); + decroldq(15, k, 0, t, 0); + subkey[16] = t[0]; + subkey[17] = t[1]; + decroldq(17, k, 0, ke, 0); + decroldq(17, k, 0, subkey, 8); + decroldq(17, k, 0, subkey, 0); + /* KA dependant keys */ + subkey[34] = ka[0]; + subkey[35] = ka[1]; + subkey[32] = ka[2]; + subkey[33] = ka[3]; + decroldq(15, ka, 0, subkey, 24); + decroldq(15, ka, 0, ke, 4); + decroldq(15, ka, 0, t, 0); + subkey[18] = t[2]; + subkey[19] = t[3]; + decroldq(15, ka, 0, subkey, 12); + decroldqo32(34, ka, 0, subkey, 4); + roldq(17, ka, 0, kw, 0); + } + } + else + { // 192bit or 256bit + /* compute KB */ + for (int i = 0; i < 4; i++) + { + kb[i] = ka[i] ^ k[i + 4]; + } + camelliaF2(kb, SIGMA, 8); + + if (forEncryption) + { + /* KL dependant keys */ + kw[0] = k[0]; + kw[1] = k[1]; + kw[2] = k[2]; + kw[3] = k[3]; + roldqo32(45, k, 0, subkey, 16); + roldq(15, k, 0, ke, 4); + roldq(17, k, 0, subkey, 32); + roldqo32(34, k, 0, subkey, 44); + /* KR dependant keys */ + roldq(15, k, 4, subkey, 4); + roldq(15, k, 4, ke, 0); + roldq(30, k, 4, subkey, 24); + roldqo32(34, k, 4, subkey, 36); + /* KA dependant keys */ + roldq(15, ka, 0, subkey, 8); + roldq(30, ka, 0, subkey, 20); + /* 32bit rotation */ + ke[8] = ka[1]; + ke[9] = ka[2]; + ke[10] = ka[3]; + ke[11] = ka[0]; + roldqo32(49, ka, 0, subkey, 40); + + /* KB dependant keys */ + subkey[0] = kb[0]; + subkey[1] = kb[1]; + subkey[2] = kb[2]; + subkey[3] = kb[3]; + roldq(30, kb, 0, subkey, 12); + roldq(30, kb, 0, subkey, 28); + roldqo32(51, kb, 0, kw, 4); + + } + else + { // decryption + /* KL dependant keys */ + kw[4] = k[0]; + kw[5] = k[1]; + kw[6] = k[2]; + kw[7] = k[3]; + decroldqo32(45, k, 0, subkey, 28); + decroldq(15, k, 0, ke, 4); + decroldq(17, k, 0, subkey, 12); + decroldqo32(34, k, 0, subkey, 0); + /* KR dependant keys */ + decroldq(15, k, 4, subkey, 40); + decroldq(15, k, 4, ke, 8); + decroldq(30, k, 4, subkey, 20); + decroldqo32(34, k, 4, subkey, 8); + /* KA dependant keys */ + decroldq(15, ka, 0, subkey, 36); + decroldq(30, ka, 0, subkey, 24); + /* 32bit rotation */ + ke[2] = ka[1]; + ke[3] = ka[2]; + ke[0] = ka[3]; + ke[1] = ka[0]; + decroldqo32(49, ka, 0, subkey, 4); + + /* KB dependant keys */ + subkey[46] = kb[0]; + subkey[47] = kb[1]; + subkey[44] = kb[2]; + subkey[45] = kb[3]; + decroldq(30, kb, 0, subkey, 32); + decroldq(30, kb, 0, subkey, 16); + roldqo32(51, kb, 0, kw, 0); + } + } + } + + private int processBlock128(byte[] in, int inOff, + byte[] out, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2int(in, inOff + (i * 4)); + state[i] ^= kw[i]; + } + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + + state[2] ^= kw[4]; + state[3] ^= kw[5]; + state[0] ^= kw[6]; + state[1] ^= kw[7]; + + int2bytes(state[2], out, outOff); + int2bytes(state[3], out, outOff + 4); + int2bytes(state[0], out, outOff + 8); + int2bytes(state[1], out, outOff + 12); + + return BLOCK_SIZE; + } + + private int processBlock192or256(byte[] in, int inOff, + byte[] out, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2int(in, inOff + (i * 4)); + state[i] ^= kw[i]; + } + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + camelliaFLs(state, ke, 8); + camelliaF2(state, subkey, 36); + camelliaF2(state, subkey, 40); + camelliaF2(state, subkey, 44); + + state[2] ^= kw[4]; + state[3] ^= kw[5]; + state[0] ^= kw[6]; + state[1] ^= kw[7]; + + int2bytes(state[2], out, outOff); + int2bytes(state[3], out, outOff + 4); + int2bytes(state[0], out, outOff + 8); + int2bytes(state[1], out, outOff + 12); + return BLOCK_SIZE; + } + + public CamelliaEngine() + { + } + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("only simple KeyParameter expected."); + } + + setKey(forEncryption, ((KeyParameter)params).getKey()); + initialised = true; + } + + public String getAlgorithmName() + { + return "Camellia"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if (!initialised) + { + throw new IllegalStateException("Camellia engine 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 (_keyIs128) + { + return processBlock128(in, inOff, out, outOff); + } + else + { + return processBlock192or256(in, inOff, out, outOff); + } + } + + public void reset() + { + // nothing + + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java new file mode 100644 index 000000000..bc8dea4a9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java @@ -0,0 +1,592 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * Camellia - based on RFC 3713, smaller implementation, about half the size of CamelliaEngine. + */ + +public class CamelliaLightEngine + implements BlockCipher +{ + private static final int BLOCK_SIZE = 16; + private static final int MASK8 = 0xff; + private boolean initialized; + private boolean _keyis128; + + private int[] subkey = new int[24 * 4]; + private int[] kw = new int[4 * 2]; // for whitening + private int[] ke = new int[6 * 2]; // for FL and FL^(-1) + private int[] state = new int[4]; // for encryption and decryption + + private static final int SIGMA[] = { + 0xa09e667f, 0x3bcc908b, + 0xb67ae858, 0x4caa73b2, + 0xc6ef372f, 0xe94f82be, + 0x54ff53a5, 0xf1d36f1c, + 0x10e527fa, 0xde682d1d, + 0xb05688c2, 0xb3e6c1fd + }; + + /* + * + * S-box data + * + */ + private static final byte SBOX1[] = { + (byte)112, (byte)130, (byte)44, (byte)236, + (byte)179, (byte)39, (byte)192, (byte)229, + (byte)228, (byte)133, (byte)87, (byte)53, + (byte)234, (byte)12, (byte)174, (byte)65, + (byte)35, (byte)239, (byte)107, (byte)147, + (byte)69, (byte)25, (byte)165, (byte)33, + (byte)237, (byte)14, (byte)79, (byte)78, + (byte)29, (byte)101, (byte)146, (byte)189, + (byte)134, (byte)184, (byte)175, (byte)143, + (byte)124, (byte)235, (byte)31, (byte)206, + (byte)62, (byte)48, (byte)220, (byte)95, + (byte)94, (byte)197, (byte)11, (byte)26, + (byte)166, (byte)225, (byte)57, (byte)202, + (byte)213, (byte)71, (byte)93, (byte)61, + (byte)217, (byte)1, (byte)90, (byte)214, + (byte)81, (byte)86, (byte)108, (byte)77, + (byte)139, (byte)13, (byte)154, (byte)102, + (byte)251, (byte)204, (byte)176, (byte)45, + (byte)116, (byte)18, (byte)43, (byte)32, + (byte)240, (byte)177, (byte)132, (byte)153, + (byte)223, (byte)76, (byte)203, (byte)194, + (byte)52, (byte)126, (byte)118, (byte)5, + (byte)109, (byte)183, (byte)169, (byte)49, + (byte)209, (byte)23, (byte)4, (byte)215, + (byte)20, (byte)88, (byte)58, (byte)97, + (byte)222, (byte)27, (byte)17, (byte)28, + (byte)50, (byte)15, (byte)156, (byte)22, + (byte)83, (byte)24, (byte)242, (byte)34, + (byte)254, (byte)68, (byte)207, (byte)178, + (byte)195, (byte)181, (byte)122, (byte)145, + (byte)36, (byte)8, (byte)232, (byte)168, + (byte)96, (byte)252, (byte)105, (byte)80, + (byte)170, (byte)208, (byte)160, (byte)125, + (byte)161, (byte)137, (byte)98, (byte)151, + (byte)84, (byte)91, (byte)30, (byte)149, + (byte)224, (byte)255, (byte)100, (byte)210, + (byte)16, (byte)196, (byte)0, (byte)72, + (byte)163, (byte)247, (byte)117, (byte)219, + (byte)138, (byte)3, (byte)230, (byte)218, + (byte)9, (byte)63, (byte)221, (byte)148, + (byte)135, (byte)92, (byte)131, (byte)2, + (byte)205, (byte)74, (byte)144, (byte)51, + (byte)115, (byte)103, (byte)246, (byte)243, + (byte)157, (byte)127, (byte)191, (byte)226, + (byte)82, (byte)155, (byte)216, (byte)38, + (byte)200, (byte)55, (byte)198, (byte)59, + (byte)129, (byte)150, (byte)111, (byte)75, + (byte)19, (byte)190, (byte)99, (byte)46, + (byte)233, (byte)121, (byte)167, (byte)140, + (byte)159, (byte)110, (byte)188, (byte)142, + (byte)41, (byte)245, (byte)249, (byte)182, + (byte)47, (byte)253, (byte)180, (byte)89, + (byte)120, (byte)152, (byte)6, (byte)106, + (byte)231, (byte)70, (byte)113, (byte)186, + (byte)212, (byte)37, (byte)171, (byte)66, + (byte)136, (byte)162, (byte)141, (byte)250, + (byte)114, (byte)7, (byte)185, (byte)85, + (byte)248, (byte)238, (byte)172, (byte)10, + (byte)54, (byte)73, (byte)42, (byte)104, + (byte)60, (byte)56, (byte)241, (byte)164, + (byte)64, (byte)40, (byte)211, (byte)123, + (byte)187, (byte)201, (byte)67, (byte)193, + (byte)21, (byte)227, (byte)173, (byte)244, + (byte)119, (byte)199, (byte)128, (byte)158 + }; + + private static int rightRotate(int x, int s) + { + return (((x) >>> (s)) + ((x) << (32 - s))); + } + + private static int leftRotate(int x, int s) + { + return ((x) << (s)) + ((x) >>> (32 - s)); + } + + private static void roldq(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot)); + ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot)); + ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot)); + ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot)); + ki[0 + ioff] = ko[0 + ooff]; + ki[1 + ioff] = ko[1 + ooff]; + ki[2 + ioff] = ko[2 + ooff]; + ki[3 + ioff] = ko[3 + ooff]; + } + + private static void decroldq(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot)); + ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot)); + ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot)); + ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot)); + ki[0 + ioff] = ko[2 + ooff]; + ki[1 + ioff] = ko[3 + ooff]; + ki[2 + ioff] = ko[0 + ooff]; + ki[3 + ioff] = ko[1 + ooff]; + } + + private static void roldqo32(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot)); + ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot)); + ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot)); + ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot)); + ki[0 + ioff] = ko[0 + ooff]; + ki[1 + ioff] = ko[1 + ooff]; + ki[2 + ioff] = ko[2 + ooff]; + ki[3 + ioff] = ko[3 + ooff]; + } + + private static void decroldqo32(int rot, int[] ki, int ioff, + int[] ko, int ooff) + { + ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot)); + ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot)); + ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot)); + ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot)); + ki[0 + ioff] = ko[2 + ooff]; + ki[1 + ioff] = ko[3 + ooff]; + ki[2 + ioff] = ko[0 + ooff]; + ki[3 + ioff] = ko[1 + ooff]; + } + + private int bytes2int(byte[] src, int offset) + { + int word = 0; + + for (int i = 0; i < 4; i++) + { + word = (word << 8) + (src[i + offset] & MASK8); + } + return word; + } + + private void int2bytes(int word, byte[] dst, int offset) + { + for (int i = 0; i < 4; i++) + { + dst[(3 - i) + offset] = (byte)word; + word >>>= 8; + } + } + + private byte lRot8(byte v, int rot) + { + return (byte)((v << rot) | ((v & 0xff) >>> (8 - rot))); + } + + private int sbox2(int x) + { + return (lRot8(SBOX1[x], 1) & MASK8); + } + + private int sbox3(int x) + { + return (lRot8(SBOX1[x], 7) & MASK8); + } + + private int sbox4(int x) + { + return (SBOX1[((int)lRot8((byte)x, 1) & MASK8)] & MASK8); + } + + private void camelliaF2(int[] s, int[] skey, int keyoff) + { + int t1, t2, u, v; + + t1 = s[0] ^ skey[0 + keyoff]; + u = sbox4((t1 & MASK8)); + u |= (sbox3(((t1 >>> 8) & MASK8)) << 8); + u |= (sbox2(((t1 >>> 16) & MASK8)) << 16); + u |= ((int)(SBOX1[((t1 >>> 24) & MASK8)] & MASK8) << 24); + + t2 = s[1] ^ skey[1 + keyoff]; + v = (int)SBOX1[(t2 & MASK8)] & MASK8; + v |= (sbox4(((t2 >>> 8) & MASK8)) << 8); + v |= (sbox3(((t2 >>> 16) & MASK8)) << 16); + v |= (sbox2(((t2 >>> 24) & MASK8)) << 24); + + v = leftRotate(v, 8); + u ^= v; + v = leftRotate(v, 8) ^ u; + u = rightRotate(u, 8) ^ v; + s[2] ^= leftRotate(v, 16) ^ u; + s[3] ^= leftRotate(u, 8); + + t1 = s[2] ^ skey[2 + keyoff]; + u = sbox4((t1 & MASK8)); + u |= sbox3(((t1 >>> 8) & MASK8)) << 8; + u |= sbox2(((t1 >>> 16) & MASK8)) << 16; + u |= ((int)SBOX1[((t1 >>> 24) & MASK8)] & MASK8) << 24; + + t2 = s[3] ^ skey[3 + keyoff]; + v = ((int)SBOX1[(t2 & MASK8)] & MASK8); + v |= sbox4(((t2 >>> 8) & MASK8)) << 8; + v |= sbox3(((t2 >>> 16) & MASK8)) << 16; + v |= sbox2(((t2 >>> 24) & MASK8)) << 24; + + v = leftRotate(v, 8); + u ^= v; + v = leftRotate(v, 8) ^ u; + u = rightRotate(u, 8) ^ v; + s[0] ^= leftRotate(v, 16) ^ u; + s[1] ^= leftRotate(u, 8); + } + + private void camelliaFLs(int[] s, int[] fkey, int keyoff) + { + + s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1); + s[0] ^= fkey[1 + keyoff] | s[1]; + + s[2] ^= fkey[3 + keyoff] | s[3]; + s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1); + } + + private void setKey(boolean forEncryption, byte[] key) + { + int[] k = new int[8]; + int[] ka = new int[4]; + int[] kb = new int[4]; + int[] t = new int[4]; + + switch (key.length) + { + case 16: + _keyis128 = true; + k[0] = bytes2int(key, 0); + k[1] = bytes2int(key, 4); + k[2] = bytes2int(key, 8); + k[3] = bytes2int(key, 12); + k[4] = k[5] = k[6] = k[7] = 0; + break; + case 24: + k[0] = bytes2int(key, 0); + k[1] = bytes2int(key, 4); + k[2] = bytes2int(key, 8); + k[3] = bytes2int(key, 12); + k[4] = bytes2int(key, 16); + k[5] = bytes2int(key, 20); + k[6] = ~k[4]; + k[7] = ~k[5]; + _keyis128 = false; + break; + case 32: + k[0] = bytes2int(key, 0); + k[1] = bytes2int(key, 4); + k[2] = bytes2int(key, 8); + k[3] = bytes2int(key, 12); + k[4] = bytes2int(key, 16); + k[5] = bytes2int(key, 20); + k[6] = bytes2int(key, 24); + k[7] = bytes2int(key, 28); + _keyis128 = false; + break; + default: + throw new + IllegalArgumentException("key sizes are only 16/24/32 bytes."); + } + + for (int i = 0; i < 4; i++) + { + ka[i] = k[i] ^ k[i + 4]; + } + /* compute KA */ + camelliaF2(ka, SIGMA, 0); + for (int i = 0; i < 4; i++) + { + ka[i] ^= k[i]; + } + camelliaF2(ka, SIGMA, 4); + + if (_keyis128) + { + if (forEncryption) + { + /* KL dependant keys */ + kw[0] = k[0]; + kw[1] = k[1]; + kw[2] = k[2]; + kw[3] = k[3]; + roldq(15, k, 0, subkey, 4); + roldq(30, k, 0, subkey, 12); + roldq(15, k, 0, t, 0); + subkey[18] = t[2]; + subkey[19] = t[3]; + roldq(17, k, 0, ke, 4); + roldq(17, k, 0, subkey, 24); + roldq(17, k, 0, subkey, 32); + /* KA dependant keys */ + subkey[0] = ka[0]; + subkey[1] = ka[1]; + subkey[2] = ka[2]; + subkey[3] = ka[3]; + roldq(15, ka, 0, subkey, 8); + roldq(15, ka, 0, ke, 0); + roldq(15, ka, 0, t, 0); + subkey[16] = t[0]; + subkey[17] = t[1]; + roldq(15, ka, 0, subkey, 20); + roldqo32(34, ka, 0, subkey, 28); + roldq(17, ka, 0, kw, 4); + + } + else + { // decryption + /* KL dependant keys */ + kw[4] = k[0]; + kw[5] = k[1]; + kw[6] = k[2]; + kw[7] = k[3]; + decroldq(15, k, 0, subkey, 28); + decroldq(30, k, 0, subkey, 20); + decroldq(15, k, 0, t, 0); + subkey[16] = t[0]; + subkey[17] = t[1]; + decroldq(17, k, 0, ke, 0); + decroldq(17, k, 0, subkey, 8); + decroldq(17, k, 0, subkey, 0); + /* KA dependant keys */ + subkey[34] = ka[0]; + subkey[35] = ka[1]; + subkey[32] = ka[2]; + subkey[33] = ka[3]; + decroldq(15, ka, 0, subkey, 24); + decroldq(15, ka, 0, ke, 4); + decroldq(15, ka, 0, t, 0); + subkey[18] = t[2]; + subkey[19] = t[3]; + decroldq(15, ka, 0, subkey, 12); + decroldqo32(34, ka, 0, subkey, 4); + roldq(17, ka, 0, kw, 0); + } + } + else + { // 192bit or 256bit + /* compute KB */ + for (int i = 0; i < 4; i++) + { + kb[i] = ka[i] ^ k[i + 4]; + } + camelliaF2(kb, SIGMA, 8); + + if (forEncryption) + { + /* KL dependant keys */ + kw[0] = k[0]; + kw[1] = k[1]; + kw[2] = k[2]; + kw[3] = k[3]; + roldqo32(45, k, 0, subkey, 16); + roldq(15, k, 0, ke, 4); + roldq(17, k, 0, subkey, 32); + roldqo32(34, k, 0, subkey, 44); + /* KR dependant keys */ + roldq(15, k, 4, subkey, 4); + roldq(15, k, 4, ke, 0); + roldq(30, k, 4, subkey, 24); + roldqo32(34, k, 4, subkey, 36); + /* KA dependant keys */ + roldq(15, ka, 0, subkey, 8); + roldq(30, ka, 0, subkey, 20); + /* 32bit rotation */ + ke[8] = ka[1]; + ke[9] = ka[2]; + ke[10] = ka[3]; + ke[11] = ka[0]; + roldqo32(49, ka, 0, subkey, 40); + + /* KB dependant keys */ + subkey[0] = kb[0]; + subkey[1] = kb[1]; + subkey[2] = kb[2]; + subkey[3] = kb[3]; + roldq(30, kb, 0, subkey, 12); + roldq(30, kb, 0, subkey, 28); + roldqo32(51, kb, 0, kw, 4); + + } + else + { // decryption + /* KL dependant keys */ + kw[4] = k[0]; + kw[5] = k[1]; + kw[6] = k[2]; + kw[7] = k[3]; + decroldqo32(45, k, 0, subkey, 28); + decroldq(15, k, 0, ke, 4); + decroldq(17, k, 0, subkey, 12); + decroldqo32(34, k, 0, subkey, 0); + /* KR dependant keys */ + decroldq(15, k, 4, subkey, 40); + decroldq(15, k, 4, ke, 8); + decroldq(30, k, 4, subkey, 20); + decroldqo32(34, k, 4, subkey, 8); + /* KA dependant keys */ + decroldq(15, ka, 0, subkey, 36); + decroldq(30, ka, 0, subkey, 24); + /* 32bit rotation */ + ke[2] = ka[1]; + ke[3] = ka[2]; + ke[0] = ka[3]; + ke[1] = ka[0]; + decroldqo32(49, ka, 0, subkey, 4); + + /* KB dependant keys */ + subkey[46] = kb[0]; + subkey[47] = kb[1]; + subkey[44] = kb[2]; + subkey[45] = kb[3]; + decroldq(30, kb, 0, subkey, 32); + decroldq(30, kb, 0, subkey, 16); + roldqo32(51, kb, 0, kw, 0); + } + } + } + + private int processBlock128(byte[] in, int inOff, + byte[] out, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2int(in, inOff + (i * 4)); + state[i] ^= kw[i]; + } + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + + state[2] ^= kw[4]; + state[3] ^= kw[5]; + state[0] ^= kw[6]; + state[1] ^= kw[7]; + + int2bytes(state[2], out, outOff); + int2bytes(state[3], out, outOff + 4); + int2bytes(state[0], out, outOff + 8); + int2bytes(state[1], out, outOff + 12); + + return BLOCK_SIZE; + } + + private int processBlock192or256(byte[] in, int inOff, + byte[] out, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2int(in, inOff + (i * 4)); + state[i] ^= kw[i]; + } + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + camelliaFLs(state, ke, 8); + camelliaF2(state, subkey, 36); + camelliaF2(state, subkey, 40); + camelliaF2(state, subkey, 44); + + state[2] ^= kw[4]; + state[3] ^= kw[5]; + state[0] ^= kw[6]; + state[1] ^= kw[7]; + + int2bytes(state[2], out, outOff); + int2bytes(state[3], out, outOff + 4); + int2bytes(state[0], out, outOff + 8); + int2bytes(state[1], out, outOff + 12); + return BLOCK_SIZE; + } + + public CamelliaLightEngine() + { + } + + public String getAlgorithmName() + { + return "Camellia"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public void init(boolean forEncryption, CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("only simple KeyParameter expected."); + } + + setKey(forEncryption, ((KeyParameter)params).getKey()); + initialized = true; + } + + public int processBlock(byte[] in, int inOff, + byte[] out, int outOff) + throws IllegalStateException + { + + if (!initialized) + { + throw new IllegalStateException("Camellia is not initialized"); + } + + 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 (_keyis128) + { + return processBlock128(in, inOff, out, outOff); + } + else + { + return processBlock192or256(in, inOff, out, outOff); + } + } + + public void reset() + { + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java new file mode 100644 index 000000000..04526eb16 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.engines; + +/** + * An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394. + *
+ * For further details see: http://www.ietf.org/rfc/rfc3657.txt. + */ +public class CamelliaWrapEngine + extends RFC3394WrapEngine +{ + public CamelliaWrapEngine() + { + super(new CamelliaEngine()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java new file mode 100644 index 000000000..a6a0138b9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java @@ -0,0 +1,186 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.util.Pack; + +/** + * Implementation of Daniel J. Bernstein's ChaCha stream cipher. + */ +public class ChaChaEngine extends Salsa20Engine +{ + + /** + * Creates a 20 rounds ChaCha engine. + */ + public ChaChaEngine() + { + super(); + } + + /** + * Creates a ChaCha engine with a specific number of rounds. + * @param rounds the number of rounds (must be an even number). + */ + public ChaChaEngine(int rounds) + { + super(rounds); + } + + public String getAlgorithmName() + { + return "ChaCha" + rounds; + } + + protected void advanceCounter() + { + if (++engineState[12] == 0) + { + ++engineState[13]; + } + } + + protected void resetCounter() + { + engineState[12] = engineState[13] = 0; + } + + protected void setKey(byte[] keyBytes, byte[] ivBytes) + { + if ((keyBytes.length != 16) && (keyBytes.length != 32)) + { + throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key"); + } + + int offset = 0; + byte[] constants; + + // Key + engineState[4] = Pack.littleEndianToInt(keyBytes, 0); + engineState[5] = Pack.littleEndianToInt(keyBytes, 4); + engineState[6] = Pack.littleEndianToInt(keyBytes, 8); + engineState[7] = Pack.littleEndianToInt(keyBytes, 12); + + if (keyBytes.length == 32) + { + constants = sigma; + offset = 16; + } else + { + constants = tau; + } + + engineState[8] = Pack.littleEndianToInt(keyBytes, offset); + engineState[9] = Pack.littleEndianToInt(keyBytes, offset + 4); + engineState[10] = Pack.littleEndianToInt(keyBytes, offset + 8); + engineState[11] = Pack.littleEndianToInt(keyBytes, offset + 12); + + engineState[0] = Pack.littleEndianToInt(constants, 0); + engineState[1] = Pack.littleEndianToInt(constants, 4); + engineState[2] = Pack.littleEndianToInt(constants, 8); + engineState[3] = Pack.littleEndianToInt(constants, 12); + + // Counter + engineState[12] = engineState[13] = 0; + + // IV + engineState[14] = Pack.littleEndianToInt(ivBytes, 0); + engineState[15] = Pack.littleEndianToInt(ivBytes, 4); + } + + protected void generateKeyStream(byte[] output) + { + chachaCore(rounds, engineState, x); + Pack.intToLittleEndian(x, output, 0); + } + + /** + * ChacCha function + * + * @param input input data + * + * @return keystream + */ + public static void chachaCore(int rounds, int[] input, int[] x) + { + if (input.length != 16) { + throw new IllegalArgumentException(); + } + if (x.length != 16) { + throw new IllegalArgumentException(); + } + if (rounds % 2 != 0) { + throw new IllegalArgumentException("Number of rounds must be even"); + } + + int x00 = input[ 0]; + int x01 = input[ 1]; + int x02 = input[ 2]; + int x03 = input[ 3]; + int x04 = input[ 4]; + int x05 = input[ 5]; + int x06 = input[ 6]; + int x07 = input[ 7]; + int x08 = input[ 8]; + int x09 = input[ 9]; + int x10 = input[10]; + int x11 = input[11]; + int x12 = input[12]; + int x13 = input[13]; + int x14 = input[14]; + int x15 = input[15]; + + for (int i = rounds; i > 0; i -= 2) + { + x00 += x04; x12 = rotl(x12 ^ x00, 16); + x08 += x12; x04 = rotl(x04 ^ x08, 12); + x00 += x04; x12 = rotl(x12 ^ x00, 8); + x08 += x12; x04 = rotl(x04 ^ x08, 7); + x01 += x05; x13 = rotl(x13 ^ x01, 16); + x09 += x13; x05 = rotl(x05 ^ x09, 12); + x01 += x05; x13 = rotl(x13 ^ x01, 8); + x09 += x13; x05 = rotl(x05 ^ x09, 7); + x02 += x06; x14 = rotl(x14 ^ x02, 16); + x10 += x14; x06 = rotl(x06 ^ x10, 12); + x02 += x06; x14 = rotl(x14 ^ x02, 8); + x10 += x14; x06 = rotl(x06 ^ x10, 7); + x03 += x07; x15 = rotl(x15 ^ x03, 16); + x11 += x15; x07 = rotl(x07 ^ x11, 12); + x03 += x07; x15 = rotl(x15 ^ x03, 8); + x11 += x15; x07 = rotl(x07 ^ x11, 7); + x00 += x05; x15 = rotl(x15 ^ x00, 16); + x10 += x15; x05 = rotl(x05 ^ x10, 12); + x00 += x05; x15 = rotl(x15 ^ x00, 8); + x10 += x15; x05 = rotl(x05 ^ x10, 7); + x01 += x06; x12 = rotl(x12 ^ x01, 16); + x11 += x12; x06 = rotl(x06 ^ x11, 12); + x01 += x06; x12 = rotl(x12 ^ x01, 8); + x11 += x12; x06 = rotl(x06 ^ x11, 7); + x02 += x07; x13 = rotl(x13 ^ x02, 16); + x08 += x13; x07 = rotl(x07 ^ x08, 12); + x02 += x07; x13 = rotl(x13 ^ x02, 8); + x08 += x13; x07 = rotl(x07 ^ x08, 7); + x03 += x04; x14 = rotl(x14 ^ x03, 16); + x09 += x14; x04 = rotl(x04 ^ x09, 12); + x03 += x04; x14 = rotl(x14 ^ x03, 8); + x09 += x14; x04 = rotl(x04 ^ x09, 7); + + } + + x[ 0] = x00 + input[ 0]; + x[ 1] = x01 + input[ 1]; + x[ 2] = x02 + input[ 2]; + x[ 3] = x03 + input[ 3]; + x[ 4] = x04 + input[ 4]; + x[ 5] = x05 + input[ 5]; + x[ 6] = x06 + input[ 6]; + x[ 7] = x07 + input[ 7]; + x[ 8] = x08 + input[ 8]; + x[ 9] = x09 + input[ 9]; + x[10] = x10 + input[10]; + x[11] = x11 + input[11]; + x[12] = x12 + input[12]; + x[13] = x13 + input[13]; + x[14] = x14 + input[14]; + x[15] = x15 + input[15]; + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java new file mode 100644 index 000000000..68d7ea156 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java @@ -0,0 +1,495 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * a class that provides a basic DES engine. + */ +public class DESEngine + implements BlockCipher +{ + protected static final int BLOCK_SIZE = 8; + + private int[] workingKey = null; + + /** + * standard constructor. + */ + public DESEngine() + { + } + + /** + * initialise a DES cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + if (((KeyParameter)params).getKey().length > 8) + { + throw new IllegalArgumentException("DES key too long - should be 8 bytes"); + } + + workingKey = generateWorkingKey(encrypting, + ((KeyParameter)params).getKey()); + + return; + } + + throw new IllegalArgumentException("invalid parameter passed to DES init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "DES"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("DES engine 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"); + } + + desFunc(workingKey, in, inOff, out, outOff); + + return BLOCK_SIZE; + } + + public void reset() + { + } + + /** + * what follows is mainly taken from "Applied Cryptography", by + * Bruce Schneier, however it also bears great resemblance to Richard + * Outerbridge's D3DES... + */ + +// private static final short[] Df_Key = +// { +// 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, +// 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, +// 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 +// }; + + private static final short[] bytebit = + { + 0200, 0100, 040, 020, 010, 04, 02, 01 + }; + + private static final int[] bigbyte = + { + 0x800000, 0x400000, 0x200000, 0x100000, + 0x80000, 0x40000, 0x20000, 0x10000, + 0x8000, 0x4000, 0x2000, 0x1000, + 0x800, 0x400, 0x200, 0x100, + 0x80, 0x40, 0x20, 0x10, + 0x8, 0x4, 0x2, 0x1 + }; + + /* + * Use the key schedule specified in the Standard (ANSI X3.92-1981). + */ + + private static final byte[] pc1 = + { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 + }; + + private static final byte[] totrot = + { + 1, 2, 4, 6, 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 + }; + + private static final byte[] pc2 = + { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 + }; + + private static final int[] SP1 = { + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 + }; + + private static final int[] SP2 = { + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 + }; + + private static final int[] SP3 = { + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 + }; + + private static final int[] SP4 = { + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 + }; + + private static final int[] SP5 = { + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 + }; + + private static final int[] SP6 = { + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 + }; + + private static final int[] SP7 = { + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 + }; + + private static final int[] SP8 = { + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 + }; + + /** + * generate an integer based working key based on our secret key + * and what we processing we are planning to do. + * + * Acknowledgements for this routine go to James Gillogly & Phil Karn. + * (whoever, and wherever they are!). + */ + protected int[] generateWorkingKey( + boolean encrypting, + byte[] key) + { + int[] newKey = new int[32]; + boolean[] pc1m = new boolean[56], + pcr = new boolean[56]; + + for (int j = 0; j < 56; j++) + { + int l = pc1[j]; + + pc1m[j] = ((key[l >>> 3] & bytebit[l & 07]) != 0); + } + + for (int i = 0; i < 16; i++) + { + int l, m, n; + + if (encrypting) + { + m = i << 1; + } + else + { + m = (15 - i) << 1; + } + + n = m + 1; + newKey[m] = newKey[n] = 0; + + for (int j = 0; j < 28; j++) + { + l = j + totrot[i]; + if (l < 28) + { + pcr[j] = pc1m[l]; + } + else + { + pcr[j] = pc1m[l - 28]; + } + } + + for (int j = 28; j < 56; j++) + { + l = j + totrot[i]; + if (l < 56) + { + pcr[j] = pc1m[l]; + } + else + { + pcr[j] = pc1m[l - 28]; + } + } + + for (int j = 0; j < 24; j++) + { + if (pcr[pc2[j]]) + { + newKey[m] |= bigbyte[j]; + } + + if (pcr[pc2[j + 24]]) + { + newKey[n] |= bigbyte[j]; + } + } + } + + // + // store the processed key + // + for (int i = 0; i != 32; i += 2) + { + int i1, i2; + + i1 = newKey[i]; + i2 = newKey[i + 1]; + + newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) + | ((i2 & 0x00fc0000) >>> 10) | ((i2 & 0x00000fc0) >>> 6); + + newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) + | ((i2 & 0x0003f000) >>> 4) | (i2 & 0x0000003f); + } + + return newKey; + } + + /** + * the DES engine. + */ + protected void desFunc( + int[] wKey, + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int work, right, left; + + left = (in[inOff + 0] & 0xff) << 24; + left |= (in[inOff + 1] & 0xff) << 16; + left |= (in[inOff + 2] & 0xff) << 8; + left |= (in[inOff + 3] & 0xff); + + right = (in[inOff + 4] & 0xff) << 24; + right |= (in[inOff + 5] & 0xff) << 16; + right |= (in[inOff + 6] & 0xff) << 8; + right |= (in[inOff + 7] & 0xff); + + work = ((left >>> 4) ^ right) & 0x0f0f0f0f; + right ^= work; + left ^= (work << 4); + work = ((left >>> 16) ^ right) & 0x0000ffff; + right ^= work; + left ^= (work << 16); + work = ((right >>> 2) ^ left) & 0x33333333; + left ^= work; + right ^= (work << 2); + work = ((right >>> 8) ^ left) & 0x00ff00ff; + left ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff; + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff; + + for (int round = 0; round < 8; round++) + { + int fval; + + work = (right << 28) | (right >>> 4); + work ^= wKey[round * 4 + 0]; + fval = SP7[ work & 0x3f]; + fval |= SP5[(work >>> 8) & 0x3f]; + fval |= SP3[(work >>> 16) & 0x3f]; + fval |= SP1[(work >>> 24) & 0x3f]; + work = right ^ wKey[round * 4 + 1]; + fval |= SP8[ work & 0x3f]; + fval |= SP6[(work >>> 8) & 0x3f]; + fval |= SP4[(work >>> 16) & 0x3f]; + fval |= SP2[(work >>> 24) & 0x3f]; + left ^= fval; + work = (left << 28) | (left >>> 4); + work ^= wKey[round * 4 + 2]; + fval = SP7[ work & 0x3f]; + fval |= SP5[(work >>> 8) & 0x3f]; + fval |= SP3[(work >>> 16) & 0x3f]; + fval |= SP1[(work >>> 24) & 0x3f]; + work = left ^ wKey[round * 4 + 3]; + fval |= SP8[ work & 0x3f]; + fval |= SP6[(work >>> 8) & 0x3f]; + fval |= SP4[(work >>> 16) & 0x3f]; + fval |= SP2[(work >>> 24) & 0x3f]; + right ^= fval; + } + + right = (right << 31) | (right >>> 1); + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = (left << 31) | (left >>> 1); + work = ((left >>> 8) ^ right) & 0x00ff00ff; + right ^= work; + left ^= (work << 8); + work = ((left >>> 2) ^ right) & 0x33333333; + right ^= work; + left ^= (work << 2); + work = ((right >>> 16) ^ left) & 0x0000ffff; + left ^= work; + right ^= (work << 16); + work = ((right >>> 4) ^ left) & 0x0f0f0f0f; + left ^= work; + right ^= (work << 4); + + out[outOff + 0] = (byte)((right >>> 24) & 0xff); + out[outOff + 1] = (byte)((right >>> 16) & 0xff); + out[outOff + 2] = (byte)((right >>> 8) & 0xff); + out[outOff + 3] = (byte)(right & 0xff); + out[outOff + 4] = (byte)((left >>> 24) & 0xff); + out[outOff + 5] = (byte)((left >>> 16) & 0xff); + out[outOff + 6] = (byte)((left >>> 8) & 0xff); + out[outOff + 7] = (byte)(left & 0xff); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java new file mode 100644 index 000000000..e0c0fb85d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java @@ -0,0 +1,127 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * a class that provides a basic DESede (or Triple DES) engine. + */ +public class DESedeEngine + extends DESEngine +{ + protected static final int BLOCK_SIZE = 8; + + private int[] workingKey1 = null; + private int[] workingKey2 = null; + private int[] workingKey3 = null; + + private boolean forEncryption; + + /** + * standard constructor. + */ + public DESedeEngine() + { + } + + /** + * initialise a DESede cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to DESede init - " + params.getClass().getName()); + } + + byte[] keyMaster = ((KeyParameter)params).getKey(); + + if (keyMaster.length != 24 && keyMaster.length != 16) + { + throw new IllegalArgumentException("key size must be 16 or 24 bytes."); + } + + this.forEncryption = encrypting; + + byte[] key1 = new byte[8]; + System.arraycopy(keyMaster, 0, key1, 0, key1.length); + workingKey1 = generateWorkingKey(encrypting, key1); + + byte[] key2 = new byte[8]; + System.arraycopy(keyMaster, 8, key2, 0, key2.length); + workingKey2 = generateWorkingKey(!encrypting, key2); + + if (keyMaster.length == 24) + { + byte[] key3 = new byte[8]; + System.arraycopy(keyMaster, 16, key3, 0, key3.length); + workingKey3 = generateWorkingKey(encrypting, key3); + } + else // 16 byte key + { + workingKey3 = workingKey1; + } + } + + public String getAlgorithmName() + { + return "DESede"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (workingKey1 == null) + { + throw new IllegalStateException("DESede engine 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"); + } + + byte[] temp = new byte[BLOCK_SIZE]; + + if (forEncryption) + { + desFunc(workingKey1, in, inOff, temp, 0); + desFunc(workingKey2, temp, 0, temp, 0); + desFunc(workingKey3, temp, 0, out, outOff); + } + else + { + desFunc(workingKey3, in, inOff, temp, 0); + desFunc(workingKey2, temp, 0, temp, 0); + desFunc(workingKey1, temp, 0, out, outOff); + } + + return BLOCK_SIZE; + } + + public void reset() + { + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java new file mode 100644 index 000000000..e03c8cb36 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java @@ -0,0 +1,348 @@ +package org.spongycastle.crypto.engines; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.util.Arrays; + +/** + * Wrap keys according to + * + * draft-ietf-smime-key-wrap-01.txt. + *
+ * Note: + *
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf + *
+ * It is a third phase candidate in the eStream contest, and is patent-free. + * No attacks are known as of today (April 2007). See + * + * http://www.ecrypt.eu.org/stream/hcp3.html + *
+ */ +public class HC128Engine + implements StreamCipher +{ + private int[] p = new int[512]; + private int[] q = new int[512]; + private int cnt = 0; + + private static int f1(int x) + { + return rotateRight(x, 7) ^ rotateRight(x, 18) + ^ (x >>> 3); + } + + private static int f2(int x) + { + return rotateRight(x, 17) ^ rotateRight(x, 19) + ^ (x >>> 10); + } + + private int g1(int x, int y, int z) + { + return (rotateRight(x, 10) ^ rotateRight(z, 23)) + + rotateRight(y, 8); + } + + private int g2(int x, int y, int z) + { + return (rotateLeft(x, 10) ^ rotateLeft(z, 23)) + rotateLeft(y, 8); + } + + private static int rotateLeft( + int x, + int bits) + { + return (x << bits) | (x >>> -bits); + } + + private static int rotateRight( + int x, + int bits) + { + return (x >>> bits) | (x << -bits); + } + + private int h1(int x) + { + return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256]; + } + + private int h2(int x) + { + return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256]; + } + + private static int mod1024(int x) + { + return x & 0x3FF; + } + + private static int mod512(int x) + { + return x & 0x1FF; + } + + private static int dim(int x, int y) + { + return mod512(x - y); + } + + private int step() + { + int j = mod512(cnt); + int ret; + if (cnt < 512) + { + p[j] += g1(p[dim(j, 3)], p[dim(j, 10)], p[dim(j, 511)]); + ret = h1(p[dim(j, 12)]) ^ p[j]; + } + else + { + q[j] += g2(q[dim(j, 3)], q[dim(j, 10)], q[dim(j, 511)]); + ret = h2(q[dim(j, 12)]) ^ q[j]; + } + cnt = mod1024(cnt + 1); + return ret; + } + + private byte[] key, iv; + private boolean initialised; + + private void init() + { + if (key.length != 16) + { + throw new java.lang.IllegalArgumentException( + "The key must be 128 bits long"); + } + + idx = 0; + cnt = 0; + + int[] w = new int[1280]; + + for (int i = 0; i < 16; i++) + { + w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3)); + } + System.arraycopy(w, 0, w, 4, 4); + + for (int i = 0; i < iv.length && i < 16; i++) + { + w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3)); + } + System.arraycopy(w, 8, w, 12, 4); + + for (int i = 16; i < 1280; i++) + { + w[i] = f2(w[i - 2]) + w[i - 7] + f1(w[i - 15]) + w[i - 16] + i; + } + + System.arraycopy(w, 256, p, 0, 512); + System.arraycopy(w, 768, q, 0, 512); + + for (int i = 0; i < 512; i++) + { + p[i] = step(); + } + for (int i = 0; i < 512; i++) + { + q[i] = step(); + } + + cnt = 0; + } + + public String getAlgorithmName() + { + return "HC-128"; + } + + /** + * Initialise a HC-128 cipher. + * + * @param forEncryption whether or not we are for encryption. Irrelevant, as + * encryption and decryption are the same. + * @param params the parameters required to set up the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate (ie. the key is not 128 bit long). + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + CipherParameters keyParam = params; + + if (params instanceof ParametersWithIV) + { + iv = ((ParametersWithIV)params).getIV(); + keyParam = ((ParametersWithIV)params).getParameters(); + } + else + { + iv = new byte[0]; + } + + if (keyParam instanceof KeyParameter) + { + key = ((KeyParameter)keyParam).getKey(); + init(); + } + else + { + throw new IllegalArgumentException( + "Invalid parameter passed to HC128 init - " + + params.getClass().getName()); + } + + initialised = true; + } + + private byte[] buf = new byte[4]; + private int idx = 0; + + private byte getByte() + { + if (idx == 0) + { + int step = step(); + buf[0] = (byte)(step & 0xFF); + step >>= 8; + buf[1] = (byte)(step & 0xFF); + step >>= 8; + buf[2] = (byte)(step & 0xFF); + step >>= 8; + buf[3] = (byte)(step & 0xFF); + } + byte ret = buf[idx]; + idx = idx + 1 & 0x3; + return ret; + } + + public void processBytes(byte[] in, int inOff, int len, byte[] out, + int outOff) throws DataLengthException + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName() + + " not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + for (int i = 0; i < len; i++) + { + out[outOff + i] = (byte)(in[inOff + i] ^ getByte()); + } + } + + public void reset() + { + init(); + } + + public byte returnByte(byte in) + { + return (byte)(in ^ getByte()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java new file mode 100644 index 000000000..2c2a45a0d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java @@ -0,0 +1,244 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; + +/** + * HC-256 is a software-efficient stream cipher created by Hongjun Wu. It + * generates keystream from a 256-bit secret key and a 256-bit initialization + * vector. + *+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf + *
+ * Its brother, HC-128, is a third phase candidate in the eStream contest. + * The algorithm is patent-free. No attacks are known as of today (April 2007). + * See + * + * http://www.ecrypt.eu.org/stream/hcp3.html + *
+ */ +public class HC256Engine + implements StreamCipher +{ + private int[] p = new int[1024]; + private int[] q = new int[1024]; + private int cnt = 0; + + private int step() + { + int j = cnt & 0x3FF; + int ret; + if (cnt < 1024) + { + int x = p[(j - 3 & 0x3FF)]; + int y = p[(j - 1023 & 0x3FF)]; + p[j] += p[(j - 10 & 0x3FF)] + + (rotateRight(x, 10) ^ rotateRight(y, 23)) + + q[((x ^ y) & 0x3FF)]; + + x = p[(j - 12 & 0x3FF)]; + ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256] + + q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768]) + ^ p[j]; + } + else + { + int x = q[(j - 3 & 0x3FF)]; + int y = q[(j - 1023 & 0x3FF)]; + q[j] += q[(j - 10 & 0x3FF)] + + (rotateRight(x, 10) ^ rotateRight(y, 23)) + + p[((x ^ y) & 0x3FF)]; + + x = q[(j - 12 & 0x3FF)]; + ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256] + + p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768]) + ^ q[j]; + } + cnt = cnt + 1 & 0x7FF; + return ret; + } + + private byte[] key, iv; + private boolean initialised; + + private void init() + { + if (key.length != 32 && key.length != 16) + { + throw new IllegalArgumentException( + "The key must be 128/256 bits long"); + } + + if (iv.length < 16) + { + throw new IllegalArgumentException( + "The IV must be at least 128 bits long"); + } + + if (key.length != 32) + { + byte[] k = new byte[32]; + + System.arraycopy(key, 0, k, 0, key.length); + System.arraycopy(key, 0, k, 16, key.length); + + key = k; + } + + if (iv.length < 32) + { + byte[] newIV = new byte[32]; + + System.arraycopy(iv, 0, newIV, 0, iv.length); + System.arraycopy(iv, 0, newIV, iv.length, newIV.length - iv.length); + + iv = newIV; + } + + idx = 0; + cnt = 0; + + int[] w = new int[2560]; + + for (int i = 0; i < 32; i++) + { + w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3)); + } + + for (int i = 0; i < 32; i++) + { + w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3)); + } + + for (int i = 16; i < 2560; i++) + { + int x = w[i - 2]; + int y = w[i - 15]; + w[i] = (rotateRight(x, 17) ^ rotateRight(x, 19) ^ (x >>> 10)) + + w[i - 7] + + (rotateRight(y, 7) ^ rotateRight(y, 18) ^ (y >>> 3)) + + w[i - 16] + i; + } + + System.arraycopy(w, 512, p, 0, 1024); + System.arraycopy(w, 1536, q, 0, 1024); + + for (int i = 0; i < 4096; i++) + { + step(); + } + + cnt = 0; + } + + public String getAlgorithmName() + { + return "HC-256"; + } + + /** + * Initialise a HC-256 cipher. + * + * @param forEncryption whether or not we are for encryption. Irrelevant, as + * encryption and decryption are the same. + * @param params the parameters required to set up the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate (ie. the key is not 256 bit long). + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + CipherParameters keyParam = params; + + if (params instanceof ParametersWithIV) + { + iv = ((ParametersWithIV)params).getIV(); + keyParam = ((ParametersWithIV)params).getParameters(); + } + else + { + iv = new byte[0]; + } + + if (keyParam instanceof KeyParameter) + { + key = ((KeyParameter)keyParam).getKey(); + init(); + } + else + { + throw new IllegalArgumentException( + "Invalid parameter passed to HC256 init - " + + params.getClass().getName()); + } + + initialised = true; + } + + private byte[] buf = new byte[4]; + private int idx = 0; + + private byte getByte() + { + if (idx == 0) + { + int step = step(); + buf[0] = (byte)(step & 0xFF); + step >>= 8; + buf[1] = (byte)(step & 0xFF); + step >>= 8; + buf[2] = (byte)(step & 0xFF); + step >>= 8; + buf[3] = (byte)(step & 0xFF); + } + byte ret = buf[idx]; + idx = idx + 1 & 0x3; + return ret; + } + + public void processBytes(byte[] in, int inOff, int len, byte[] out, + int outOff) throws DataLengthException + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName() + + " not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + for (int i = 0; i < len; i++) + { + out[outOff + i] = (byte)(in[inOff + i] ^ getByte()); + } + } + + public void reset() + { + init(); + } + + public byte returnByte(byte in) + { + return (byte)(in ^ getByte()); + } + + private static int rotateRight( + int x, + int bits) + { + return (x >>> bits) | (x << -bits); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java new file mode 100644 index 000000000..11f9ef19c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java @@ -0,0 +1,367 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * A class that provides a basic International Data Encryption Algorithm (IDEA) engine. + *+ * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM" + * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the + * end of the mulinv function!). + *
+ * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/ + *
+ * Note 1: This algorithm is patented in the USA, Japan, and Europe including + * at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland + * and the United Kingdom. Non-commercial use is free, however any commercial + * products are liable for royalties. Please see + * www.mediacrypt.com for + * further details. This announcement has been included at the request of + * the patent holders. + *
+ * Note 2: Due to the requests concerning the above, this algorithm is now only + * included in the extended Bouncy Castle provider and JCE signed jars. It is + * not included in the default distributions. + */ +public class IDEAEngine + implements BlockCipher +{ + protected static final int BLOCK_SIZE = 8; + + private int[] workingKey = null; + + /** + * standard constructor. + */ + public IDEAEngine() + { + } + + /** + * initialise an IDEA cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + workingKey = generateWorkingKey(forEncryption, + ((KeyParameter)params).getKey()); + return; + } + + throw new IllegalArgumentException("invalid parameter passed to IDEA init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "IDEA"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("IDEA engine 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"); + } + + ideaFunc(workingKey, in, inOff, out, outOff); + + return BLOCK_SIZE; + } + + public void reset() + { + } + + private static final int MASK = 0xffff; + private static final int BASE = 0x10001; + + private int bytesToWord( + byte[] in, + int inOff) + { + return ((in[inOff] << 8) & 0xff00) + (in[inOff + 1] & 0xff); + } + + private void wordToBytes( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)(word >>> 8); + out[outOff + 1] = (byte)word; + } + + /** + * return x = x * y where the multiplication is done modulo + * 65537 (0x10001) (as defined in the IDEA specification) and + * a zero input is taken to be 65536 (0x10000). + * + * @param x the x value + * @param y the y value + * @return x = x * y + */ + private int mul( + int x, + int y) + { + if (x == 0) + { + x = (BASE - y); + } + else if (y == 0) + { + x = (BASE - x); + } + else + { + int p = x * y; + + y = p & MASK; + x = p >>> 16; + x = y - x + ((y < x) ? 1 : 0); + } + + return x & MASK; + } + + private void ideaFunc( + int[] workingKey, + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int x0, x1, x2, x3, t0, t1; + int keyOff = 0; + + x0 = bytesToWord(in, inOff); + x1 = bytesToWord(in, inOff + 2); + x2 = bytesToWord(in, inOff + 4); + x3 = bytesToWord(in, inOff + 6); + + for (int round = 0; round < 8; round++) + { + x0 = mul(x0, workingKey[keyOff++]); + x1 += workingKey[keyOff++]; + x1 &= MASK; + x2 += workingKey[keyOff++]; + x2 &= MASK; + x3 = mul(x3, workingKey[keyOff++]); + + t0 = x1; + t1 = x2; + x2 ^= x0; + x1 ^= x3; + + x2 = mul(x2, workingKey[keyOff++]); + x1 += x2; + x1 &= MASK; + + x1 = mul(x1, workingKey[keyOff++]); + x2 += x1; + x2 &= MASK; + + x0 ^= x1; + x3 ^= x2; + x1 ^= t1; + x2 ^= t0; + } + + wordToBytes(mul(x0, workingKey[keyOff++]), out, outOff); + wordToBytes(x2 + workingKey[keyOff++], out, outOff + 2); /* NB: Order */ + wordToBytes(x1 + workingKey[keyOff++], out, outOff + 4); + wordToBytes(mul(x3, workingKey[keyOff]), out, outOff + 6); + } + + /** + * The following function is used to expand the user key to the encryption + * subkey. The first 16 bytes are the user key, and the rest of the subkey + * is calculated by rotating the previous 16 bytes by 25 bits to the left, + * and so on until the subkey is completed. + */ + private int[] expandKey( + byte[] uKey) + { + int[] key = new int[52]; + + if (uKey.length < 16) + { + byte[] tmp = new byte[16]; + + System.arraycopy(uKey, 0, tmp, tmp.length - uKey.length, uKey.length); + + uKey = tmp; + } + + for (int i = 0; i < 8; i++) + { + key[i] = bytesToWord(uKey, i * 2); + } + + for (int i = 8; i < 52; i++) + { + if ((i & 7) < 6) + { + key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK; + } + else if ((i & 7) == 6) + { + key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK; + } + else + { + key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK; + } + } + + return key; + } + + /** + * This function computes multiplicative inverse using Euclid's Greatest + * Common Divisor algorithm. Zero and one are self inverse. + *
+ * i.e. x * mulInv(x) == 1 (modulo BASE) + */ + private int mulInv( + int x) + { + int t0, t1, q, y; + + if (x < 2) + { + return x; + } + + t0 = 1; + t1 = BASE / x; + y = BASE % x; + + while (y != 1) + { + q = x / y; + x = x % y; + t0 = (t0 + (t1 * q)) & MASK; + if (x == 1) + { + return t0; + } + q = y / x; + y = y % x; + t1 = (t1 + (t0 * q)) & MASK; + } + + return (1 - t1) & MASK; + } + + /** + * Return the additive inverse of x. + *
+ * i.e. x + addInv(x) == 0 + */ + int addInv( + int x) + { + return (0 - x) & MASK; + } + + /** + * The function to invert the encryption subkey to the decryption subkey. + * It also involves the multiplicative inverse and the additive inverse functions. + */ + private int[] invertKey( + int[] inKey) + { + int t1, t2, t3, t4; + int p = 52; /* We work backwards */ + int[] key = new int[52]; + int inOff = 0; + + t1 = mulInv(inKey[inOff++]); + t2 = addInv(inKey[inOff++]); + t3 = addInv(inKey[inOff++]); + t4 = mulInv(inKey[inOff++]); + key[--p] = t4; + key[--p] = t3; + key[--p] = t2; + key[--p] = t1; + + for (int round = 1; round < 8; round++) + { + t1 = inKey[inOff++]; + t2 = inKey[inOff++]; + key[--p] = t2; + key[--p] = t1; + + t1 = mulInv(inKey[inOff++]); + t2 = addInv(inKey[inOff++]); + t3 = addInv(inKey[inOff++]); + t4 = mulInv(inKey[inOff++]); + key[--p] = t4; + key[--p] = t2; /* NB: Order */ + key[--p] = t3; + key[--p] = t1; + } + + t1 = inKey[inOff++]; + t2 = inKey[inOff++]; + key[--p] = t2; + key[--p] = t1; + + t1 = mulInv(inKey[inOff++]); + t2 = addInv(inKey[inOff++]); + t3 = addInv(inKey[inOff++]); + t4 = mulInv(inKey[inOff]); + key[--p] = t4; + key[--p] = t3; + key[--p] = t2; + key[--p] = t1; + + return key; + } + + private int[] generateWorkingKey( + boolean forEncryption, + byte[] userKey) + { + if (forEncryption) + { + return expandKey(userKey); + } + else + { + return invertKey(expandKey(userKey)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/IESEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/IESEngine.java new file mode 100755 index 000000000..892f25c74 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/IESEngine.java @@ -0,0 +1,398 @@ +package org.spongycastle.crypto.engines; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DerivationFunction; +import org.spongycastle.crypto.EphemeralKeyPair; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.KeyParser; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.IESParameters; +import org.spongycastle.crypto.params.IESWithCipherParameters; +import org.spongycastle.crypto.params.KDFParameters; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.BigIntegers; + +/** + * 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 IESEngine +{ + BasicAgreement agree; + DerivationFunction kdf; + Mac mac; + BufferedBlockCipher cipher; + byte[] macBuf; + + boolean forEncryption; + CipherParameters privParam, pubParam; + IESParameters param; + + byte[] V; + private EphemeralKeyPairGenerator keyPairGenerator; + private KeyParser keyParser; + + + /** + * 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 IESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac) + { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.macBuf = new byte[mac.getMacSize()]; + this.cipher = null; + } + + + /** + * 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 IESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac, + BufferedBlockCipher cipher) + { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.macBuf = new byte[mac.getMacSize()]; + this.cipher = cipher; + } + + + /** + * Initialise the encryptor. + * + * @param forEncryption whether or not this is encryption/decryption. + * @param privParam our private key parameters + * @param pubParam the recipient's/sender's public key parameters + * @param param encoding and derivation parameters. + */ + public void init( + boolean forEncryption, + CipherParameters privParam, + CipherParameters pubParam, + CipherParameters param) + { + this.forEncryption = forEncryption; + this.privParam = privParam; + this.pubParam = pubParam; + this.param = (IESParameters)param; + this.V = new byte[0]; + } + + + /** + * Initialise the encryptor. + * + * @param publicKey the recipient's/sender's public key parameters + * @param params encoding and derivation parameters. + * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use. + */ + public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator) + { + this.forEncryption = true; + this.pubParam = publicKey; + this.param = (IESParameters)params; + this.keyPairGenerator = ephemeralKeyPairGenerator; + } + + /** + * Initialise the encryptor. + * + * @param privateKey the recipient's private key. + * @param params encoding and derivation parameters. + * @param publicKeyParser the parser for reading the ephemeral public key. + */ + public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser) + { + this.forEncryption = false; + this.privParam = privateKey; + this.param = (IESParameters)params; + this.keyParser = publicKeyParser; + } + + public BufferedBlockCipher getCipher() + { + return cipher; + } + + public Mac getMac() + { + return mac; + } + + private byte[] encryptBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] C = null, K = null, K1 = null, K2 = null; + int len; + + if (cipher == null) + { + // Streaming mode. + K1 = new byte[inLen]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + + if (V.length != 0) + { + System.arraycopy(K, 0, K2, 0, K2.length); + System.arraycopy(K, K2.length, K1, 0, K1.length); + } + else + { + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, inLen, K2, 0, K2.length); + } + + C = new byte[inLen]; + + for (int i = 0; i != inLen; i++) + { + C[i] = (byte)(in[inOff + i] ^ K1[i]); + } + len = inLen; + } + else + { + // Block cipher mode. + K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, K1.length, K2, 0, K2.length); + + cipher.init(true, new KeyParameter(K1)); + C = new byte[cipher.getOutputSize(inLen)]; + len = cipher.processBytes(in, inOff, inLen, C, 0); + len += cipher.doFinal(C, len); + } + + + // Convert the length of the encoding vector into a byte array. + byte[] P2 = param.getEncodingV(); + byte[] L2 = new byte[4]; + if (V.length != 0 && P2 != null) + { + Pack.intToBigEndian(P2.length * 8, L2, 0); + } + + + // Apply the MAC. + byte[] T = new byte[mac.getMacSize()]; + + mac.init(new KeyParameter(K2)); + mac.update(C, 0, C.length); + if (P2 != null) + { + mac.update(P2, 0, P2.length); + } + if (V.length != 0) + { + mac.update(L2, 0, L2.length); + } + mac.doFinal(T, 0); + + + // Output the triple (V,C,T). + byte[] Output = new byte[V.length + len + T.length]; + System.arraycopy(V, 0, Output, 0, V.length); + System.arraycopy(C, 0, Output, V.length, len); + System.arraycopy(T, 0, Output, V.length + len, T.length); + return Output; + } + + private byte[] decryptBlock( + byte[] in_enc, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] M = null, K = null, K1 = null, K2 = null; + int len; + + if (cipher == null) + { + // Streaming mode. + K1 = new byte[inLen - V.length - mac.getMacSize()]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + + if (V.length != 0) + { + System.arraycopy(K, 0, K2, 0, K2.length); + System.arraycopy(K, K2.length, K1, 0, K1.length); + } + else + { + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, K1.length, K2, 0, K2.length); + } + + M = new byte[K1.length]; + + for (int i = 0; i != K1.length; i++) + { + M[i] = (byte)(in_enc[inOff + V.length + i] ^ K1[i]); + } + + len = K1.length; + } + else + { + // Block cipher mode. + K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, K1.length, K2, 0, K2.length); + + cipher.init(false, new KeyParameter(K1)); + + M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())]; + len = cipher.processBytes(in_enc, inOff + V.length, inLen - V.length - mac.getMacSize(), M, 0); + len += cipher.doFinal(M, len); + } + + + // Convert the length of the encoding vector into a byte array. + byte[] P2 = param.getEncodingV(); + byte[] L2 = new byte[4]; + if (V.length != 0 && P2 != null) + { + Pack.intToBigEndian(P2.length * 8, L2, 0); + } + + + // Verify the MAC. + int end = inOff + inLen; + byte[] T1 = Arrays.copyOfRange(in_enc, end - mac.getMacSize(), end); + + byte[] T2 = new byte[T1.length]; + mac.init(new KeyParameter(K2)); + mac.update(in_enc, inOff + V.length, inLen - V.length - T2.length); + + if (P2 != null) + { + mac.update(P2, 0, P2.length); + } + if (V.length != 0) + { + mac.update(L2, 0, L2.length); + } + mac.doFinal(T2, 0); + + if (!Arrays.constantTimeAreEqual(T1, T2)) + { + throw new InvalidCipherTextException("Invalid MAC."); + } + + + // Output the message. + return Arrays.copyOfRange(M, 0, len); + } + + + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forEncryption) + { + if (keyPairGenerator != null) + { + EphemeralKeyPair ephKeyPair = keyPairGenerator.generate(); + + this.privParam = ephKeyPair.getKeyPair().getPrivate(); + this.V = ephKeyPair.getEncodedPublicKey(); + } + } + else + { + if (keyParser != null) + { + ByteArrayInputStream bIn = new ByteArrayInputStream(in, inOff, inLen); + + try + { + this.pubParam = keyParser.readKey(bIn); + } + catch (IOException e) + { + throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e); + } + + int encLength = (inLen - bIn.available()); + this.V = Arrays.copyOfRange(in, inOff, inOff + encLength); + } + } + + // Compute the common value and convert to byte array. + agree.init(privParam); + BigInteger z = agree.calculateAgreement(pubParam); + byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z); + + // Create input to KDF. + byte[] VZ; + if (V.length != 0) + { + VZ = new byte[V.length + Z.length]; + System.arraycopy(V, 0, VZ, 0, V.length); + System.arraycopy(Z, 0, VZ, V.length, Z.length); + } + else + { + VZ = Z; + } + + // Initialise the KDF. + KDFParameters kdfParam = new KDFParameters(VZ, param.getDerivationV()); + kdf.init(kdfParam); + + return forEncryption + ? encryptBlock(in, inOff, inLen) + : decryptBlock(in, inOff, inLen); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java new file mode 100644 index 000000000..5c15db1d1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java @@ -0,0 +1,219 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.util.Pack; + +/** + * Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count). + * see: http://www.burtleburtle.net/bob/rand/isaacafa.html +*/ +public class ISAACEngine + implements StreamCipher +{ + // Constants + private final int sizeL = 8, + stateArraySize = sizeL<<5; // 256 + + // Cipher's internal state + private int[] engineState = null, // mm + results = null; // randrsl + private int a = 0, b = 0, c = 0; + + // Engine state + private int index = 0; + private byte[] keyStream = new byte[stateArraySize<<2], // results expanded into bytes + workingKey = null; + private boolean initialised = false; + + /** + * initialise an ISAAC cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to ISAAC init - " + params.getClass().getName()); + } + /* + * ISAAC encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. + */ + KeyParameter p = (KeyParameter)params; + setKey(p.getKey()); + + return; + } + + public byte returnByte(byte in) + { + if (index == 0) + { + isaac(); + keyStream = Pack.intToBigEndian(results); + } + byte out = (byte)(keyStream[index]^in); + index = (index + 1) & 1023; + + return out; + } + + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName()+" not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + isaac(); + keyStream = Pack.intToBigEndian(results); + } + out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]); + index = (index + 1) & 1023; + } + } + + public String getAlgorithmName() + { + return "ISAAC"; + } + + public void reset() + { + setKey(workingKey); + } + + // Private implementation + private void setKey(byte[] keyBytes) + { + workingKey = keyBytes; + + if (engineState == null) + { + engineState = new int[stateArraySize]; + } + + if (results == null) + { + results = new int[stateArraySize]; + } + + int i, j, k; + + // Reset state + for (i = 0; i < stateArraySize; i++) + { + engineState[i] = results[i] = 0; + } + a = b = c = 0; + + // Reset index counter for output + index = 0; + + // Convert the key bytes to ints and put them into results[] for initialization + byte[] t = new byte[keyBytes.length + (keyBytes.length & 3)]; + System.arraycopy(keyBytes, 0, t, 0, keyBytes.length); + for (i = 0; i < t.length; i+=4) + { + results[i >>> 2] = Pack.littleEndianToInt(t, i); + } + + // It has begun? + int[] abcdefgh = new int[sizeL]; + + for (i = 0; i < sizeL; i++) + { + abcdefgh[i] = 0x9e3779b9; // Phi (golden ratio) + } + + for (i = 0; i < 4; i++) + { + mix(abcdefgh); + } + + for (i = 0; i < 2; i++) + { + for (j = 0; j < stateArraySize; j+=sizeL) + { + for (k = 0; k < sizeL; k++) + { + abcdefgh[k] += (i<1) ? results[j+k] : engineState[j+k]; + } + + mix(abcdefgh); + + for (k = 0; k < sizeL; k++) + { + engineState[j+k] = abcdefgh[k]; + } + } + } + + isaac(); + + initialised = true; + } + + private void isaac() + { + int i, x, y; + + b += ++c; + for (i = 0; i < stateArraySize; i++) + { + x = engineState[i]; + switch (i & 3) + { + case 0: a ^= (a << 13); break; + case 1: a ^= (a >>> 6); break; + case 2: a ^= (a << 2); break; + case 3: a ^= (a >>> 16); break; + } + a += engineState[(i+128) & 0xFF]; + engineState[i] = y = engineState[(x >>> 2) & 0xFF] + a + b; + results[i] = b = engineState[(y >>> 10) & 0xFF] + x; + } + } + + private void mix(int[] x) + { + x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2]; + x[1]^=x[2]>>> 2; x[4]+=x[1]; x[2]+=x[3]; + x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4]; + x[3]^=x[4]>>>16; x[6]+=x[3]; x[4]+=x[5]; + x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6]; + x[5]^=x[6]>>> 4; x[0]+=x[5]; x[6]+=x[7]; + x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0]; + x[7]^=x[0]>>> 9; x[2]+=x[7]; x[0]+=x[1]; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java new file mode 100644 index 000000000..a25b12f14 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java @@ -0,0 +1,437 @@ +package org.spongycastle.crypto.engines; + +import java.math.BigInteger; +import java.util.Vector; +import org.spongycastle.util.Arrays; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.params.NaccacheSternKeyParameters; +import org.spongycastle.crypto.params.NaccacheSternPrivateKeyParameters; +import org.spongycastle.crypto.params.ParametersWithRandom; + +/** + * NaccacheStern Engine. For details on this cipher, please see + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ +public class NaccacheSternEngine + implements AsymmetricBlockCipher +{ + private boolean forEncryption; + + private NaccacheSternKeyParameters key; + + private Vector[] lookup = null; + + private boolean debug = false; + + private static BigInteger ZERO = BigInteger.valueOf(0); + private static BigInteger ONE = BigInteger.valueOf(1); + + /** + * Initializes this algorithm. Must be called before all other Functions. + * + * @see org.spongycastle.crypto.AsymmetricBlockCipher#init(boolean, + * org.spongycastle.crypto.CipherParameters) + */ + public void init(boolean forEncryption, CipherParameters param) + { + this.forEncryption = forEncryption; + + if (param instanceof ParametersWithRandom) + { + param = ((ParametersWithRandom) param).getParameters(); + } + + key = (NaccacheSternKeyParameters)param; + + // construct lookup table for faster decryption if necessary + if (!this.forEncryption) + { + if (debug) + { + System.out.println("Constructing lookup Array"); + } + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + Vector primes = priv.getSmallPrimes(); + lookup = new Vector[primes.size()]; + for (int i = 0; i < primes.size(); i++) + { + BigInteger actualPrime = (BigInteger)primes.elementAt(i); + int actualPrimeValue = actualPrime.intValue(); + + lookup[i] = new Vector(); + lookup[i].addElement(ONE); + + if (debug) + { + System.out.println("Constructing lookup ArrayList for " + actualPrimeValue); + } + + BigInteger accJ = ZERO; + + for (int j = 1; j < actualPrimeValue; j++) + { + accJ = accJ.add(priv.getPhi_n()); + BigInteger comp = accJ.divide(actualPrime); + lookup[i].addElement(priv.getG().modPow(comp, priv.getModulus())); + } + } + } + } + + public void setDebug(boolean debug) + { + this.debug = debug; + } + + /** + * Returns the input block size of this algorithm. + * + * @see org.spongycastle.crypto.AsymmetricBlockCipher#getInputBlockSize() + */ + public int getInputBlockSize() + { + if (forEncryption) + { + // We can only encrypt values up to lowerSigmaBound + return (key.getLowerSigmaBound() + 7) / 8 - 1; + } + else + { + // We pad to modulus-size bytes for easier decryption. + return key.getModulus().toByteArray().length; + } + } + + /** + * Returns the output block size of this algorithm. + * + * @see org.spongycastle.crypto.AsymmetricBlockCipher#getOutputBlockSize() + */ + public int getOutputBlockSize() + { + if (forEncryption) + { + // encrypted Data is always padded up to modulus size + return key.getModulus().toByteArray().length; + } + else + { + // decrypted Data has upper limit lowerSigmaBound + return (key.getLowerSigmaBound() + 7) / 8 - 1; + } + } + + /** + * Process a single Block using the Naccache-Stern algorithm. + * + * @see org.spongycastle.crypto.AsymmetricBlockCipher#processBlock(byte[], + * int, int) + */ + public byte[] processBlock(byte[] in, int inOff, int len) throws InvalidCipherTextException + { + if (key == null) + { + throw new IllegalStateException("NaccacheStern engine not initialised"); + } + if (len > (getInputBlockSize() + 1)) + { + throw new DataLengthException("input too large for Naccache-Stern cipher.\n"); + } + + if (!forEncryption) + { + // At decryption make sure that we receive padded data blocks + if (len < getInputBlockSize()) + { + throw new InvalidCipherTextException("BlockLength does not match modulus for Naccache-Stern cipher.\n"); + } + } + + byte[] block; + + if (inOff != 0 || len != in.length) + { + block = new byte[len]; + System.arraycopy(in, inOff, block, 0, len); + } + else + { + block = in; + } + + // transform input into BigInteger + BigInteger input = new BigInteger(1, block); + if (debug) + { + System.out.println("input as BigInteger: " + input); + } + byte[] output; + if (forEncryption) + { + output = encrypt(input); + } + else + { + Vector plain = new Vector(); + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + Vector primes = priv.getSmallPrimes(); + // Get Chinese Remainders of CipherText + for (int i = 0; i < primes.size(); i++) + { + BigInteger exp = input.modPow(priv.getPhi_n().divide((BigInteger)primes.elementAt(i)), priv.getModulus()); + Vector al = lookup[i]; + if (lookup[i].size() != ((BigInteger)primes.elementAt(i)).intValue()) + { + if (debug) + { + System.out.println("Prime is " + primes.elementAt(i) + ", lookup table has size " + al.size()); + } + throw new InvalidCipherTextException("Error in lookup Array for " + + ((BigInteger)primes.elementAt(i)).intValue() + + ": Size mismatch. Expected ArrayList with length " + + ((BigInteger)primes.elementAt(i)).intValue() + " but found ArrayList of length " + + lookup[i].size()); + } + int lookedup = al.indexOf(exp); + + if (lookedup == -1) + { + if (debug) + { + System.out.println("Actual prime is " + primes.elementAt(i)); + System.out.println("Decrypted value is " + exp); + + System.out.println("LookupList for " + primes.elementAt(i) + " with size " + lookup[i].size() + + " is: "); + for (int j = 0; j < lookup[i].size(); j++) + { + System.out.println(lookup[i].elementAt(j)); + } + } + throw new InvalidCipherTextException("Lookup failed"); + } + plain.addElement(BigInteger.valueOf(lookedup)); + } + BigInteger test = chineseRemainder(plain, primes); + + // Should not be used as an oracle, so reencrypt output to see + // if it corresponds to input + + // this breaks probabilisic encryption, so disable it. Anyway, we do + // use the first n primes for key generation, so it is pretty easy + // to guess them. But as stated in the paper, this is not a security + // breach. So we can just work with the correct sigma. + + // if (debug) { + // System.out.println("Decryption is " + test); + // } + // if ((key.getG().modPow(test, key.getModulus())).equals(input)) { + // output = test.toByteArray(); + // } else { + // if(debug){ + // System.out.println("Engine seems to be used as an oracle, + // returning null"); + // } + // output = null; + // } + + output = test.toByteArray(); + + } + + return output; + } + + /** + * Encrypts a BigInteger aka Plaintext with the public key. + * + * @param plain + * The BigInteger to encrypt + * @return The byte[] representation of the encrypted BigInteger (i.e. + * crypted.toByteArray()) + */ + public byte[] encrypt(BigInteger plain) + { + // Always return modulus size values 0-padded at the beginning + // 0-padding at the beginning is correctly parsed by BigInteger :) + byte[] output = key.getModulus().toByteArray(); + Arrays.fill(output, (byte)0); + byte[] tmp = key.getG().modPow(plain, key.getModulus()).toByteArray(); + System + .arraycopy(tmp, 0, output, output.length - tmp.length, + tmp.length); + if (debug) + { + System.out + .println("Encrypted value is: " + new BigInteger(output)); + } + return output; + } + + /** + * Adds the contents of two encrypted blocks mod sigma + * + * @param block1 + * the first encrypted block + * @param block2 + * the second encrypted block + * @return encrypt((block1 + block2) mod sigma) + * @throws InvalidCipherTextException + */ + public byte[] addCryptedBlocks(byte[] block1, byte[] block2) + throws InvalidCipherTextException + { + // check for correct blocksize + if (forEncryption) + { + if ((block1.length > getOutputBlockSize()) + || (block2.length > getOutputBlockSize())) + { + throw new InvalidCipherTextException( + "BlockLength too large for simple addition.\n"); + } + } + else + { + if ((block1.length > getInputBlockSize()) + || (block2.length > getInputBlockSize())) + { + throw new InvalidCipherTextException( + "BlockLength too large for simple addition.\n"); + } + } + + // calculate resulting block + BigInteger m1Crypt = new BigInteger(1, block1); + BigInteger m2Crypt = new BigInteger(1, block2); + BigInteger m1m2Crypt = m1Crypt.multiply(m2Crypt); + m1m2Crypt = m1m2Crypt.mod(key.getModulus()); + if (debug) + { + System.out.println("c(m1) as BigInteger:....... " + m1Crypt); + System.out.println("c(m2) as BigInteger:....... " + m2Crypt); + System.out.println("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt); + } + + byte[] output = key.getModulus().toByteArray(); + Arrays.fill(output, (byte)0); + System.arraycopy(m1m2Crypt.toByteArray(), 0, output, output.length + - m1m2Crypt.toByteArray().length, + m1m2Crypt.toByteArray().length); + + return output; + } + + /** + * Convenience Method for data exchange with the cipher. + * + * Determines blocksize and splits data to blocksize. + * + * @param data the data to be processed + * @return the data after it went through the NaccacheSternEngine. + * @throws InvalidCipherTextException + */ + public byte[] processData(byte[] data) throws InvalidCipherTextException + { + if (debug) + { + System.out.println(); + } + if (data.length > getInputBlockSize()) + { + int inBlocksize = getInputBlockSize(); + int outBlocksize = getOutputBlockSize(); + if (debug) + { + System.out.println("Input blocksize is: " + inBlocksize + " bytes"); + System.out.println("Output blocksize is: " + outBlocksize + " bytes"); + System.out.println("Data has length:.... " + data.length + " bytes"); + } + int datapos = 0; + int retpos = 0; + byte[] retval = new byte[(data.length / inBlocksize + 1) * outBlocksize]; + while (datapos < data.length) + { + byte[] tmp; + if (datapos + inBlocksize < data.length) + { + tmp = processBlock(data, datapos, inBlocksize); + datapos += inBlocksize; + } + else + { + tmp = processBlock(data, datapos, data.length - datapos); + datapos += data.length - datapos; + } + if (debug) + { + System.out.println("new datapos is " + datapos); + } + if (tmp != null) + { + System.arraycopy(tmp, 0, retval, retpos, tmp.length); + + retpos += tmp.length; + } + else + { + if (debug) + { + System.out.println("cipher returned null"); + } + throw new InvalidCipherTextException("cipher returned null"); + } + } + byte[] ret = new byte[retpos]; + System.arraycopy(retval, 0, ret, 0, retpos); + if (debug) + { + System.out.println("returning " + ret.length + " bytes"); + } + return ret; + } + else + { + if (debug) + { + System.out.println("data size is less then input block size, processing directly"); + } + return processBlock(data, 0, data.length); + } + } + + /** + * Computes the integer x that is expressed through the given primes and the + * congruences with the chinese remainder theorem (CRT). + * + * @param congruences + * the congruences c_i + * @param primes + * the primes p_i + * @return an integer x for that x % p_i == c_i + */ + private static BigInteger chineseRemainder(Vector congruences, Vector primes) + { + BigInteger retval = ZERO; + BigInteger all = ONE; + for (int i = 0; i < primes.size(); i++) + { + all = all.multiply((BigInteger)primes.elementAt(i)); + } + for (int i = 0; i < primes.size(); i++) + { + BigInteger a = (BigInteger)primes.elementAt(i); + BigInteger b = all.divide(a); + BigInteger b_ = b.modInverse(a); + BigInteger tmp = b.multiply(b_); + tmp = tmp.multiply((BigInteger)congruences.elementAt(i)); + retval = retval.add(tmp); + } + + return retval.mod(all); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java new file mode 100644 index 000000000..3abaeedec --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java @@ -0,0 +1,263 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * A Noekeon engine, using direct-key mode. + */ + +public class NoekeonEngine + implements BlockCipher +{ + private static final int genericSize = 16; // Block and key size, as well as the amount of rounds. + + private static final int[] nullVector = + { + 0x00, 0x00, 0x00, 0x00 // Used in decryption + }, + + roundConstants = + { + 0x80, 0x1b, 0x36, 0x6c, + 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, + 0xc6, 0x97, 0x35, 0x6a, + 0xd4 + }; + + private int[] state = new int[4], // a + subKeys = new int[4], // k + decryptKeys = new int[4]; + + private boolean _initialised, + _forEncryption; + + /** + * Create an instance of the Noekeon encryption algorithm + * and set some defaults + */ + public NoekeonEngine() + { + _initialised = false; + } + + public String getAlgorithmName() + { + return "Noekeon"; + } + + public int getBlockSize() + { + return genericSize; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to Noekeon init - " + params.getClass().getName()); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter)params; + + setKey(p.getKey()); + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (!_initialised) + { + throw new IllegalStateException(getAlgorithmName()+" not initialised"); + } + + if ((inOff + genericSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + genericSize) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + return (_forEncryption) ? encryptBlock(in, inOff, out, outOff) + : decryptBlock(in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * Re-key the cipher. + *
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ subKeys[0] = bytesToIntBig(key, 0);
+ subKeys[1] = bytesToIntBig(key, 4);
+ subKeys[2] = bytesToIntBig(key, 8);
+ subKeys[3] = bytesToIntBig(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ state[0] = bytesToIntBig(in, inOff);
+ state[1] = bytesToIntBig(in, inOff+4);
+ state[2] = bytesToIntBig(in, inOff+8);
+ state[3] = bytesToIntBig(in, inOff+12);
+
+ int i;
+ for (i = 0; i < genericSize; i++)
+ {
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+
+ intToBytesBig(state[0], out, outOff);
+ intToBytesBig(state[1], out, outOff+4);
+ intToBytesBig(state[2], out, outOff+8);
+ intToBytesBig(state[3], out, outOff+12);
+
+ return genericSize;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ state[0] = bytesToIntBig(in, inOff);
+ state[1] = bytesToIntBig(in, inOff+4);
+ state[2] = bytesToIntBig(in, inOff+8);
+ state[3] = bytesToIntBig(in, inOff+12);
+
+ System.arraycopy(subKeys, 0, decryptKeys, 0, subKeys.length);
+ theta(decryptKeys, nullVector);
+
+ int i;
+ for (i = genericSize; i > 0; i--)
+ {
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+
+ intToBytesBig(state[0], out, outOff);
+ intToBytesBig(state[1], out, outOff+4);
+ intToBytesBig(state[2], out, outOff+8);
+ intToBytesBig(state[3], out, outOff+12);
+
+ return genericSize;
+ }
+
+ private void gamma(int[] a)
+ {
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+
+ int tmp = a[3];
+ a[3] = a[0];
+ a[0] = tmp;
+ a[2] ^= a[0]^a[1]^a[3];
+
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+ }
+
+ private void theta(int[] a, int[] k)
+ {
+ int tmp;
+
+ tmp = a[0]^a[2];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[1] ^= tmp;
+ a[3] ^= tmp;
+
+ for (int i = 0; i < 4; i++)
+ {
+ a[i] ^= k[i];
+ }
+
+ tmp = a[1]^a[3];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[0] ^= tmp;
+ a[2] ^= tmp;
+ }
+
+ private void pi1(int[] a)
+ {
+ a[1] = rotl(a[1], 1);
+ a[2] = rotl(a[2], 5);
+ a[3] = rotl(a[3], 2);
+ }
+
+ private void pi2(int[] a)
+ {
+ a[1] = rotl(a[1], 31);
+ a[2] = rotl(a[2], 27);
+ a[3] = rotl(a[3], 30);
+ }
+
+ // Helpers
+
+ private int bytesToIntBig(byte[] in, int off)
+ {
+ return ((in[off++]) << 24) |
+ ((in[off++] & 0xff) << 16) |
+ ((in[off++] & 0xff) << 8) |
+ (in[off ] & 0xff);
+ }
+
+ private void intToBytesBig(int x, byte[] out, int off)
+ {
+ out[off++] = (byte)(x >>> 24);
+ out[off++] = (byte)(x >>> 16);
+ out[off++] = (byte)(x >>> 8);
+ out[off ] = (byte)x;
+ }
+
+ private int rotl(int x, int y)
+ {
+ return (x << y) | (x >>> (32-y));
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java
new file mode 100644
index 000000000..f8d284138
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java
@@ -0,0 +1,96 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+
+/**
+ * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting.
+ * Provided for the sake of completeness.
+ */
+public class NullEngine implements BlockCipher
+{
+ private boolean initialised;
+ protected static final int DEFAULT_BLOCK_SIZE = 1;
+ private final int blockSize;
+
+ /**
+ * Constructs a null engine with a block size of 1 byte.
+ */
+ public NullEngine()
+ {
+ this(DEFAULT_BLOCK_SIZE);
+ }
+
+ /**
+ * Constructs a null engine with a specific block size.
+ *
+ * @param blockSize the block size in bytes.
+ */
+ public NullEngine(int blockSize)
+ {
+ this.blockSize = blockSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#init(boolean, org.spongycastle.crypto.CipherParameters)
+ */
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ // we don't mind any parameters that may come in
+ this.initialised = true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#getAlgorithmName()
+ */
+ public String getAlgorithmName()
+ {
+ return "Null";
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#getBlockSize()
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#processBlock(byte[], int, byte[], int)
+ */
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException("Null engine not initialised");
+ }
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < blockSize; ++i)
+ {
+ out[outOff + i] = in[inOff + i];
+ }
+
+ return blockSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#reset()
+ */
+ public void reset()
+ {
+ // nothing needs to be done
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java
new file mode 100644
index 000000000..24bdab6a7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java
@@ -0,0 +1,317 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RC2Parameters;
+
+/**
+ * an implementation of RC2 as described in RFC 2268
+ * "A Description of the RC2(r) Encryption Algorithm" R. Rivest.
+ */
+public class RC2Engine
+ implements BlockCipher
+{
+ //
+ // the values we use for key expansion (based on the digits of PI)
+ //
+ private static byte[] piTable =
+ {
+ (byte)0xd9, (byte)0x78, (byte)0xf9, (byte)0xc4, (byte)0x19, (byte)0xdd, (byte)0xb5, (byte)0xed,
+ (byte)0x28, (byte)0xe9, (byte)0xfd, (byte)0x79, (byte)0x4a, (byte)0xa0, (byte)0xd8, (byte)0x9d,
+ (byte)0xc6, (byte)0x7e, (byte)0x37, (byte)0x83, (byte)0x2b, (byte)0x76, (byte)0x53, (byte)0x8e,
+ (byte)0x62, (byte)0x4c, (byte)0x64, (byte)0x88, (byte)0x44, (byte)0x8b, (byte)0xfb, (byte)0xa2,
+ (byte)0x17, (byte)0x9a, (byte)0x59, (byte)0xf5, (byte)0x87, (byte)0xb3, (byte)0x4f, (byte)0x13,
+ (byte)0x61, (byte)0x45, (byte)0x6d, (byte)0x8d, (byte)0x9, (byte)0x81, (byte)0x7d, (byte)0x32,
+ (byte)0xbd, (byte)0x8f, (byte)0x40, (byte)0xeb, (byte)0x86, (byte)0xb7, (byte)0x7b, (byte)0xb,
+ (byte)0xf0, (byte)0x95, (byte)0x21, (byte)0x22, (byte)0x5c, (byte)0x6b, (byte)0x4e, (byte)0x82,
+ (byte)0x54, (byte)0xd6, (byte)0x65, (byte)0x93, (byte)0xce, (byte)0x60, (byte)0xb2, (byte)0x1c,
+ (byte)0x73, (byte)0x56, (byte)0xc0, (byte)0x14, (byte)0xa7, (byte)0x8c, (byte)0xf1, (byte)0xdc,
+ (byte)0x12, (byte)0x75, (byte)0xca, (byte)0x1f, (byte)0x3b, (byte)0xbe, (byte)0xe4, (byte)0xd1,
+ (byte)0x42, (byte)0x3d, (byte)0xd4, (byte)0x30, (byte)0xa3, (byte)0x3c, (byte)0xb6, (byte)0x26,
+ (byte)0x6f, (byte)0xbf, (byte)0xe, (byte)0xda, (byte)0x46, (byte)0x69, (byte)0x7, (byte)0x57,
+ (byte)0x27, (byte)0xf2, (byte)0x1d, (byte)0x9b, (byte)0xbc, (byte)0x94, (byte)0x43, (byte)0x3,
+ (byte)0xf8, (byte)0x11, (byte)0xc7, (byte)0xf6, (byte)0x90, (byte)0xef, (byte)0x3e, (byte)0xe7,
+ (byte)0x6, (byte)0xc3, (byte)0xd5, (byte)0x2f, (byte)0xc8, (byte)0x66, (byte)0x1e, (byte)0xd7,
+ (byte)0x8, (byte)0xe8, (byte)0xea, (byte)0xde, (byte)0x80, (byte)0x52, (byte)0xee, (byte)0xf7,
+ (byte)0x84, (byte)0xaa, (byte)0x72, (byte)0xac, (byte)0x35, (byte)0x4d, (byte)0x6a, (byte)0x2a,
+ (byte)0x96, (byte)0x1a, (byte)0xd2, (byte)0x71, (byte)0x5a, (byte)0x15, (byte)0x49, (byte)0x74,
+ (byte)0x4b, (byte)0x9f, (byte)0xd0, (byte)0x5e, (byte)0x4, (byte)0x18, (byte)0xa4, (byte)0xec,
+ (byte)0xc2, (byte)0xe0, (byte)0x41, (byte)0x6e, (byte)0xf, (byte)0x51, (byte)0xcb, (byte)0xcc,
+ (byte)0x24, (byte)0x91, (byte)0xaf, (byte)0x50, (byte)0xa1, (byte)0xf4, (byte)0x70, (byte)0x39,
+ (byte)0x99, (byte)0x7c, (byte)0x3a, (byte)0x85, (byte)0x23, (byte)0xb8, (byte)0xb4, (byte)0x7a,
+ (byte)0xfc, (byte)0x2, (byte)0x36, (byte)0x5b, (byte)0x25, (byte)0x55, (byte)0x97, (byte)0x31,
+ (byte)0x2d, (byte)0x5d, (byte)0xfa, (byte)0x98, (byte)0xe3, (byte)0x8a, (byte)0x92, (byte)0xae,
+ (byte)0x5, (byte)0xdf, (byte)0x29, (byte)0x10, (byte)0x67, (byte)0x6c, (byte)0xba, (byte)0xc9,
+ (byte)0xd3, (byte)0x0, (byte)0xe6, (byte)0xcf, (byte)0xe1, (byte)0x9e, (byte)0xa8, (byte)0x2c,
+ (byte)0x63, (byte)0x16, (byte)0x1, (byte)0x3f, (byte)0x58, (byte)0xe2, (byte)0x89, (byte)0xa9,
+ (byte)0xd, (byte)0x38, (byte)0x34, (byte)0x1b, (byte)0xab, (byte)0x33, (byte)0xff, (byte)0xb0,
+ (byte)0xbb, (byte)0x48, (byte)0xc, (byte)0x5f, (byte)0xb9, (byte)0xb1, (byte)0xcd, (byte)0x2e,
+ (byte)0xc5, (byte)0xf3, (byte)0xdb, (byte)0x47, (byte)0xe5, (byte)0xa5, (byte)0x9c, (byte)0x77,
+ (byte)0xa, (byte)0xa6, (byte)0x20, (byte)0x68, (byte)0xfe, (byte)0x7f, (byte)0xc1, (byte)0xad
+ };
+
+ private static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey;
+ private boolean encrypting;
+
+ private int[] generateWorkingKey(
+ byte[] key,
+ int bits)
+ {
+ int x;
+ int[] xKey = new int[128];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ xKey[i] = key[i] & 0xff;
+ }
+
+ // Phase 1: Expand input key to 128 bytes
+ int len = key.length;
+
+ if (len < 128)
+ {
+ int index = 0;
+
+ x = xKey[len - 1];
+
+ do
+ {
+ x = piTable[(x + xKey[index++]) & 255] & 0xff;
+ xKey[len++] = x;
+ }
+ while (len < 128);
+ }
+
+ // Phase 2 - reduce effective key size to "bits"
+ len = (bits + 7) >> 3;
+ x = piTable[xKey[128 - len] & (255 >> (7 & -bits))] & 0xff;
+ xKey[128 - len] = x;
+
+ for (int i = 128 - len - 1; i >= 0; i--)
+ {
+ x = piTable[x ^ xKey[i + len]] & 0xff;
+ xKey[i] = x;
+ }
+
+ // Phase 3 - copy to newKey in little-endian order
+ int[] newKey = new int[64];
+
+ for (int i = 0; i != newKey.length; i++)
+ {
+ newKey[i] = (xKey[2 * i] + (xKey[2 * i + 1] << 8));
+ }
+
+ return newKey;
+ }
+
+ /**
+ * initialise a RC2 cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ this.encrypting = encrypting;
+
+ if (params instanceof RC2Parameters)
+ {
+ RC2Parameters param = (RC2Parameters)params;
+
+ workingKey = generateWorkingKey(param.getKey(),
+ param.getEffectiveKeyBits());
+ }
+ else if (params instanceof KeyParameter)
+ {
+ byte[] key = ((KeyParameter)params).getKey();
+
+ workingKey = generateWorkingKey(key, key.length * 8);
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC2 init - " + params.getClass().getName());
+ }
+
+ }
+
+ public void reset()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC2";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("RC2 engine 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;
+ }
+
+ /**
+ * return the result rotating the 16 bit number in x left by y
+ */
+ private int rotateWordLeft(
+ int x,
+ int y)
+ {
+ x &= 0xffff;
+ return (x << y) | (x >> (16 - y));
+ }
+
+ private void encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((in[inOff + 7] & 0xff) << 8) + (in[inOff + 6] & 0xff);
+ x54 = ((in[inOff + 5] & 0xff) << 8) + (in[inOff + 4] & 0xff);
+ x32 = ((in[inOff + 3] & 0xff) << 8) + (in[inOff + 2] & 0xff);
+ x10 = ((in[inOff + 1] & 0xff) << 8) + (in[inOff + 0] & 0xff);
+
+ for (int i = 0; i <= 16; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 20; i <= 40; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 44; i < 64; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ out[outOff + 0] = (byte)x10;
+ out[outOff + 1] = (byte)(x10 >> 8);
+ out[outOff + 2] = (byte)x32;
+ out[outOff + 3] = (byte)(x32 >> 8);
+ out[outOff + 4] = (byte)x54;
+ out[outOff + 5] = (byte)(x54 >> 8);
+ out[outOff + 6] = (byte)x76;
+ out[outOff + 7] = (byte)(x76 >> 8);
+ }
+
+ private void decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((in[inOff + 7] & 0xff) << 8) + (in[inOff + 6] & 0xff);
+ x54 = ((in[inOff + 5] & 0xff) << 8) + (in[inOff + 4] & 0xff);
+ x32 = ((in[inOff + 3] & 0xff) << 8) + (in[inOff + 2] & 0xff);
+ x10 = ((in[inOff + 1] & 0xff) << 8) + (in[inOff + 0] & 0xff);
+
+ for (int i = 60; i >= 44; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 40; i >= 20; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 16; i >= 0; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ out[outOff + 0] = (byte)x10;
+ out[outOff + 1] = (byte)(x10 >> 8);
+ out[outOff + 2] = (byte)x32;
+ out[outOff + 3] = (byte)(x32 >> 8);
+ out[outOff + 4] = (byte)x54;
+ out[outOff + 5] = (byte)(x54 >> 8);
+ out[outOff + 6] = (byte)x76;
+ out[outOff + 7] = (byte)(x76 >> 8);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java
new file mode 100644
index 000000000..000d0228d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java
@@ -0,0 +1,383 @@
+package org.spongycastle.crypto.engines;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Wrap keys according to RFC 3217 - RC2 mechanism
+ */
+public class RC2WrapEngine
+ implements Wrapper
+{
+ /** Field engine */
+ private CBCBlockCipher engine;
+
+ /** Field param */
+ private CipherParameters param;
+
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+
+ /** Field iv */
+ private byte[] iv;
+
+ /** Field forWrapping */
+ private boolean forWrapping;
+
+ private SecureRandom sr;
+
+ /** Field IV2 */
+ private static final byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05 };
+
+ //
+ // checksum digest
+ //
+ Digest sha1 = new SHA1Digest();
+ byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping
+ * @param param
+ */
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+ this.engine = new CBCBlockCipher(new RC2Engine());
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom pWithR = (ParametersWithRandom)param;
+ sr = pWithR.getRandom();
+ param = pWithR.getParameters();
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (param instanceof ParametersWithIV)
+ {
+ this.paramPlusIV = (ParametersWithIV)param;
+ this.iv = this.paramPlusIV.getIV();
+ this.param = this.paramPlusIV.getParameters();
+
+ if (this.forWrapping)
+ {
+ if ((this.iv == null) || (this.iv.length != 8))
+ {
+ throw new IllegalArgumentException("IV is not 8 octets");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "You should not supply an IV for unwrapping");
+ }
+ }
+ else
+ {
+ this.param = param;
+
+ if (this.forWrapping)
+ {
+
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+
+ sr.nextBytes(iv);
+
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ }
+ }
+
+ }
+
+ /**
+ * Method getAlgorithmName
+ *
+ * @return the algorithm name "RC2".
+ */
+ public String getAlgorithmName()
+ {
+ return "RC2";
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the wrapped bytes.
+ */
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("Not initialized for wrapping");
+ }
+
+ int length = inLen + 1;
+ if ((length % 8) != 0)
+ {
+ length += 8 - (length % 8);
+ }
+
+ byte keyToBeWrapped[] = new byte[length];
+
+ keyToBeWrapped[0] = (byte)inLen;
+ System.arraycopy(in, inOff, keyToBeWrapped, 1, inLen);
+
+ byte[] pad = new byte[keyToBeWrapped.length - inLen - 1];
+
+ if (pad.length > 0)
+ {
+ sr.nextBytes(pad);
+ System.arraycopy(pad, 0, keyToBeWrapped, inLen + 1, pad.length);
+ }
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = calculateCMSKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.length + CKS.length];
+
+ System.arraycopy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.length);
+ System.arraycopy(CKS, 0, WKCKS, keyToBeWrapped.length, CKS.length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+ byte TEMP1[] = new byte[WKCKS.length];
+
+ System.arraycopy(WKCKS, 0, TEMP1, 0, WKCKS.length);
+
+ int noOfBlocks = WKCKS.length / engine.getBlockSize();
+ int extraBytes = WKCKS.length % engine.getBlockSize();
+
+ if (extraBytes != 0)
+ {
+ throw new IllegalStateException("Not multiple of block length");
+ }
+
+ engine.init(true, paramPlusIV);
+
+ for (int i = 0; i < noOfBlocks; i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP1, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Left TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.length + TEMP1.length];
+
+ System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
+ System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = new byte[TEMP2.length];
+
+ for (int i = 0; i < TEMP2.length; i++)
+ {
+ TEMP3[i] = TEMP2[TEMP2.length - (i + 1)];
+ }
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the
+ // desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(true, param2);
+
+ for (int i = 0; i < noOfBlocks + 1; i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the unwrapped bytes.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+
+ if (forWrapping)
+ {
+ throw new IllegalStateException("Not set for unwrapping");
+ }
+
+ if (in == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ if (inLen % engine.getBlockSize() != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of "
+ + engine.getBlockSize());
+ }
+
+ /*
+ * // Check if the length of the cipher text is reasonable given the key //
+ * type. It must be 40 bytes for a 168 bit key and either 32, 40, or //
+ * 48 bytes for a 128, 192, or 256 bit key. If the length is not
+ * supported // or inconsistent with the algorithm for which the key is
+ * intended, // return error. // // we do not accept 168 bit keys. it
+ * has to be 192 bit. int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ * int lengthB = estimatedKeyLengthInBit % 8;
+ *
+ * if ((lengthA != keyToBeUnwrapped.length) || (lengthB != 0)) { throw
+ * new XMLSecurityException("empty"); }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the
+ // output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(false, param2);
+
+ byte TEMP3[] = new byte[inLen];
+
+ System.arraycopy(in, inOff, TEMP3, 0, inLen);
+
+ for (int i = 0; i < (TEMP3.length / engine.getBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = new byte[TEMP3.length];
+
+ for (int i = 0; i < TEMP3.length; i++)
+ {
+ TEMP2[i] = TEMP3[TEMP3.length - (i + 1)];
+ }
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining
+ // octets.
+ this.iv = new byte[8];
+
+ byte[] TEMP1 = new byte[TEMP2.length - 8];
+
+ System.arraycopy(TEMP2, 0, this.iv, 0, 8);
+ System.arraycopy(TEMP2, 8, TEMP1, 0, TEMP2.length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+
+ this.engine.init(false, this.paramPlusIV);
+
+ byte[] LCEKPADICV = new byte[TEMP1.length];
+
+ System.arraycopy(TEMP1, 0, LCEKPADICV, 0, TEMP1.length);
+
+ for (int i = 0; i < (LCEKPADICV.length / engine.getBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(LCEKPADICV, currentBytePos, LCEKPADICV,
+ currentBytePos);
+ }
+
+ // Decompose LCEKPADICV. CKS is the last 8 octets and WK, the wrapped
+ // key, are
+ // those octets before the CKS.
+ byte[] result = new byte[LCEKPADICV.length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+
+ System.arraycopy(LCEKPADICV, 0, result, 0, LCEKPADICV.length - 8);
+ System.arraycopy(LCEKPADICV, LCEKPADICV.length - 8, CKStoBeVerified, 0,
+ 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and
+ // compare
+ // with the CKS extracted in the above step. If they are not equal,
+ // return error.
+ if (!checkCMSKeyChecksum(result, CKStoBeVerified))
+ {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ if ((result.length - ((result[0] & 0xff) + 1)) > 7)
+ {
+ throw new InvalidCipherTextException("too many pad bytes ("
+ + (result.length - ((result[0] & 0xff) + 1)) + ")");
+ }
+
+ // CEK is the wrapped key, now extracted for use in data decryption.
+ byte[] CEK = new byte[result[0]];
+ System.arraycopy(result, 1, CEK, 0, CEK.length);
+ return CEK;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * @param key
+ * @return
+ * @throws RuntimeException
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private byte[] calculateCMSKeyChecksum(
+ byte[] key)
+ {
+ byte[] result = new byte[8];
+
+ sha1.update(key, 0, key.length);
+ sha1.doFinal(digest, 0);
+
+ System.arraycopy(digest, 0, result, 0, 8);
+
+ return result;
+ }
+
+ /**
+ * @param key
+ * @param checksum
+ * @return
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private boolean checkCMSKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java
new file mode 100644
index 000000000..712426f1e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java
@@ -0,0 +1,144 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+
+public class RC4Engine implements StreamCipher
+{
+ private final static int STATE_LENGTH = 256;
+
+ /*
+ * variables to hold the state of the RC4 engine
+ * during encryption and decryption
+ */
+
+ private byte[] engineState = null;
+ private int x = 0;
+ private int y = 0;
+ private byte[] workingKey = null;
+
+ /**
+ * initialise a RC4 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params
+ )
+ {
+ if (params instanceof KeyParameter)
+ {
+ /*
+ * RC4 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ workingKey = ((KeyParameter)params).getKey();
+ setKey(workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC4";
+ }
+
+ public byte returnByte(byte in)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len ; i++)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ out[i+outOff] = (byte)(in[i + inOff]
+ ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+ }
+
+ public void reset()
+ {
+ setKey(workingKey);
+ }
+
+ // Private implementation
+
+ private void setKey(byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ // System.out.println("the key length is ; "+ workingKey.length);
+
+ x = 0;
+ y = 0;
+
+ if (engineState == null)
+ {
+ engineState = new byte[STATE_LENGTH];
+ }
+
+ // reset the state of the engine
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ engineState[i] = (byte)i;
+ }
+
+ int i1 = 0;
+ int i2 = 0;
+
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
+ // do the byte-swap inline
+ byte tmp = engineState[i];
+ engineState[i] = engineState[i2];
+ engineState[i2] = tmp;
+ i1 = (i1+1) % keyBytes.length;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java
new file mode 100644
index 000000000..24619716d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java
@@ -0,0 +1,287 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the RC5 Encryption Algorithm
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * http://www.rsasecurity.com/rsalabs/cryptobytes.
+ *
+ * This implementation has a word size of 32 bits. + *
+ * Implementation courtesy of Tito Pena. + */ +public class RC532Engine + implements BlockCipher +{ + /* + * the number of rounds to perform + */ + private int _noRounds; + + /* + * the expanded key array of size 2*(rounds + 1) + */ + private int _S[]; + + /* + * our "magic constants" for 32 32 + * + * Pw = Odd((e-2) * 2^wordsize) + * Qw = Odd((o-2) * 2^wordsize) + * + * where e is the base of natural logarithms (2.718281828...) + * and o is the golden ratio (1.61803398...) + */ + private static final int P32 = 0xb7e15163; + private static final int Q32 = 0x9e3779b9; + + private boolean forEncryption; + + /** + * Create an instance of the RC5 encryption algorithm + * and set some defaults + */ + public RC532Engine() + { + _noRounds = 12; // the default + _S = null; + } + + public String getAlgorithmName() + { + return "RC5-32"; + } + + public int getBlockSize() + { + return 2 * 4; + } + + /** + * initialise a RC5-32 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (params instanceof RC5Parameters) + { + RC5Parameters p = (RC5Parameters)params; + + _noRounds = p.getRounds(); + + setKey(p.getKey()); + } + else if (params instanceof KeyParameter) + { + KeyParameter p = (KeyParameter)params; + + setKey(p.getKey()); + } + else + { + throw new IllegalArgumentException("invalid parameter passed to RC532 init - " + params.getClass().getName()); + } + + this.forEncryption = forEncryption; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + return (forEncryption) ? encryptBlock(in, inOff, out, outOff) + : decryptBlock(in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * Re-key the cipher. + *
+ * @param key the key to be used + */ + private void setKey( + byte[] key) + { + // + // KEY EXPANSION: + // + // There are 3 phases to the key expansion. + // + // Phase 1: + // Copy the secret key K[0...b-1] into an array L[0..c-1] of + // c = ceil(b/u), where u = 32/8 in little-endian order. + // In other words, we fill up L using u consecutive key bytes + // of K. Any unfilled byte positions in L are zeroed. In the + // case that b = c = 0, set c = 1 and L[0] = 0. + // + int[] L = new int[(key.length + (4 - 1)) / 4]; + + for (int i = 0; i != key.length; i++) + { + L[i / 4] += (key[i] & 0xff) << (8 * (i % 4)); + } + + // + // Phase 2: + // Initialize S to a particular fixed pseudo-random bit pattern + // using an arithmetic progression modulo 2^wordsize determined + // by the magic numbers, Pw & Qw. + // + _S = new int[2*(_noRounds + 1)]; + + _S[0] = P32; + for (int i=1; i < _S.length; i++) + { + _S[i] = (_S[i-1] + Q32); + } + + // + // Phase 3: + // Mix in the user's secret key in 3 passes over the arrays S & L. + // The max of the arrays sizes is used as the loop control + // + int iter; + + if (L.length > _S.length) + { + iter = 3 * L.length; + } + else + { + iter = 3 * _S.length; + } + + int A = 0, B = 0; + int i = 0, j = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[i] = rotateLeft(_S[i] + A + B, 3); + B = L[j] = rotateLeft(L[j] + A + B, A+B); + i = (i+1) % _S.length; + j = (j+1) % L.length; + } + } + + /** + * Encrypt the given block starting at the given offset and place + * the result in the provided buffer starting at the given offset. + *
+ * @param in in byte buffer containing data to encrypt + * @param inOff offset into src buffer + * @param out out buffer where encrypted data is written + * @param outOff offset into out buffer + */ + private int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int A = bytesToWord(in, inOff) + _S[0]; + int B = bytesToWord(in, inOff + 4) + _S[1]; + + for (int i = 1; i <= _noRounds; i++) + { + A = rotateLeft(A ^ B, B) + _S[2*i]; + B = rotateLeft(B ^ A, A) + _S[2*i+1]; + } + + wordToBytes(A, out, outOff); + wordToBytes(B, out, outOff + 4); + + return 2 * 4; + } + + private int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int A = bytesToWord(in, inOff); + int B = bytesToWord(in, inOff + 4); + + for (int i = _noRounds; i >= 1; i--) + { + B = rotateRight(B - _S[2*i+1], A) ^ A; + A = rotateRight(A - _S[2*i], B) ^ B; + } + + wordToBytes(A - _S[0], out, outOff); + wordToBytes(B - _S[1], out, outOff + 4); + + return 2 * 4; + } + + + ////////////////////////////////////////////////////////////// + // + // PRIVATE Helper Methods + // + ////////////////////////////////////////////////////////////// + + /** + * Perform a left "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(32) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + *
+ * @param x word to rotate + * @param y number of bits to rotate % 32 + */ + private int rotateLeft(int x, int y) + { + return ((x << (y & (32-1))) | (x >>> (32 - (y & (32-1))))); + } + + /** + * Perform a right "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(32) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + *
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int rotateRight(int x, int y)
+ {
+ return ((x >>> (y & (32-1))) | (x << (32 - (y & (32-1)))));
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8)
+ | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24);
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff] = (byte)word;
+ dst[dstOff + 1] = (byte)(word >> 8);
+ dst[dstOff + 2] = (byte)(word >> 16);
+ dst[dstOff + 3] = (byte)(word >> 24);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java
new file mode 100644
index 000000000..06fcbf7c3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java
@@ -0,0 +1,288 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the RC5 Encryption Algorithm
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * http://www.rsasecurity.com/rsalabs/cryptobytes.
+ *
+ * This implementation is set to work with a 64 bit word size. + *
+ * Implementation courtesy of Tito Pena. + */ +public class RC564Engine + implements BlockCipher +{ + private static final int wordSize = 64; + private static final int bytesPerWord = wordSize / 8; + + /* + * the number of rounds to perform + */ + private int _noRounds; + + /* + * the expanded key array of size 2*(rounds + 1) + */ + private long _S[]; + + /* + * our "magic constants" for wordSize 62 + * + * Pw = Odd((e-2) * 2^wordsize) + * Qw = Odd((o-2) * 2^wordsize) + * + * where e is the base of natural logarithms (2.718281828...) + * and o is the golden ratio (1.61803398...) + */ + private static final long P64 = 0xb7e151628aed2a6bL; + private static final long Q64 = 0x9e3779b97f4a7c15L; + + private boolean forEncryption; + + /** + * Create an instance of the RC5 encryption algorithm + * and set some defaults + */ + public RC564Engine() + { + _noRounds = 12; + _S = null; + } + + public String getAlgorithmName() + { + return "RC5-64"; + } + + public int getBlockSize() + { + return 2 * bytesPerWord; + } + + /** + * initialise a RC5-64 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (!(params instanceof RC5Parameters)) + { + throw new IllegalArgumentException("invalid parameter passed to RC564 init - " + params.getClass().getName()); + } + + RC5Parameters p = (RC5Parameters)params; + + this.forEncryption = forEncryption; + + _noRounds = p.getRounds(); + + setKey(p.getKey()); + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + return (forEncryption) ? encryptBlock(in, inOff, out, outOff) + : decryptBlock(in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * Re-key the cipher. + *
+ * @param key the key to be used + */ + private void setKey( + byte[] key) + { + // + // KEY EXPANSION: + // + // There are 3 phases to the key expansion. + // + // Phase 1: + // Copy the secret key K[0...b-1] into an array L[0..c-1] of + // c = ceil(b/u), where u = wordSize/8 in little-endian order. + // In other words, we fill up L using u consecutive key bytes + // of K. Any unfilled byte positions in L are zeroed. In the + // case that b = c = 0, set c = 1 and L[0] = 0. + // + long[] L = new long[(key.length + (bytesPerWord - 1)) / bytesPerWord]; + + for (int i = 0; i != key.length; i++) + { + L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord)); + } + + // + // Phase 2: + // Initialize S to a particular fixed pseudo-random bit pattern + // using an arithmetic progression modulo 2^wordsize determined + // by the magic numbers, Pw & Qw. + // + _S = new long[2*(_noRounds + 1)]; + + _S[0] = P64; + for (int i=1; i < _S.length; i++) + { + _S[i] = (_S[i-1] + Q64); + } + + // + // Phase 3: + // Mix in the user's secret key in 3 passes over the arrays S & L. + // The max of the arrays sizes is used as the loop control + // + int iter; + + if (L.length > _S.length) + { + iter = 3 * L.length; + } + else + { + iter = 3 * _S.length; + } + + long A = 0, B = 0; + int i = 0, j = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[i] = rotateLeft(_S[i] + A + B, 3); + B = L[j] = rotateLeft(L[j] + A + B, A+B); + i = (i+1) % _S.length; + j = (j+1) % L.length; + } + } + + /** + * Encrypt the given block starting at the given offset and place + * the result in the provided buffer starting at the given offset. + *
+ * @param in in byte buffer containing data to encrypt + * @param inOff offset into src buffer + * @param out out buffer where encrypted data is written + * @param outOff offset into out buffer + */ + private int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + long A = bytesToWord(in, inOff) + _S[0]; + long B = bytesToWord(in, inOff + bytesPerWord) + _S[1]; + + for (int i = 1; i <= _noRounds; i++) + { + A = rotateLeft(A ^ B, B) + _S[2*i]; + B = rotateLeft(B ^ A, A) + _S[2*i+1]; + } + + wordToBytes(A, out, outOff); + wordToBytes(B, out, outOff + bytesPerWord); + + return 2 * bytesPerWord; + } + + private int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + long A = bytesToWord(in, inOff); + long B = bytesToWord(in, inOff + bytesPerWord); + + for (int i = _noRounds; i >= 1; i--) + { + B = rotateRight(B - _S[2*i+1], A) ^ A; + A = rotateRight(A - _S[2*i], B) ^ B; + } + + wordToBytes(A - _S[0], out, outOff); + wordToBytes(B - _S[1], out, outOff + bytesPerWord); + + return 2 * bytesPerWord; + } + + + ////////////////////////////////////////////////////////////// + // + // PRIVATE Helper Methods + // + ////////////////////////////////////////////////////////////// + + /** + * Perform a left "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + *
+ * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private long rotateLeft(long x, long y) + { + return ((x << (y & (wordSize-1))) | (x >>> (wordSize - (y & (wordSize-1))))); + } + + /** + * Perform a right "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + *
+ * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private long rotateRight(long x, long y) + { + return ((x >>> (y & (wordSize-1))) | (x << (wordSize - (y & (wordSize-1))))); + } + + private long bytesToWord( + byte[] src, + int srcOff) + { + long word = 0; + + for (int i = bytesPerWord - 1; i >= 0; i--) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void wordToBytes( + long word, + byte[] dst, + int dstOff) + { + for (int i = 0; i < bytesPerWord; i++) + { + dst[i + dstOff] = (byte)word; + word >>>= 8; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java new file mode 100644 index 000000000..e91e654e8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java @@ -0,0 +1,363 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * An RC6 engine. + */ +public class RC6Engine + implements BlockCipher +{ + private static final int wordSize = 32; + private static final int bytesPerWord = wordSize / 8; + + /* + * the number of rounds to perform + */ + private static final int _noRounds = 20; + + /* + * the expanded key array of size 2*(rounds + 1) + */ + private int _S[]; + + /* + * our "magic constants" for wordSize 32 + * + * Pw = Odd((e-2) * 2^wordsize) + * Qw = Odd((o-2) * 2^wordsize) + * + * where e is the base of natural logarithms (2.718281828...) + * and o is the golden ratio (1.61803398...) + */ + private static final int P32 = 0xb7e15163; + private static final int Q32 = 0x9e3779b9; + + private static final int LGW = 5; // log2(32) + + private boolean forEncryption; + + /** + * Create an instance of the RC6 encryption algorithm + * and set some defaults + */ + public RC6Engine() + { + _S = null; + } + + public String getAlgorithmName() + { + return "RC6"; + } + + public int getBlockSize() + { + return 4 * bytesPerWord; + } + + /** + * initialise a RC5-32 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to RC6 init - " + params.getClass().getName()); + } + + KeyParameter p = (KeyParameter)params; + this.forEncryption = forEncryption; + setKey(p.getKey()); + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int blockSize = getBlockSize(); + if (_S == null) + { + throw new IllegalStateException("RC6 engine not initialised"); + } + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + blockSize) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + return (forEncryption) + ? encryptBlock(in, inOff, out, outOff) + : decryptBlock(in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * Re-key the cipher. + *
+ * @param key the key to be used + */ + private void setKey( + byte[] key) + { + + // + // KEY EXPANSION: + // + // There are 3 phases to the key expansion. + // + // Phase 1: + // Copy the secret key K[0...b-1] into an array L[0..c-1] of + // c = ceil(b/u), where u = wordSize/8 in little-endian order. + // In other words, we fill up L using u consecutive key bytes + // of K. Any unfilled byte positions in L are zeroed. In the + // case that b = c = 0, set c = 1 and L[0] = 0. + // + // compute number of dwords + int c = (key.length + (bytesPerWord - 1)) / bytesPerWord; + if (c == 0) + { + c = 1; + } + int[] L = new int[(key.length + bytesPerWord - 1) / bytesPerWord]; + + // load all key bytes into array of key dwords + for (int i = key.length - 1; i >= 0; i--) + { + L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff); + } + + // + // Phase 2: + // Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords. + // Initialize S to a particular fixed pseudo-random bit pattern + // using an arithmetic progression modulo 2^wordsize determined + // by the magic numbers, Pw & Qw. + // + _S = new int[2+2*_noRounds+2]; + + _S[0] = P32; + for (int i=1; i < _S.length; i++) + { + _S[i] = (_S[i-1] + Q32); + } + + // + // Phase 3: + // Mix in the user's secret key in 3 passes over the arrays S & L. + // The max of the arrays sizes is used as the loop control + // + int iter; + + if (L.length > _S.length) + { + iter = 3 * L.length; + } + else + { + iter = 3 * _S.length; + } + + int A = 0; + int B = 0; + int i = 0, j = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[i] = rotateLeft(_S[i] + A + B, 3); + B = L[j] = rotateLeft(L[j] + A + B, A+B); + i = (i+1) % _S.length; + j = (j+1) % L.length; + } + } + + private int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + // load A,B,C and D registers from in. + int A = bytesToWord(in, inOff); + int B = bytesToWord(in, inOff + bytesPerWord); + int C = bytesToWord(in, inOff + bytesPerWord*2); + int D = bytesToWord(in, inOff + bytesPerWord*3); + + // Do pseudo-round #0: pre-whitening of B and D + B += _S[0]; + D += _S[1]; + + // perform round #1,#2 ... #ROUNDS of encryption + for (int i = 1; i <= _noRounds; i++) + { + int t = 0,u = 0; + + t = B*(2*B+1); + t = rotateLeft(t,5); + + u = D*(2*D+1); + u = rotateLeft(u,5); + + A ^= t; + A = rotateLeft(A,u); + A += _S[2*i]; + + C ^= u; + C = rotateLeft(C,t); + C += _S[2*i+1]; + + int temp = A; + A = B; + B = C; + C = D; + D = temp; + } + // do pseudo-round #(ROUNDS+1) : post-whitening of A and C + A += _S[2*_noRounds+2]; + C += _S[2*_noRounds+3]; + + // store A, B, C and D registers to out + wordToBytes(A, out, outOff); + wordToBytes(B, out, outOff + bytesPerWord); + wordToBytes(C, out, outOff + bytesPerWord*2); + wordToBytes(D, out, outOff + bytesPerWord*3); + + return 4 * bytesPerWord; + } + + private int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + // load A,B,C and D registers from out. + int A = bytesToWord(in, inOff); + int B = bytesToWord(in, inOff + bytesPerWord); + int C = bytesToWord(in, inOff + bytesPerWord*2); + int D = bytesToWord(in, inOff + bytesPerWord*3); + + // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C + C -= _S[2*_noRounds+3]; + A -= _S[2*_noRounds+2]; + + // Undo round #ROUNDS, .., #2,#1 of encryption + for (int i = _noRounds; i >= 1; i--) + { + int t=0,u = 0; + + int temp = D; + D = C; + C = B; + B = A; + A = temp; + + t = B*(2*B+1); + t = rotateLeft(t, LGW); + + u = D*(2*D+1); + u = rotateLeft(u, LGW); + + C -= _S[2*i+1]; + C = rotateRight(C,t); + C ^= u; + + A -= _S[2*i]; + A = rotateRight(A,u); + A ^= t; + + } + // Undo pseudo-round #0: pre-whitening of B and D + D -= _S[1]; + B -= _S[0]; + + wordToBytes(A, out, outOff); + wordToBytes(B, out, outOff + bytesPerWord); + wordToBytes(C, out, outOff + bytesPerWord*2); + wordToBytes(D, out, outOff + bytesPerWord*3); + + return 4 * bytesPerWord; + } + + + ////////////////////////////////////////////////////////////// + // + // PRIVATE Helper Methods + // + ////////////////////////////////////////////////////////////// + + /** + * Perform a left "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is 32. + *
+ * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private int rotateLeft(int x, int y) + { + return (x << y) | (x >>> -y); + } + + /** + * Perform a right "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + *
+ * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private int rotateRight(int x, int y) + { + return (x >>> y) | (x << -y); + } + + private int bytesToWord( + byte[] src, + int srcOff) + { + int word = 0; + + for (int i = bytesPerWord - 1; i >= 0; i--) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void wordToBytes( + int word, + byte[] dst, + int dstOff) + { + for (int i = 0; i < bytesPerWord; i++) + { + dst[i + dstOff] = (byte)word; + word >>>= 8; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java new file mode 100644 index 000000000..f55a717a3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java @@ -0,0 +1,175 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.ParametersWithRandom; + +import java.security.SecureRandom; + +/** + * an implementation of the RFC 3211 Key Wrap + * Specification. + */ +public class RFC3211WrapEngine + implements Wrapper +{ + private CBCBlockCipher engine; + private ParametersWithIV param; + private boolean forWrapping; + private SecureRandom rand; + + public RFC3211WrapEngine(BlockCipher engine) + { + this.engine = new CBCBlockCipher(engine); + } + + public void init( + boolean forWrapping, + CipherParameters param) + { + this.forWrapping = forWrapping; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom)param; + + rand = p.getRandom(); + this.param = (ParametersWithIV)p.getParameters(); + } + else + { + if (forWrapping) + { + rand = new SecureRandom(); + } + + this.param = (ParametersWithIV)param; + } + } + + public String getAlgorithmName() + { + return engine.getUnderlyingCipher().getAlgorithmName() + "/RFC3211Wrap"; + } + + public byte[] wrap( + byte[] in, + int inOff, + int inLen) + { + if (!forWrapping) + { + throw new IllegalStateException("not set for wrapping"); + } + + engine.init(true, param); + + int blockSize = engine.getBlockSize(); + byte[] cekBlock; + + if (inLen + 4 < blockSize * 2) + { + cekBlock = new byte[blockSize * 2]; + } + else + { + cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize]; + } + + cekBlock[0] = (byte)inLen; + cekBlock[1] = (byte)~in[inOff]; + cekBlock[2] = (byte)~in[inOff + 1]; + cekBlock[3] = (byte)~in[inOff + 2]; + + System.arraycopy(in, inOff, cekBlock, 4, inLen); + + for (int i = inLen + 4; i < cekBlock.length; i++) + { + cekBlock[i] = (byte)rand.nextInt(); + } + + for (int i = 0; i < cekBlock.length; i += blockSize) + { + engine.processBlock(cekBlock, i, cekBlock, i); + } + + for (int i = 0; i < cekBlock.length; i += blockSize) + { + engine.processBlock(cekBlock, i, cekBlock, i); + } + + return cekBlock; + } + + public byte[] unwrap( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forWrapping) + { + throw new IllegalStateException("not set for unwrapping"); + } + + int blockSize = engine.getBlockSize(); + + if (inLen < 2 * blockSize) + { + throw new InvalidCipherTextException("input too short"); + } + + byte[] cekBlock = new byte[inLen]; + byte[] iv = new byte[blockSize]; + + System.arraycopy(in, inOff, cekBlock, 0, inLen); + System.arraycopy(in, inOff, iv, 0, iv.length); + + engine.init(false, new ParametersWithIV(param.getParameters(), iv)); + + for (int i = blockSize; i < cekBlock.length; i += blockSize) + { + engine.processBlock(cekBlock, i, cekBlock, i); + } + + System.arraycopy(cekBlock, cekBlock.length - iv.length, iv, 0, iv.length); + + engine.init(false, new ParametersWithIV(param.getParameters(), iv)); + + engine.processBlock(cekBlock, 0, cekBlock, 0); + + engine.init(false, param); + + for (int i = 0; i < cekBlock.length; i += blockSize) + { + engine.processBlock(cekBlock, i, cekBlock, i); + } + + if ((cekBlock[0] & 0xff) > cekBlock.length - 4) + { + throw new InvalidCipherTextException("wrapped key corrupted"); + } + + byte[] key = new byte[cekBlock[0] & 0xff]; + + System.arraycopy(cekBlock, 4, key, 0, cekBlock[0]); + + // Note: Using constant time comparison + int nonEqual = 0; + for (int i = 0; i != 3; i++) + { + byte check = (byte)~cekBlock[1 + i]; + nonEqual |= (check ^ key[i]); + } + if (nonEqual != 0) + { + throw new InvalidCipherTextException("wrapped key fails checksum"); + } + + return key; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java new file mode 100644 index 000000000..67ee43a79 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java @@ -0,0 +1,177 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.util.Arrays; + +/** + * an implementation of the AES Key Wrapper from the NIST Key Wrap + * Specification as described in RFC 3394. + *
+ * For further details see: http://www.ietf.org/rfc/rfc3394.txt + * and http://csrc.nist.gov/encryption/kms/key-wrap.pdf. + */ +public class RFC3394WrapEngine + implements Wrapper +{ + private BlockCipher engine; + private KeyParameter param; + private boolean forWrapping; + + private byte[] iv = { + (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, + (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 }; + + public RFC3394WrapEngine(BlockCipher engine) + { + this.engine = engine; + } + + public void init( + boolean forWrapping, + CipherParameters param) + { + this.forWrapping = forWrapping; + + if (param instanceof ParametersWithRandom) + { + param = ((ParametersWithRandom) param).getParameters(); + } + + if (param instanceof KeyParameter) + { + this.param = (KeyParameter)param; + } + else if (param instanceof ParametersWithIV) + { + this.iv = ((ParametersWithIV)param).getIV(); + this.param = (KeyParameter)((ParametersWithIV) param).getParameters(); + if (this.iv.length != 8) + { + throw new IllegalArgumentException("IV not equal to 8"); + } + } + } + + public String getAlgorithmName() + { + return engine.getAlgorithmName(); + } + + public byte[] wrap( + byte[] in, + int inOff, + int inLen) + { + if (!forWrapping) + { + throw new IllegalStateException("not set for wrapping"); + } + + int n = inLen / 8; + + if ((n * 8) != inLen) + { + throw new DataLengthException("wrap data must be a multiple of 8 bytes"); + } + + byte[] block = new byte[inLen + iv.length]; + byte[] buf = new byte[8 + iv.length]; + + System.arraycopy(iv, 0, block, 0, iv.length); + System.arraycopy(in, inOff, block, iv.length, inLen); + + engine.init(true, param); + + for (int j = 0; j != 6; j++) + { + for (int i = 1; i <= n; i++) + { + System.arraycopy(block, 0, buf, 0, iv.length); + System.arraycopy(block, 8 * i, buf, iv.length, 8); + engine.processBlock(buf, 0, buf, 0); + + int t = n * j + i; + for (int k = 1; t != 0; k++) + { + byte v = (byte)t; + + buf[iv.length - k] ^= v; + + t >>>= 8; + } + + System.arraycopy(buf, 0, block, 0, 8); + System.arraycopy(buf, 8, block, 8 * i, 8); + } + } + + return block; + } + + public byte[] unwrap( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forWrapping) + { + throw new IllegalStateException("not set for unwrapping"); + } + + int n = inLen / 8; + + if ((n * 8) != inLen) + { + throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes"); + } + + byte[] block = new byte[inLen - iv.length]; + byte[] a = new byte[iv.length]; + byte[] buf = new byte[8 + iv.length]; + + System.arraycopy(in, inOff, a, 0, iv.length); + System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length); + + engine.init(false, param); + + n = n - 1; + + for (int j = 5; j >= 0; j--) + { + for (int i = n; i >= 1; i--) + { + System.arraycopy(a, 0, buf, 0, iv.length); + System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8); + + int t = n * j + i; + for (int k = 1; t != 0; k++) + { + byte v = (byte)t; + + buf[iv.length - k] ^= v; + + t >>>= 8; + } + + engine.processBlock(buf, 0, buf, 0); + System.arraycopy(buf, 0, a, 0, 8); + System.arraycopy(buf, 8, block, 8 * (i - 1), 8); + } + } + + if (!Arrays.constantTimeAreEqual(a, iv)) + { + throw new InvalidCipherTextException("checksum failed"); + } + + return block; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java new file mode 100644 index 000000000..d254f1c60 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java @@ -0,0 +1,126 @@ +package org.spongycastle.crypto.engines; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; +import org.spongycastle.util.BigIntegers; + +/** + * this does your basic RSA algorithm with blinding + */ +public class RSABlindedEngine + implements AsymmetricBlockCipher +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + + private RSACoreEngine core = new RSACoreEngine(); + private RSAKeyParameters key; + private SecureRandom random; + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void init( + boolean forEncryption, + CipherParameters param) + { + core.init(forEncryption, param); + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + key = (RSAKeyParameters)rParam.getParameters(); + random = rParam.getRandom(); + } + else + { + key = (RSAKeyParameters)param; + random = new SecureRandom(); + } + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + return core.getInputBlockSize(); + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int getOutputBlockSize() + { + return core.getOutputBlockSize(); + } + + /** + * Process a single block using the basic RSA algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @exception DataLengthException the input block is too large. + */ + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + { + if (key == null) + { + throw new IllegalStateException("RSA engine not initialised"); + } + + BigInteger input = core.convertInput(in, inOff, inLen); + + BigInteger result; + if (key instanceof RSAPrivateCrtKeyParameters) + { + RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key; + + BigInteger e = k.getPublicExponent(); + if (e != null) // can't do blinding without a public exponent + { + BigInteger m = k.getModulus(); + BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random); + + BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m); + BigInteger blindedResult = core.processBlock(blindedInput); + + BigInteger rInv = r.modInverse(m); + result = blindedResult.multiply(rInv).mod(m); + } + else + { + result = core.processBlock(input); + } + } + else + { + result = core.processBlock(input); + } + + return core.convertOutput(result); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java new file mode 100644 index 000000000..1888916e5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java @@ -0,0 +1,137 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.RSABlindingParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; + +import java.math.BigInteger; + +/** + * This does your basic RSA Chaum's blinding and unblinding as outlined in + * "Handbook of Applied Cryptography", page 475. You need to use this if you are + * trying to get another party to generate signatures without them being aware + * of the message they are signing. + */ +public class RSABlindingEngine + implements AsymmetricBlockCipher +{ + private RSACoreEngine core = new RSACoreEngine(); + + private RSAKeyParameters key; + private BigInteger blindingFactor; + + private boolean forEncryption; + + /** + * Initialise the blinding engine. + * + * @param forEncryption true if we are encrypting (blinding), false otherwise. + * @param param the necessary RSA key parameters. + */ + public void init( + boolean forEncryption, + CipherParameters param) + { + RSABlindingParameters p; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + p = (RSABlindingParameters)rParam.getParameters(); + } + else + { + p = (RSABlindingParameters)param; + } + + core.init(forEncryption, p.getPublicKey()); + + this.forEncryption = forEncryption; + this.key = p.getPublicKey(); + this.blindingFactor = p.getBlindingFactor(); + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + return core.getInputBlockSize(); + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int getOutputBlockSize() + { + return core.getOutputBlockSize(); + } + + /** + * Process a single block using the RSA blinding algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @throws DataLengthException the input block is too large. + */ + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + { + BigInteger msg = core.convertInput(in, inOff, inLen); + + if (forEncryption) + { + msg = blindMessage(msg); + } + else + { + msg = unblindMessage(msg); + } + + return core.convertOutput(msg); + } + + /* + * Blind message with the blind factor. + */ + private BigInteger blindMessage( + BigInteger msg) + { + BigInteger blindMsg = blindingFactor; + blindMsg = msg.multiply(blindMsg.modPow(key.getExponent(), key.getModulus())); + blindMsg = blindMsg.mod(key.getModulus()); + + return blindMsg; + } + + /* + * Unblind the message blinded with the blind factor. + */ + private BigInteger unblindMessage( + BigInteger blindedMsg) + { + BigInteger m = key.getModulus(); + BigInteger msg = blindedMsg; + BigInteger blindFactorInverse = blindingFactor.modInverse(m); + msg = msg.multiply(blindFactorInverse); + msg = msg.mod(m); + + return msg; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java new file mode 100644 index 000000000..4a98bbdad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java @@ -0,0 +1,203 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; + +import java.math.BigInteger; + +/** + * this does your basic RSA algorithm. + */ +class RSACoreEngine +{ + private RSAKeyParameters key; + private boolean forEncryption; + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void init( + boolean forEncryption, + CipherParameters param) + { + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + key = (RSAKeyParameters)rParam.getParameters(); + } + else + { + key = (RSAKeyParameters)param; + } + + this.forEncryption = forEncryption; + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + int bitSize = key.getModulus().bitLength(); + + if (forEncryption) + { + return (bitSize + 7) / 8 - 1; + } + else + { + return (bitSize + 7) / 8; + } + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int getOutputBlockSize() + { + int bitSize = key.getModulus().bitLength(); + + if (forEncryption) + { + return (bitSize + 7) / 8; + } + else + { + return (bitSize + 7) / 8 - 1; + } + } + + public BigInteger convertInput( + byte[] in, + int inOff, + int inLen) + { + if (inLen > (getInputBlockSize() + 1)) + { + throw new DataLengthException("input too large for RSA cipher."); + } + else if (inLen == (getInputBlockSize() + 1) && !forEncryption) + { + throw new DataLengthException("input too large for RSA cipher."); + } + + byte[] block; + + if (inOff != 0 || inLen != in.length) + { + block = new byte[inLen]; + + System.arraycopy(in, inOff, block, 0, inLen); + } + else + { + block = in; + } + + BigInteger res = new BigInteger(1, block); + if (res.compareTo(key.getModulus()) >= 0) + { + throw new DataLengthException("input too large for RSA cipher."); + } + + return res; + } + + public byte[] convertOutput( + BigInteger result) + { + byte[] output = result.toByteArray(); + + if (forEncryption) + { + if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down. + { + byte[] tmp = new byte[output.length - 1]; + + System.arraycopy(output, 1, tmp, 0, tmp.length); + + return tmp; + } + + if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen + { + byte[] tmp = new byte[getOutputBlockSize()]; + + System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length); + + return tmp; + } + } + else + { + if (output[0] == 0) // have ended up with an extra zero byte, copy down. + { + byte[] tmp = new byte[output.length - 1]; + + System.arraycopy(output, 1, tmp, 0, tmp.length); + + return tmp; + } + } + + return output; + } + + public BigInteger processBlock(BigInteger input) + { + if (key instanceof RSAPrivateCrtKeyParameters) + { + // + // we have the extra factors, use the Chinese Remainder Theorem - the author + // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for + // advice regarding the expression of this. + // + RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key; + + BigInteger p = crtKey.getP(); + BigInteger q = crtKey.getQ(); + BigInteger dP = crtKey.getDP(); + BigInteger dQ = crtKey.getDQ(); + BigInteger qInv = crtKey.getQInv(); + + BigInteger mP, mQ, h, m; + + // mP = ((input mod p) ^ dP)) mod p + mP = (input.remainder(p)).modPow(dP, p); + + // mQ = ((input mod q) ^ dQ)) mod q + mQ = (input.remainder(q)).modPow(dQ, q); + + // h = qInv * (mP - mQ) mod p + h = mP.subtract(mQ); + h = h.multiply(qInv); + h = h.mod(p); // mod (in Java) returns the positive residual + + // m = h * q + mQ + m = h.multiply(q); + m = m.add(mQ); + + return m; + } + else + { + return input.modPow( + key.getExponent(), key.getModulus()); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java new file mode 100644 index 000000000..5ffe2ace3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java @@ -0,0 +1,78 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; + +/** + * this does your basic RSA algorithm. + */ +public class RSAEngine + implements AsymmetricBlockCipher +{ + private RSACoreEngine core; + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void init( + boolean forEncryption, + CipherParameters param) + { + if (core == null) + { + core = new RSACoreEngine(); + } + + core.init(forEncryption, param); + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + return core.getInputBlockSize(); + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int getOutputBlockSize() + { + return core.getOutputBlockSize(); + } + + /** + * Process a single block using the basic RSA algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @exception DataLengthException the input block is too large. + */ + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + { + if (core == null) + { + throw new IllegalStateException("RSA engine not initialised"); + } + + return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen))); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java new file mode 100644 index 000000000..b38c71149 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java @@ -0,0 +1,725 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * an implementation of Rijndael, based on the documentation and reference implementation + * by Paulo Barreto, Vincent Rijmen, for v2.0 August '99. + *
+ * Note: this implementation is based on information prior to final NIST publication. + */ +public class RijndaelEngine + implements BlockCipher +{ + private static final int MAXROUNDS = 14; + + private static final int MAXKC = (256/4); + + private static final byte[] logtable = { + (byte)0, (byte)0, (byte)25, (byte)1, (byte)50, (byte)2, (byte)26, (byte)198, + (byte)75, (byte)199, (byte)27, (byte)104, (byte)51, (byte)238, (byte)223, (byte)3, + (byte)100, (byte)4, (byte)224, (byte)14, (byte)52, (byte)141, (byte)129, (byte)239, + (byte)76, (byte)113, (byte)8, (byte)200, (byte)248, (byte)105, (byte)28, (byte)193, + (byte)125, (byte)194, (byte)29, (byte)181, (byte)249, (byte)185, (byte)39, (byte)106, + (byte)77, (byte)228, (byte)166, (byte)114, (byte)154, (byte)201, (byte)9, (byte)120, + (byte)101, (byte)47, (byte)138, (byte)5, (byte)33, (byte)15, (byte)225, (byte)36, + (byte)18, (byte)240, (byte)130, (byte)69, (byte)53, (byte)147, (byte)218, (byte)142, + (byte)150, (byte)143, (byte)219, (byte)189, (byte)54, (byte)208, (byte)206, (byte)148, + (byte)19, (byte)92, (byte)210, (byte)241, (byte)64, (byte)70, (byte)131, (byte)56, + (byte)102, (byte)221, (byte)253, (byte)48, (byte)191, (byte)6, (byte)139, (byte)98, + (byte)179, (byte)37, (byte)226, (byte)152, (byte)34, (byte)136, (byte)145, (byte)16, + (byte)126, (byte)110, (byte)72, (byte)195, (byte)163, (byte)182, (byte)30, (byte)66, + (byte)58, (byte)107, (byte)40, (byte)84, (byte)250, (byte)133, (byte)61, (byte)186, + (byte)43, (byte)121, (byte)10, (byte)21, (byte)155, (byte)159, (byte)94, (byte)202, + (byte)78, (byte)212, (byte)172, (byte)229, (byte)243, (byte)115, (byte)167, (byte)87, + (byte)175, (byte)88, (byte)168, (byte)80, (byte)244, (byte)234, (byte)214, (byte)116, + (byte)79, (byte)174, (byte)233, (byte)213, (byte)231, (byte)230, (byte)173, (byte)232, + (byte)44, (byte)215, (byte)117, (byte)122, (byte)235, (byte)22, (byte)11, (byte)245, + (byte)89, (byte)203, (byte)95, (byte)176, (byte)156, (byte)169, (byte)81, (byte)160, + (byte)127, (byte)12, (byte)246, (byte)111, (byte)23, (byte)196, (byte)73, (byte)236, + (byte)216, (byte)67, (byte)31, (byte)45, (byte)164, (byte)118, (byte)123, (byte)183, + (byte)204, (byte)187, (byte)62, (byte)90, (byte)251, (byte)96, (byte)177, (byte)134, + (byte)59, (byte)82, (byte)161, (byte)108, (byte)170, (byte)85, (byte)41, (byte)157, + (byte)151, (byte)178, (byte)135, (byte)144, (byte)97, (byte)190, (byte)220, (byte)252, + (byte)188, (byte)149, (byte)207, (byte)205, (byte)55, (byte)63, (byte)91, (byte)209, + (byte)83, (byte)57, (byte)132, (byte)60, (byte)65, (byte)162, (byte)109, (byte)71, + (byte)20, (byte)42, (byte)158, (byte)93, (byte)86, (byte)242, (byte)211, (byte)171, + (byte)68, (byte)17, (byte)146, (byte)217, (byte)35, (byte)32, (byte)46, (byte)137, + (byte)180, (byte)124, (byte)184, (byte)38, (byte)119, (byte)153, (byte)227, (byte)165, + (byte)103, (byte)74, (byte)237, (byte)222, (byte)197, (byte)49, (byte)254, (byte)24, + (byte)13, (byte)99, (byte)140, (byte)128, (byte)192, (byte)247, (byte)112, (byte)7 + }; + + private static final byte[] aLogtable = { + (byte)0, (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53, + (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170, + (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49, + (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205, + (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136, + (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154, + (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163, + (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160, + (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65, + (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117, + (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128, + (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84, + (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202, + (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14, + (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23, + (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1, + (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53, + (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170, + (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49, + (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205, + (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136, + (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154, + (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163, + (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160, + (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65, + (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117, + (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128, + (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84, + (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202, + (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14, + (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23, + (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1, + }; + + private static final byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + private static final byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + private static final int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + static byte[][] shifts0 = + { + { 0, 8, 16, 24 }, + { 0, 8, 16, 24 }, + { 0, 8, 16, 24 }, + { 0, 8, 16, 32 }, + { 0, 8, 24, 32 } + }; + + static byte[][] shifts1 = + { + { 0, 24, 16, 8 }, + { 0, 32, 24, 16 }, + { 0, 40, 32, 24 }, + { 0, 48, 40, 24 }, + { 0, 56, 40, 32 } + }; + + /** + * multiply two elements of GF(2^m) + * needed for MixColumn and InvMixColumn + */ + private byte mul0x2( + int b) + { + if (b != 0) + { + return aLogtable[25 + (logtable[b] & 0xff)]; + } + else + { + return 0; + } + } + + private byte mul0x3( + int b) + { + if (b != 0) + { + return aLogtable[1 + (logtable[b] & 0xff)]; + } + else + { + return 0; + } + } + + private byte mul0x9( + int b) + { + if (b >= 0) + { + return aLogtable[199 + b]; + } + else + { + return 0; + } + } + + private byte mul0xb( + int b) + { + if (b >= 0) + { + return aLogtable[104 + b]; + } + else + { + return 0; + } + } + + private byte mul0xd( + int b) + { + if (b >= 0) + { + return aLogtable[238 + b]; + } + else + { + return 0; + } + } + + private byte mul0xe( + int b) + { + if (b >= 0) + { + return aLogtable[223 + b]; + } + else + { + return 0; + } + } + + /** + * xor corresponding text input and round key input bytes + */ + private void KeyAddition( + long[] rk) + { + A0 ^= rk[0]; + A1 ^= rk[1]; + A2 ^= rk[2]; + A3 ^= rk[3]; + } + + private long shift( + long r, + int shift) + { + return (((r >>> shift) | (r << (BC - shift)))) & BC_MASK; + } + + /** + * Row 0 remains unchanged + * The other three rows are shifted a variable amount + */ + private void ShiftRow( + byte[] shiftsSC) + { + A1 = shift(A1, shiftsSC[1]); + A2 = shift(A2, shiftsSC[2]); + A3 = shift(A3, shiftsSC[3]); + } + + private long applyS( + long r, + byte[] box) + { + long res = 0; + + for (int j = 0; j < BC; j += 8) + { + res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j; + } + + return res; + } + + /** + * Replace every byte of the input by the byte at that place + * in the nonlinear S-box + */ + private void Substitution( + byte[] box) + { + A0 = applyS(A0, box); + A1 = applyS(A1, box); + A2 = applyS(A2, box); + A3 = applyS(A3, box); + } + + /** + * Mix the bytes of every column in a linear way + */ + private void MixColumn() + { + long r0, r1, r2, r3; + + r0 = r1 = r2 = r3 = 0; + + for (int j = 0; j < BC; j += 8) + { + int a0 = (int)((A0 >> j) & 0xff); + int a1 = (int)((A1 >> j) & 0xff); + int a2 = (int)((A2 >> j) & 0xff); + int a3 = (int)((A3 >> j) & 0xff); + + r0 |= (long)((mul0x2(a0) ^ mul0x3(a1) ^ a2 ^ a3) & 0xff) << j; + + r1 |= (long)((mul0x2(a1) ^ mul0x3(a2) ^ a3 ^ a0) & 0xff) << j; + + r2 |= (long)((mul0x2(a2) ^ mul0x3(a3) ^ a0 ^ a1) & 0xff) << j; + + r3 |= (long)((mul0x2(a3) ^ mul0x3(a0) ^ a1 ^ a2) & 0xff) << j; + } + + A0 = r0; + A1 = r1; + A2 = r2; + A3 = r3; + } + + /** + * Mix the bytes of every column in a linear way + * This is the opposite operation of Mixcolumn + */ + private void InvMixColumn() + { + long r0, r1, r2, r3; + + r0 = r1 = r2 = r3 = 0; + for (int j = 0; j < BC; j += 8) + { + int a0 = (int)((A0 >> j) & 0xff); + int a1 = (int)((A1 >> j) & 0xff); + int a2 = (int)((A2 >> j) & 0xff); + int a3 = (int)((A3 >> j) & 0xff); + + // + // pre-lookup the log table + // + a0 = (a0 != 0) ? (logtable[a0 & 0xff] & 0xff) : -1; + a1 = (a1 != 0) ? (logtable[a1 & 0xff] & 0xff) : -1; + a2 = (a2 != 0) ? (logtable[a2 & 0xff] & 0xff) : -1; + a3 = (a3 != 0) ? (logtable[a3 & 0xff] & 0xff) : -1; + + r0 |= (long)((mul0xe(a0) ^ mul0xb(a1) ^ mul0xd(a2) ^ mul0x9(a3)) & 0xff) << j; + + r1 |= (long)((mul0xe(a1) ^ mul0xb(a2) ^ mul0xd(a3) ^ mul0x9(a0)) & 0xff) << j; + + r2 |= (long)((mul0xe(a2) ^ mul0xb(a3) ^ mul0xd(a0) ^ mul0x9(a1)) & 0xff) << j; + + r3 |= (long)((mul0xe(a3) ^ mul0xb(a0) ^ mul0xd(a1) ^ mul0x9(a2)) & 0xff) << j; + } + + A0 = r0; + A1 = r1; + A2 = r2; + A3 = r3; + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on keyBits and blockBits + */ + private long[][] generateWorkingKey( + byte[] key) + { + int KC; + int t, rconpointer = 0; + int keyBits = key.length * 8; + byte[][] tk = new byte[4][MAXKC]; + long[][] W = new long[MAXROUNDS+1][4]; + + switch (keyBits) + { + case 128: + KC = 4; + break; + case 160: + KC = 5; + break; + case 192: + KC = 6; + break; + case 224: + KC = 7; + break; + case 256: + KC = 8; + break; + default : + throw new IllegalArgumentException("Key length not 128/160/192/224/256 bits."); + } + + if (keyBits >= blockBits) + { + ROUNDS = KC + 6; + } + else + { + ROUNDS = (BC / 8) + 6; + } + + // + // copy the key into the processing area + // + int index = 0; + + for (int i = 0; i < key.length; i++) + { + tk[i % 4][i / 4] = key[index++]; + } + + t = 0; + + // + // copy values into round key array + // + for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC / 8)); j++, t++) + { + for (int i = 0; i < 4; i++) + { + W[t / (BC / 8)][i] |= (long)(tk[i][j] & 0xff) << ((t * 8) % BC); + } + } + + // + // while not enough round key material calculated + // calculate new values + // + while (t < (ROUNDS+1)*(BC/8)) + { + for (int i = 0; i < 4; i++) + { + tk[i][0] ^= S[tk[(i+1)%4][KC-1] & 0xff]; + } + tk[0][0] ^= rcon[rconpointer++]; + + if (KC <= 6) + { + for (int j = 1; j < KC; j++) + { + for (int i = 0; i < 4; i++) + { + tk[i][j] ^= tk[i][j-1]; + } + } + } + else + { + for (int j = 1; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + tk[i][j] ^= tk[i][j-1]; + } + } + for (int i = 0; i < 4; i++) + { + tk[i][4] ^= S[tk[i][3] & 0xff]; + } + for (int j = 5; j < KC; j++) + { + for (int i = 0; i < 4; i++) + { + tk[i][j] ^= tk[i][j-1]; + } + } + } + + // + // copy values into round key array + // + for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC/8)); j++, t++) + { + for (int i = 0; i < 4; i++) + { + W[t / (BC/8)][i] |= (long)(tk[i][j] & 0xff) << ((t * 8) % (BC)); + } + } + } + + return W; + } + + private int BC; + private long BC_MASK; + private int ROUNDS; + private int blockBits; + private long[][] workingKey; + private long A0, A1, A2, A3; + private boolean forEncryption; + private byte[] shifts0SC; + private byte[] shifts1SC; + + /** + * default constructor - 128 bit block size. + */ + public RijndaelEngine() + { + this(128); + } + + /** + * basic constructor - set the cipher up for a given blocksize + * + * @param blockBits the blocksize in bits, must be 128, 192, or 256. + */ + public RijndaelEngine( + int blockBits) + { + switch (blockBits) + { + case 128: + BC = 32; + BC_MASK = 0xffffffffL; + shifts0SC = shifts0[0]; + shifts1SC = shifts1[0]; + break; + case 160: + BC = 40; + BC_MASK = 0xffffffffffL; + shifts0SC = shifts0[1]; + shifts1SC = shifts1[1]; + break; + case 192: + BC = 48; + BC_MASK = 0xffffffffffffL; + shifts0SC = shifts0[2]; + shifts1SC = shifts1[2]; + break; + case 224: + BC = 56; + BC_MASK = 0xffffffffffffffL; + shifts0SC = shifts0[3]; + shifts1SC = shifts1[3]; + break; + case 256: + BC = 64; + BC_MASK = 0xffffffffffffffffL; + shifts0SC = shifts0[4]; + shifts1SC = shifts1[4]; + break; + default: + throw new IllegalArgumentException("unknown blocksize to Rijndael"); + } + + this.blockBits = blockBits; + } + + /** + * initialise a Rijndael cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + workingKey = generateWorkingKey(((KeyParameter)params).getKey()); + this.forEncryption = forEncryption; + return; + } + + throw new IllegalArgumentException("invalid parameter passed to Rijndael init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "Rijndael"; + } + + public int getBlockSize() + { + return BC / 2; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("Rijndael engine not initialised"); + } + + if ((inOff + (BC / 2)) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (BC / 2)) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (forEncryption) + { + unpackBlock(in, inOff); + encryptBlock(workingKey); + packBlock(out, outOff); + } + else + { + unpackBlock(in, inOff); + decryptBlock(workingKey); + packBlock(out, outOff); + } + + return BC / 2; + } + + public void reset() + { + } + + private void unpackBlock( + byte[] bytes, + int off) + { + int index = off; + + A0 = (bytes[index++] & 0xff); + A1 = (bytes[index++] & 0xff); + A2 = (bytes[index++] & 0xff); + A3 = (bytes[index++] & 0xff); + + for (int j = 8; j != BC; j += 8) + { + A0 |= (long)(bytes[index++] & 0xff) << j; + A1 |= (long)(bytes[index++] & 0xff) << j; + A2 |= (long)(bytes[index++] & 0xff) << j; + A3 |= (long)(bytes[index++] & 0xff) << j; + } + } + + private void packBlock( + byte[] bytes, + int off) + { + int index = off; + + for (int j = 0; j != BC; j += 8) + { + bytes[index++] = (byte)(A0 >> j); + bytes[index++] = (byte)(A1 >> j); + bytes[index++] = (byte)(A2 >> j); + bytes[index++] = (byte)(A3 >> j); + } + } + + private void encryptBlock( + long[][] rk) + { + int r; + + // + // begin with a key addition + // + KeyAddition(rk[0]); + + // + // ROUNDS-1 ordinary rounds + // + for (r = 1; r < ROUNDS; r++) + { + Substitution(S); + ShiftRow(shifts0SC); + MixColumn(); + KeyAddition(rk[r]); + } + + // + // Last round is special: there is no MixColumn + // + Substitution(S); + ShiftRow(shifts0SC); + KeyAddition(rk[ROUNDS]); + } + + private void decryptBlock( + long[][] rk) + { + int r; + + // To decrypt: apply the inverse operations of the encrypt routine, + // in opposite order + // + // (KeyAddition is an involution: it 's equal to its inverse) + // (the inverse of Substitution with table S is Substitution with the inverse table of S) + // (the inverse of Shiftrow is Shiftrow over a suitable distance) + // + + // First the special round: + // without InvMixColumn + // with extra KeyAddition + // + KeyAddition(rk[ROUNDS]); + Substitution(Si); + ShiftRow(shifts1SC); + + // + // ROUNDS-1 ordinary rounds + // + for (r = ROUNDS-1; r > 0; r--) + { + KeyAddition(rk[r]); + InvMixColumn(); + Substitution(Si); + ShiftRow(shifts1SC); + } + + // + // End with the extra key addition + // + KeyAddition(rk[0]); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java new file mode 100644 index 000000000..1a376c914 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java @@ -0,0 +1,346 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * Implementation of the SEED algorithm as described in RFC 4009 + */ +public class SEEDEngine + implements BlockCipher +{ + private final int BLOCK_SIZE = 16; + + private static final int[] SS0 = + { + 0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0, 0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124, + 0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c, 0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360, + 0x28082028, 0x04444044, 0x20002020, 0x1d8d919c, 0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314, + 0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378, 0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec, + 0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8, 0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074, + 0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354, 0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100, + 0x24042024, 0x1c0c101c, 0x33437370, 0x18889098, 0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8, + 0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380, 0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8, + 0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8, 0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c, + 0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078, 0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4, + 0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140, 0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008, + 0x1f0f131c, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0, + 0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324, 0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8, + 0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c, 0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208, + 0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4, 0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064, + 0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218, 0x06060204, 0x21012120, 0x2b4b6368, 0x26466264, + 0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288, 0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0, + 0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4, 0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc, + 0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac, 0x36063234, 0x15051114, 0x22022220, 0x38083038, + 0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c, 0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394, + 0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c, 0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188, + 0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8, 0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4, + 0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364, 0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8, + 0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320, 0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4, + 0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0, 0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040, + 0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0, 0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154, + 0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c, 0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254, + 0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244, 0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8, + 0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c, 0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0, + 0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c, 0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088, + 0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4, 0x22426260, 0x29092128, 0x07070304, 0x33033330, + 0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178, 0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298 + }; + + private static final int[] SS1 = + { + + 0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2, 0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0, + 0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3, 0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53, + 0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1, 0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3, + 0xd013c3d3, 0x90118191, 0x10110111, 0x04060602, 0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43, + 0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0, 0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0, + 0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2, 0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890, + 0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32, 0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3, + 0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72, 0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272, + 0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0, 0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83, + 0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13, 0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430, + 0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1, 0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0, + 0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1, 0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1, + 0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131, 0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1, + 0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171, + 0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991, 0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951, + 0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0, 0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0, + 0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12, 0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3, + 0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2, 0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41, + 0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32, 0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62, + 0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292, 0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0, + 0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571, 0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303, + 0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470, 0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901, + 0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040, 0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501, + 0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22, 0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343, + 0x84058581, 0x14140410, 0x88098981, 0x981b8b93, 0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971, + 0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282, 0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53, + 0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11, 0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642, + 0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3, 0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1, + 0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30, 0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70, + 0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393, + 0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0, 0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783, + 0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83, 0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3 + }; + + private static final int[] SS2 = + { + + 0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3, 0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505, + 0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e, 0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343, + 0x20282808, 0x40440444, 0x20202000, 0x919c1d8d, 0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707, + 0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b, 0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece, + 0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888, 0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444, + 0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747, 0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101, + 0x20242404, 0x101c1c0c, 0x73703343, 0x90981888, 0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9, + 0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383, 0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9, + 0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb, 0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f, + 0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848, 0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5, + 0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141, 0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808, + 0x131c1f0f, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1, + 0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707, 0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b, + 0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d, 0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a, + 0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5, 0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444, + 0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a, 0x02040606, 0x21202101, 0x63682b4b, 0x62642646, + 0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a, 0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0, + 0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5, 0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf, + 0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e, 0x32343606, 0x11141505, 0x22202202, 0x30383808, + 0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c, 0x81800181, 0xe1e829c9, 0x80840484, 0x93941787, + 0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c, 0x71703141, 0x11101101, 0xc3c407c7, 0x81880989, + 0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8, 0x90941484, 0x51581949, 0x82800282, 0xc0c404c4, + 0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747, 0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888, + 0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303, 0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484, + 0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2, 0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040, + 0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1, 0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545, + 0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f, 0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646, + 0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646, 0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca, + 0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f, 0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282, + 0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f, 0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888, + 0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4, 0x62602242, 0x21282909, 0x03040707, 0x33303303, + 0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949, 0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a + }; + + + private static final int[] SS3 = + { + + 0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426, 0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838, + 0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407, 0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b, + 0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435, 0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427, + 0xc3d3d013, 0x81919011, 0x01111011, 0x06020406, 0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b, + 0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828, 0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434, + 0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416, 0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818, + 0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e, 0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f, + 0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a, 0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032, + 0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000, 0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b, + 0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f, 0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434, + 0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829, 0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838, + 0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405, 0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839, + 0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031, 0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031, + 0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031, + 0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819, 0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819, + 0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c, 0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010, + 0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a, 0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f, + 0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022, 0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d, + 0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a, 0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e, + 0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012, 0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c, + 0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435, 0x4f737c3f, 0x05313435, 0x00101010, 0x03030003, + 0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434, 0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809, + 0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000, 0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405, + 0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a, 0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003, + 0x85818405, 0x04101414, 0x89818809, 0x8b93981b, 0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839, + 0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002, 0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f, + 0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d, 0x05212425, 0x4f434c0f, 0x00000000, 0x46424406, + 0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b, 0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d, + 0x00303030, 0x85919415, 0x45616425, 0x0c303c3c, 0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c, + 0x0e020c0e, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013, + 0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424, 0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407, + 0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f, 0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437 + }; + + + private static final int[] KC = + { + 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc, + 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf, + 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1, + 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b + }; + + private int[] wKey; + private boolean forEncryption; + + public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException + { + this.forEncryption = forEncryption; + wKey = createWorkingKey(((KeyParameter)params).getKey()); + } + + public String getAlgorithmName() + { + return "SEED"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException + { + if (wKey == null) + { + throw new IllegalStateException("SEED engine 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"); + } + + long l = bytesToLong(in, inOff + 0); + long r = bytesToLong(in, inOff + 8); + + if (forEncryption) + { + for (int i = 0; i < 16; i++) + { + long nl = r; + + r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r); + l = nl; + } + } + else + { + for (int i = 15; i >= 0; i--) + { + long nl = r; + + r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r); + l = nl; + } + } + + longToBytes(out, outOff + 0, r); + longToBytes(out, outOff + 8, l); + + return BLOCK_SIZE; + } + + public void reset() + { + } + + private int[] createWorkingKey(byte[] inKey) + { + int[] key = new int[32]; + long lower = bytesToLong(inKey, 0); + long upper = bytesToLong(inKey, 8); + + int key0 = extractW0(lower); + int key1 = extractW1(lower); + int key2 = extractW0(upper); + int key3 = extractW1(upper); + + for (int i = 0; i < 16; i++) + { + key[2 * i] = G(key0 + key2 - KC[i]); + key[2 * i + 1] = G(key1 - key3 + KC[i]); + + if (i % 2 == 0) + { + lower = rotateRight8(lower); + key0 = extractW0(lower); + key1 = extractW1(lower); + } + else + { + upper = rotateLeft8(upper); + key2 = extractW0(upper); + key3 = extractW1(upper); + } + } + + return key; + } + + private int extractW1(long lVal) + { + return (int)lVal; + } + + private int extractW0(long lVal) + { + return (int)(lVal >> 32); + } + + private long rotateLeft8(long x) + { + return (x << 8) | (x >>> 56); + } + + private long rotateRight8(long x) + { + return (x >>> 8) | (x << 56); + } + + private long bytesToLong( + byte[] src, + int srcOff) + { + long word = 0; + + for (int i = 0; i <= 7; i++) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void longToBytes( + byte[] dest, + int destOff, + long value) + { + for (int i = 0; i < 8; i++) + { + dest[i + destOff] = (byte)(value >> ((7 - i) * 8)); + } + } + + private int G(int x) + { + return SS0[x & 0xff] ^ SS1[(x >> 8) & 0xff] ^ SS2[(x >> 16) & 0xff] ^ SS3[(x >> 24) & 0xff]; + } + + private long F(int ki0, int ki1, long r) + { + int r0 = (int)(r >> 32); + int r1 = (int)r; + int rd1 = phaseCalc2(r0, ki0, r1, ki1); + int rd0 = rd1 + phaseCalc1(r0, ki0, r1, ki1); + + return ((long)rd0 << 32) | (rd1 & 0xffffffffL); + } + + private int phaseCalc1(int r0, int ki0, int r1, int ki1) + { + return G(G((r0 ^ ki0) ^ (r1 ^ ki1)) + (r0 ^ ki0)); + } + + private int phaseCalc2(int r0, int ki0, int r1, int ki1) + { + return G(phaseCalc1(r0, ki0, r1, ki1) + G((r0 ^ ki0) ^ (r1 ^ ki1))); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java new file mode 100644 index 000000000..2b5213c2e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.engines; + +/** + * An implementation of the SEED key wrapper based on RFC 4010/RFC 3394. + *
+ * For further details see: http://www.ietf.org/rfc/rfc4010.txt. + */ +public class SEEDWrapEngine + extends RFC3394WrapEngine +{ + public SEEDWrapEngine() + { + super(new SEEDEngine()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java new file mode 100644 index 000000000..0036e1c23 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java @@ -0,0 +1,392 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.MaxBytesExceededException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.util.Pack; +import org.spongycastle.util.Strings; + +/** + * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + */ +public class Salsa20Engine + implements StreamCipher +{ + public final static int DEFAULT_ROUNDS = 20; + + /** Constants */ + private final static int STATE_SIZE = 16; // 16, 32 bit ints = 64 bytes + + protected final static byte[] + sigma = Strings.toByteArray("expand 32-byte k"), + tau = Strings.toByteArray("expand 16-byte k"); + + protected int rounds; + + /* + * variables to hold the state of the engine + * during encryption and decryption + */ + private int index = 0; + protected int[] engineState = new int[STATE_SIZE]; // state + protected int[] x = new int[STATE_SIZE] ; // internal buffer + private byte[] keyStream = new byte[STATE_SIZE * 4]; // expanded state, 64 bytes + private boolean initialised = false; + + /* + * internal counter + */ + private int cW0, cW1, cW2; + + /** + * Creates a 20 round Salsa20 engine. + */ + public Salsa20Engine() + { + this(DEFAULT_ROUNDS); + } + + /** + * Creates a Salsa20 engine with a specific number of rounds. + * @param rounds the number of rounds (must be an even number). + */ + public Salsa20Engine(int rounds) + { + if (rounds <= 0 || (rounds & 1) != 0) + { + throw new IllegalArgumentException("'rounds' must be a positive, even number"); + } + + this.rounds = rounds; + } + + /** + * initialise a Salsa20 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + /* + * Salsa20 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. (Like 90% of stream ciphers) + */ + + if (!(params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include an IV"); + } + + ParametersWithIV ivParams = (ParametersWithIV) params; + + byte[] iv = ivParams.getIV(); + if (iv == null || iv.length != getNonceSize()) + { + throw new IllegalArgumentException(getAlgorithmName() + " requires exactly " + getNonceSize() + + " bytes of IV"); + } + + if (!(ivParams.getParameters() instanceof KeyParameter)) + { + throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include a key"); + } + + KeyParameter key = (KeyParameter) ivParams.getParameters(); + + setKey(key.getKey(), iv); + reset(); + initialised = true; + } + + protected int getNonceSize() + { + return 8; + } + + public String getAlgorithmName() + { + String name = "Salsa20"; + if (rounds != DEFAULT_ROUNDS) + { + name += "/" + rounds; + } + return name; + } + + public byte returnByte(byte in) + { + if (limitExceeded()) + { + throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV"); + } + + if (index == 0) + { + generateKeyStream(keyStream); + advanceCounter(); + } + + byte out = (byte)(keyStream[index]^in); + index = (index + 1) & 63; + + return out; + } + + protected void advanceCounter() + { + if (++engineState[8] == 0) + { + ++engineState[9]; + } + } + + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName() + " not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (limitExceeded(len)) + { + throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV"); + } + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + generateKeyStream(keyStream); + advanceCounter(); + } + + out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]); + index = (index + 1) & 63; + } + } + + public void reset() + { + index = 0; + resetLimitCounter(); + resetCounter(); + } + + protected void resetCounter() + { + engineState[8] = engineState[9] = 0; + } + + protected void setKey(byte[] keyBytes, byte[] ivBytes) + { + if ((keyBytes.length != 16) && (keyBytes.length != 32)) { + throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key"); + } + + int offset = 0; + byte[] constants; + + // Key + engineState[1] = Pack.littleEndianToInt(keyBytes, 0); + engineState[2] = Pack.littleEndianToInt(keyBytes, 4); + engineState[3] = Pack.littleEndianToInt(keyBytes, 8); + engineState[4] = Pack.littleEndianToInt(keyBytes, 12); + + if (keyBytes.length == 32) + { + constants = sigma; + offset = 16; + } + else + { + constants = tau; + } + + engineState[11] = Pack.littleEndianToInt(keyBytes, offset); + engineState[12] = Pack.littleEndianToInt(keyBytes, offset+4); + engineState[13] = Pack.littleEndianToInt(keyBytes, offset+8); + engineState[14] = Pack.littleEndianToInt(keyBytes, offset+12); + + engineState[0 ] = Pack.littleEndianToInt(constants, 0); + engineState[5 ] = Pack.littleEndianToInt(constants, 4); + engineState[10] = Pack.littleEndianToInt(constants, 8); + engineState[15] = Pack.littleEndianToInt(constants, 12); + + // IV + engineState[6] = Pack.littleEndianToInt(ivBytes, 0); + engineState[7] = Pack.littleEndianToInt(ivBytes, 4); + resetCounter(); + } + + protected void generateKeyStream(byte[] output) + { + salsaCore(rounds, engineState, x); + Pack.intToLittleEndian(x, output, 0); + } + + /** + * Salsa20 function + * + * @param input input data + * + * @return keystream + */ + public static void salsaCore(int rounds, int[] input, int[] x) + { + if (input.length != 16) { + throw new IllegalArgumentException(); + } + if (x.length != 16) { + throw new IllegalArgumentException(); + } + if (rounds % 2 != 0) { + throw new IllegalArgumentException("Number of rounds must be even"); + } + + int x00 = input[ 0]; + int x01 = input[ 1]; + int x02 = input[ 2]; + int x03 = input[ 3]; + int x04 = input[ 4]; + int x05 = input[ 5]; + int x06 = input[ 6]; + int x07 = input[ 7]; + int x08 = input[ 8]; + int x09 = input[ 9]; + int x10 = input[10]; + int x11 = input[11]; + int x12 = input[12]; + int x13 = input[13]; + int x14 = input[14]; + int x15 = input[15]; + + for (int i = rounds; i > 0; i -= 2) + { + x04 ^= rotl((x00+x12), 7); + x08 ^= rotl((x04+x00), 9); + x12 ^= rotl((x08+x04),13); + x00 ^= rotl((x12+x08),18); + x09 ^= rotl((x05+x01), 7); + x13 ^= rotl((x09+x05), 9); + x01 ^= rotl((x13+x09),13); + x05 ^= rotl((x01+x13),18); + x14 ^= rotl((x10+x06), 7); + x02 ^= rotl((x14+x10), 9); + x06 ^= rotl((x02+x14),13); + x10 ^= rotl((x06+x02),18); + x03 ^= rotl((x15+x11), 7); + x07 ^= rotl((x03+x15), 9); + x11 ^= rotl((x07+x03),13); + x15 ^= rotl((x11+x07),18); + + x01 ^= rotl((x00+x03), 7); + x02 ^= rotl((x01+x00), 9); + x03 ^= rotl((x02+x01),13); + x00 ^= rotl((x03+x02),18); + x06 ^= rotl((x05+x04), 7); + x07 ^= rotl((x06+x05), 9); + x04 ^= rotl((x07+x06),13); + x05 ^= rotl((x04+x07),18); + x11 ^= rotl((x10+x09), 7); + x08 ^= rotl((x11+x10), 9); + x09 ^= rotl((x08+x11),13); + x10 ^= rotl((x09+x08),18); + x12 ^= rotl((x15+x14), 7); + x13 ^= rotl((x12+x15), 9); + x14 ^= rotl((x13+x12),13); + x15 ^= rotl((x14+x13),18); + } + + x[ 0] = x00 + input[ 0]; + x[ 1] = x01 + input[ 1]; + x[ 2] = x02 + input[ 2]; + x[ 3] = x03 + input[ 3]; + x[ 4] = x04 + input[ 4]; + x[ 5] = x05 + input[ 5]; + x[ 6] = x06 + input[ 6]; + x[ 7] = x07 + input[ 7]; + x[ 8] = x08 + input[ 8]; + x[ 9] = x09 + input[ 9]; + x[10] = x10 + input[10]; + x[11] = x11 + input[11]; + x[12] = x12 + input[12]; + x[13] = x13 + input[13]; + x[14] = x14 + input[14]; + x[15] = x15 + input[15]; + } + + /** + * Rotate left + * + * @param x value to rotate + * @param y amount to rotate x + * + * @return rotated x + */ + protected static int rotl(int x, int y) + { + return (x << y) | (x >>> -y); + } + + private void resetLimitCounter() + { + cW0 = 0; + cW1 = 0; + cW2 = 0; + } + + private boolean limitExceeded() + { + if (++cW0 == 0) + { + if (++cW1 == 0) + { + return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + + return false; + } + + /* + * this relies on the fact len will always be positive. + */ + private boolean limitExceeded(int len) + { + cW0 += len; + if (cW0 < len && cW0 >= 0) + { + if (++cW1 == 0) + { + return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + + return false; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java new file mode 100644 index 000000000..e96203b6b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java @@ -0,0 +1,783 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * Serpent 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. + *
+ * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a + * candidate algorithm for the NIST AES Quest.> + *
+ * For full details see the The Serpent home page + */ +public class SerpentEngine + implements BlockCipher +{ + private static final int BLOCK_SIZE = 16; + + static final int ROUNDS = 32; + static final int PHI = 0x9E3779B9; // (sqrt(5) - 1) * 2**31 + + private boolean encrypting; + private int[] wKey; + + private int X0, X1, X2, X3; // registers + + /** + * initialise a Serpent cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception 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 Serpent 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. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public final int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (wKey == null) + { + throw new IllegalStateException("Serpent 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() + { + } + + /** + * Expand a user-supplied key material into a session key. + * + * @param key The user-key bytes (multiples of 4) to use. + * @exception IllegalArgumentException + */ + private 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++] = bytesToWord(key, off); + } + + if (off == 0) + { + kPad[length++] = bytesToWord(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; + } + + private int rotateLeft( + int x, + int bits) + { + return (x << bits) | (x >>> -bits); + } + + private int rotateRight( + int x, + int bits) + { + return (x >>> bits) | (x << -bits); + } + + private int bytesToWord( + byte[] src, + int srcOff) + { + return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | + ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff))); + } + + private void wordToBytes( + int word, + byte[] dst, + int dstOff) + { + dst[dstOff + 3] = (byte)(word); + dst[dstOff + 2] = (byte)(word >>> 8); + dst[dstOff + 1] = (byte)(word >>> 16); + dst[dstOff] = (byte)(word >>> 24); + } + + /** + * Encrypt one block of plaintext. + * + * @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. + */ + private void encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + X3 = bytesToWord(in, inOff); + X2 = bytesToWord(in, inOff + 4); + X1 = bytesToWord(in, inOff + 8); + X0 = bytesToWord(in, 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); + + wordToBytes(wKey[131] ^ X3, out, outOff); + wordToBytes(wKey[130] ^ X2, out, outOff + 4); + wordToBytes(wKey[129] ^ X1, out, outOff + 8); + wordToBytes(wKey[128] ^ X0, out, outOff + 12); + } + + /** + * Decrypt one block of ciphertext. + * + * @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. + */ + private void decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + X3 = wKey[131] ^ bytesToWord(in, inOff); + X2 = wKey[130] ^ bytesToWord(in, inOff + 4); + X1 = wKey[129] ^ bytesToWord(in, inOff + 8); + X0 = wKey[128] ^ bytesToWord(in, 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); + + wordToBytes(X3 ^ wKey[3], out, outOff); + wordToBytes(X2 ^ wKey[2], out, outOff + 4); + wordToBytes(X1 ^ wKey[1], out, outOff + 8); + wordToBytes(X0 ^ wKey[0], out, outOff + 12); + } + + /** + * The sboxes below are based on the work of Brian Gladman and + * Sam Simpson, whose original notice appears below. + *
+ * 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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 + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java new file mode 100644 index 000000000..8b461f86a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java @@ -0,0 +1,201 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * Block cipher Shacal2, designed by Helena Handschuh and David Naccache, + * based on hash function SHA-256, + * using SHA-256-Initialization-Values as data and SHA-256-Data as key. + *
+ * A description of Shacal can be found at: + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.3.4066 + * Best known cryptanalytic (Wikipedia 11.2013): + * Related-key rectangle attack on 44-rounds (Jiqiang Lu, Jongsung Kim). + * Comments are related to SHA-256-Naming as described in FIPS PUB 180-2 + *
+ */ +public class Shacal2Engine + implements BlockCipher +{ + private final static int[] K = { // SHA-256-Constants + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + private static final int BLOCK_SIZE = 32; + private boolean forEncryption = false; + private static final int ROUNDS = 64; + + private int[] workingKey = null; // expanded key: corresponds to the message block W in FIPS PUB 180-2 + + public Shacal2Engine() + { + } + + public void reset() + { + } + + public String getAlgorithmName() + { + return "Shacal2"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public void init(boolean _forEncryption, CipherParameters params) + throws IllegalArgumentException + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("only simple KeyParameter expected."); + } + this.forEncryption = _forEncryption; + workingKey = new int[64]; + setKey( ((KeyParameter)params).getKey() ); + } + + public void setKey(byte[] kb) + { + if (kb.length == 0 || kb.length > 64 || kb.length < 16 || kb.length % 8 != 0) + { + throw new IllegalArgumentException("Shacal2-key must be 16 - 64 bytes and multiple of 8"); + } + + bytes2ints(kb, workingKey, 0, 0); + + for ( int i = 16; i < 64; i++) + { // Key-Expansion, implicitly Zero-Padding for 16 > i > kb.length/4 + workingKey[i] = + ( (workingKey[i-2] >>> 17 | workingKey[i-2] << -17) // corresponds to ROTL n(x) of FIPS PUB 180-2 + ^ (workingKey[i-2] >>> 19 | workingKey[i-2] << -19) + ^ (workingKey[i-2] >>> 10) ) // corresponds to sigma1(x)-Function of FIPS PUB 180-2 + + workingKey[i-7] + + ( (workingKey[i-15] >>> 7 | workingKey[i-15] << -7) + ^ (workingKey[i-15] >>> 18 | workingKey[i-15] << -18) + ^ (workingKey[i-15] >>> 3) ) // corresponds to sigma0(x)-Function of FIPS PUB 180-2 + + workingKey[i-16]; + } + } + + public void encryptBlock(byte[] in, int inOffset, byte[] out, int outOffset) + { + int[] block = new int[BLOCK_SIZE / 4];// corresponds to working variables a,b,c,d,e,f,g,h of FIPS PUB 180-2 + bytes2ints(in, block, inOffset, 0); + + for (int i = 0; i < ROUNDS; i++) + { + int tmp = + (((block[4] >>> 6) | (block[4] << -6)) + ^ ((block[4] >>> 11) | (block[4] << -11)) + ^ ((block[4] >>> 25) | (block[4] << -25))) + + ((block[4] & block[5]) ^ ((~block[4]) & block[6])) + + block[7] + K[i] + workingKey[i]; // corresponds to T1 of FIPS PUB 180-2 + block[7] = block[6]; + block[6] = block[5]; + block[5] = block[4]; + block[4] = block[3] + tmp; + block[3] = block[2]; + block[2] = block[1]; + block[1] = block[0]; + block[0] = tmp + + (((block[0] >>> 2) | (block[0] << -2)) + ^ ((block[0] >>> 13) | (block[0] << -13)) + ^ ((block[0] >>> 22) | (block[0] << -22))) + + ((block[0] & block[2]) ^ (block[0] & block[3]) ^ (block[2] & block[3])); + //corresponds to T2 of FIPS PUB 180-2, block[1] and block[2] replaced + } + ints2bytes(block, out, outOffset); + } + + public void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset) + { + int[] block = new int[BLOCK_SIZE / 4]; + bytes2ints(in, block, inOffset, 0); + for (int i = ROUNDS - 1; i >-1; i--) + { + int tmp = block[0] - (((block[1] >>> 2) | (block[1] << -2)) + ^ ((block[1] >>> 13) | (block[1] << -13)) + ^ ((block[1] >>> 22) | (block[1] << -22))) + - ((block[1] & block[2]) ^ (block[1] & block[3]) ^ (block[2] & block[3])); // T2 + block[0] = block[1]; + block[1] = block[2]; + block[2] = block[3]; + block[3] = block[4] - tmp; + block[4] = block[5]; + block[5] = block[6]; + block[6] = block[7]; + block[7] = tmp - K[i] - workingKey[i] + - (((block[4] >>> 6) | (block[4] << -6)) + ^ ((block[4] >>> 11) | (block[4] << -11)) + ^ ((block[4] >>> 25) | (block[4] << -25))) + - ((block[4] & block[5]) ^ ((~block[4]) & block[6])); // T1 + } + ints2bytes(block, out, outOffset); + } + + public int processBlock(byte[] in, int inOffset, byte[] out, int outOffset) + throws DataLengthException, IllegalStateException + { + if (workingKey == null) + { + throw new IllegalStateException("Shacal2 not initialised"); + } + + if ((inOffset + BLOCK_SIZE) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOffset + BLOCK_SIZE) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (forEncryption) + { + encryptBlock(in, inOffset, out, outOffset); + } + else + { + decryptBlock(in, inOffset, out, outOffset); + } + + return BLOCK_SIZE; + } + + private void bytes2ints(byte[] bytes, int[] block, int bytesPos, int blockPos) + { + for (int i = blockPos; i < bytes.length / 4; i++) + { + block[i] = ((bytes[bytesPos++] & 0xFF) << 24) + | ((bytes[bytesPos++] & 0xFF) << 16) + | ((bytes[bytesPos++] & 0xFF) << 8) + | (bytes[bytesPos++] & 0xFF); + } + } + + private void ints2bytes(int[] block, byte[] out, int pos) + { + for (int i = 0; i < block.length; i++) + { + out[pos++] = (byte)(block[i] >>> 24); + out[pos++] = (byte)(block[i] >>> 16); + out[pos++] = (byte)(block[i] >>> 8); + out[pos++] = (byte)block[i]; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java new file mode 100644 index 000000000..4f7a5f6b7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java @@ -0,0 +1,260 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * a class that provides a basic SKIPJACK engine. + */ +public class SkipjackEngine + implements BlockCipher +{ + static final int BLOCK_SIZE = 8; + + static short ftable[] = + { + 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9, + 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28, + 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53, + 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2, + 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8, + 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90, + 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76, + 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d, + 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18, + 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4, + 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40, + 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5, + 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2, + 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8, + 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac, + 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46 + }; + + private int[] key0, key1, key2, key3; + private boolean encrypting; + + /** + * initialise a SKIPJACK cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to SKIPJACK init - " + params.getClass().getName()); + } + + byte[] keyBytes = ((KeyParameter)params).getKey(); + + this.encrypting = encrypting; + this.key0 = new int[32]; + this.key1 = new int[32]; + this.key2 = new int[32]; + this.key3 = new int[32]; + + // + // expand the key to 128 bytes in 4 parts (saving us a modulo, multiply + // and an addition). + // + for (int i = 0; i < 32; i ++) + { + key0[i] = keyBytes[(i * 4) % 10] & 0xff; + key1[i] = keyBytes[(i * 4 + 1) % 10] & 0xff; + key2[i] = keyBytes[(i * 4 + 2) % 10] & 0xff; + key3[i] = keyBytes[(i * 4 + 3) % 10] & 0xff; + } + } + + public String getAlgorithmName() + { + return "SKIPJACK"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (key1 == null) + { + throw new IllegalStateException("SKIPJACK engine 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() + { + } + + /** + * The G permutation + */ + private int g( + int k, + int w) + { + int g1, g2, g3, g4, g5, g6; + + g1 = (w >> 8) & 0xff; + g2 = w & 0xff; + + g3 = ftable[g2 ^ key0[k]] ^ g1; + g4 = ftable[g3 ^ key1[k]] ^ g2; + g5 = ftable[g4 ^ key2[k]] ^ g3; + g6 = ftable[g5 ^ key3[k]] ^ g4; + + return ((g5 << 8) + g6); + } + + public int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int w1 = (in[inOff + 0] << 8) + (in[inOff + 1] & 0xff); + int w2 = (in[inOff + 2] << 8) + (in[inOff + 3] & 0xff); + int w3 = (in[inOff + 4] << 8) + (in[inOff + 5] & 0xff); + int w4 = (in[inOff + 6] << 8) + (in[inOff + 7] & 0xff); + + int k = 0; + + for (int t = 0; t < 2; t++) + { + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w2; + w2 = g(k, w1); + w1 = w2 ^ tmp ^ (k + 1); + k++; + } + + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w1 ^ w2 ^ (k + 1); + w2 = g(k, w1); + w1 = tmp; + k++; + } + } + + out[outOff + 0] = (byte)((w1 >> 8)); + out[outOff + 1] = (byte)(w1); + out[outOff + 2] = (byte)((w2 >> 8)); + out[outOff + 3] = (byte)(w2); + out[outOff + 4] = (byte)((w3 >> 8)); + out[outOff + 5] = (byte)(w3); + out[outOff + 6] = (byte)((w4 >> 8)); + out[outOff + 7] = (byte)(w4); + + return BLOCK_SIZE; + } + + /** + * the inverse of the G permutation. + */ + private int h( + int k, + int w) + { + int h1, h2, h3, h4, h5, h6; + + h1 = w & 0xff; + h2 = (w >> 8) & 0xff; + + h3 = ftable[h2 ^ key3[k]] ^ h1; + h4 = ftable[h3 ^ key2[k]] ^ h2; + h5 = ftable[h4 ^ key1[k]] ^ h3; + h6 = ftable[h5 ^ key0[k]] ^ h4; + + return ((h6 << 8) + h5); + } + + public int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + int w2 = (in[inOff + 0] << 8) + (in[inOff + 1] & 0xff); + int w1 = (in[inOff + 2] << 8) + (in[inOff + 3] & 0xff); + int w4 = (in[inOff + 4] << 8) + (in[inOff + 5] & 0xff); + int w3 = (in[inOff + 6] << 8) + (in[inOff + 7] & 0xff); + + int k = 31; + + for (int t = 0; t < 2; t++) + { + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w2; + w2 = h(k, w1); + w1 = w2 ^ tmp ^ (k + 1); + k--; + } + + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w1 ^ w2 ^ (k + 1); + w2 = h(k, w1); + w1 = tmp; + k--; + } + } + + out[outOff + 0] = (byte)((w2 >> 8)); + out[outOff + 1] = (byte)(w2); + out[outOff + 2] = (byte)((w1 >> 8)); + out[outOff + 3] = (byte)(w1); + out[outOff + 4] = (byte)((w4 >> 8)); + out[outOff + 5] = (byte)(w4); + out[outOff + 6] = (byte)((w3 >> 8)); + out[outOff + 7] = (byte)(w3); + + return BLOCK_SIZE; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java new file mode 100644 index 000000000..a027edc91 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java @@ -0,0 +1,184 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * An TEA engine. + */ +public class TEAEngine + implements BlockCipher +{ + private static final int rounds = 32, + block_size = 8, +// key_size = 16, + delta = 0x9E3779B9, + d_sum = 0xC6EF3720; // sum on decrypt + /* + * the expanded key array of 4 subkeys + */ + private int _a, _b, _c, _d; + private boolean _initialised; + private boolean _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public TEAEngine() + { + _initialised = false; + } + + public String getAlgorithmName() + { + return "TEA"; + } + + public int getBlockSize() + { + return block_size; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to TEA init - " + params.getClass().getName()); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter)params; + + setKey(p.getKey()); + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (!_initialised) + { + 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"); + } + + return (_forEncryption) ? encryptBlock(in, inOff, out, outOff) + : decryptBlock(in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * Re-key the cipher. + *+ * @param key the key to be used + */ + private void setKey( + byte[] key) + { + if (key.length != 16) + { + throw new IllegalArgumentException("Key size must be 128 bits."); + } + + _a = bytesToInt(key, 0); + _b = bytesToInt(key, 4); + _c = bytesToInt(key, 8); + _d = bytesToInt(key, 12); + } + + private int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(in, inOff); + int v1 = bytesToInt(in, inOff + 4); + + int sum = 0; + + for (int i = 0; i != rounds; i++) + { + sum += delta; + v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b); + v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d); + } + + unpackInt(v0, out, outOff); + unpackInt(v1, out, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(in, inOff); + int v1 = bytesToInt(in, inOff + 4); + + int sum = d_sum; + + for (int i = 0; i != rounds; i++) + { + v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d); + v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b); + sum -= delta; + } + + unpackInt(v0, out, outOff); + unpackInt(v1, out, outOff + 4); + + return block_size; + } + + private int bytesToInt(byte[] in, int inOff) + { + return ((in[inOff++]) << 24) | + ((in[inOff++] & 255) << 16) | + ((in[inOff++] & 255) << 8) | + ((in[inOff] & 255)); + } + + private void unpackInt(int v, byte[] out, int outOff) + { + out[outOff++] = (byte)(v >>> 24); + out[outOff++] = (byte)(v >>> 16); + out[outOff++] = (byte)(v >>> 8); + out[outOff ] = (byte)v; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java new file mode 100644 index 000000000..a5d59308b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java @@ -0,0 +1,1494 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.TweakableBlockCipherParameters; + +/** + * Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block + * sizes. + *
+ * This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST + * SHA-3 competition in October 2010. + * + * Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + * + * This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables + * to speed up key schedule injection.null
to use the current key.
+ * @param tweak the 2 word (128 bit) tweak, or null
to use the current tweak.
+ */
+ public void init(boolean forEncryption, final long[] key, final long[] tweak)
+ {
+ this.forEncryption = forEncryption;
+ if (key != null)
+ {
+ setKey(key);
+ }
+ if (tweak != null)
+ {
+ setTweak(tweak);
+ }
+ }
+
+ private void setKey(long[] key)
+ {
+ if (key.length != this.blocksizeWords)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeWords
+ + " words)");
+ }
+
+ /*
+ * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512,
+ * 20k for 1024).
+ *
+ * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations
+ * used, to avoid expensive mod computations during cipher operation.
+ */
+
+ long knw = C_240;
+ for (int i = 0; i < blocksizeWords; i++)
+ {
+ kw[i] = key[i];
+ knw = knw ^ kw[i];
+ }
+ kw[blocksizeWords] = knw;
+ System.arraycopy(kw, 0, kw, blocksizeWords + 1, blocksizeWords);
+ }
+
+ private void setTweak(long[] tweak)
+ {
+ if (tweak.length != TWEAK_SIZE_WORDS)
+ {
+ throw new IllegalArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words.");
+ }
+
+ /*
+ * Tweak schedule partially repeated to avoid mod computations during cipher operation
+ */
+ t[0] = tweak[0];
+ t[1] = tweak[1];
+ t[2] = t[0] ^ t[1];
+ t[3] = t[0];
+ t[4] = t[1];
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Threefish-" + (blocksizeBytes * 8);
+ }
+
+ public int getBlockSize()
+ {
+ return blocksizeBytes;
+ }
+
+ public void reset()
+ {
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ if ((outOff + blocksizeBytes) > out.length)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if ((inOff + blocksizeBytes) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ currentBlock[i >> 3] = bytesToWord(in, inOff + i);
+ }
+ processBlock(this.currentBlock, this.currentBlock);
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ wordToBytes(this.currentBlock[i >> 3], out, outOff + i);
+ }
+
+ return blocksizeBytes;
+ }
+
+ /**
+ * Process a block of data represented as 64 bit words.
+ *
+ * @param in a block sized buffer of words to process.
+ * @param out a block sized buffer of words to receive the output of the operation.
+ * @return the number of 8 byte words processed (which will be the same as the block size).
+ * @throws DataLengthException if either the input or output is not block sized.
+ * @throws IllegalStateException if this engine is not initialised.
+ */
+ public int processBlock(long[] in, long[] out)
+ throws DataLengthException, IllegalStateException
+ {
+ if (kw[blocksizeWords] == 0)
+ {
+ throw new IllegalStateException("Threefish engine not initialised");
+ }
+
+ if (in.length != blocksizeWords)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+ if (out.length != blocksizeWords)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ cipher.encryptBlock(in, out);
+ }
+ else
+ {
+ cipher.decryptBlock(in, out);
+ }
+
+ return blocksizeWords;
+ }
+
+ /**
+ * Read a single 64 bit word from input in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static long bytesToWord(final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+
+ long word = 0;
+ int index = off;
+
+ word = (bytes[index++] & 0xffL);
+ word |= (bytes[index++] & 0xffL) << 8;
+ word |= (bytes[index++] & 0xffL) << 16;
+ word |= (bytes[index++] & 0xffL) << 24;
+ word |= (bytes[index++] & 0xffL) << 32;
+ word |= (bytes[index++] & 0xffL) << 40;
+ word |= (bytes[index++] & 0xffL) << 48;
+ word |= (bytes[index++] & 0xffL) << 56;
+
+ return word;
+ }
+
+ /**
+ * Write a 64 bit word to output in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static void wordToBytes(final long word, final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+ int index = off;
+
+ bytes[index++] = (byte)word;
+ bytes[index++] = (byte)(word >> 8);
+ bytes[index++] = (byte)(word >> 16);
+ bytes[index++] = (byte)(word >> 24);
+ bytes[index++] = (byte)(word >> 32);
+ bytes[index++] = (byte)(word >> 40);
+ bytes[index++] = (byte)(word >> 48);
+ bytes[index++] = (byte)(word >> 56);
+ }
+
+ /**
+ * Rotate left + xor part of the mix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long rotlXor(long x, int n, long xor)
+ {
+ return ((x << n) | (x >>> -n)) ^ xor;
+ }
+
+ /**
+ * Rotate xor + rotate right part of the unmix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long xorRotr(long x, int n, long xor)
+ {
+ long xored = x ^ xor;
+ return (xored >>> n) | (xored << -n);
+ }
+
+ private static abstract class ThreefishCipher
+ {
+ /**
+ * The extended + repeated tweak words
+ */
+ protected final long[] t;
+ /**
+ * The extended + repeated key words
+ */
+ protected final long[] kw;
+
+ protected ThreefishCipher(final long[] kw, final long[] t)
+ {
+ this.kw = kw;
+ this.t = t;
+ }
+
+ abstract void encryptBlock(long[] block, long[] out);
+
+ abstract void decryptBlock(long[] block, long[] out);
+
+ }
+
+ private static final class Threefish256Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 14, ROTATION_0_1 = 16;
+ private static final int ROTATION_1_0 = 52, ROTATION_1_1 = 57;
+ private static final int ROTATION_2_0 = 23, ROTATION_2_1 = 40;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 37;
+
+ private static final int ROTATION_4_0 = 25, ROTATION_4_1 = 33;
+ private static final int ROTATION_5_0 = 46, ROTATION_5_1 = 12;
+ private static final int ROTATION_6_0 = 58, ROTATION_6_1 = 22;
+ private static final int ROTATION_7_0 = 32, ROTATION_7_1 = 32;
+
+ public Threefish256Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 4 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1] + t[0];
+ b2 += kw[2] + t[1];
+ b3 += kw[3];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 2 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_256 / 4); d += 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 2 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_1_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_1_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_3_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_3_1, b2 += b1);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm5];
+ b1 += kw[dm5 + 1] + t[dm3];
+ b2 += kw[dm5 + 2] + t[dm3 + 1];
+ b3 += kw[dm5 + 3] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_5_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_5_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_7_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_7_1, b2 += b1);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm5 + 1];
+ b1 += kw[dm5 + 2] + t[dm3 + 1];
+ b2 += kw[dm5 + 3] + t[dm3 + 2];
+ b3 += kw[dm5 + 4] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm5 + 1];
+ b1 -= kw[dm5 + 2] + t[dm3 + 1];
+ b2 -= kw[dm5 + 3] + t[dm3 + 2];
+ b3 -= kw[dm5 + 4] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b3 = xorRotr(b3, ROTATION_7_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_7_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_5_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_5_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm5];
+ b1 -= kw[dm5 + 1] + t[dm3];
+ b2 -= kw[dm5 + 2] + t[dm3 + 1];
+ b3 -= kw[dm5 + 3] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b3 = xorRotr(b3, ROTATION_3_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_3_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_1_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_1_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1] + t[0];
+ b2 -= kw[2] + t[1];
+ b3 -= kw[3];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ }
+
+ }
+
+ private static final class Threefish512Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37;
+ private static final int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42;
+ private static final int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39;
+ private static final int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56;
+
+ private static final int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24;
+ private static final int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17;
+ private static final int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43;
+ private static final int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22;
+
+ protected Threefish512Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ public void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 8 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5] + t[0];
+ b6 += kw[6] + t[1];
+ b7 += kw[7];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_512 / 4); d += 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_1_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_1_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_1_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_1_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_2_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_2_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_3_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_3_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_3_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_3_3, b4 += b3);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm9];
+ b1 += kw[dm9 + 1];
+ b2 += kw[dm9 + 2];
+ b3 += kw[dm9 + 3];
+ b4 += kw[dm9 + 4];
+ b5 += kw[dm9 + 5] + t[dm3];
+ b6 += kw[dm9 + 6] + t[dm3 + 1];
+ b7 += kw[dm9 + 7] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_5_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_5_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_5_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_5_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_6_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_6_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_7_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_7_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_7_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_7_3, b4 += b3);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm9 + 1];
+ b1 += kw[dm9 + 2];
+ b2 += kw[dm9 + 3];
+ b3 += kw[dm9 + 4];
+ b4 += kw[dm9 + 5];
+ b5 += kw[dm9 + 6] + t[dm3 + 1];
+ b6 += kw[dm9 + 7] + t[dm3 + 2];
+ b7 += kw[dm9 + 8] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ }
+
+ public void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm9 + 1];
+ b1 -= kw[dm9 + 2];
+ b2 -= kw[dm9 + 3];
+ b3 -= kw[dm9 + 4];
+ b4 -= kw[dm9 + 5];
+ b5 -= kw[dm9 + 6] + t[dm3 + 1];
+ b6 -= kw[dm9 + 7] + t[dm3 + 2];
+ b7 -= kw[dm9 + 8] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b1 = xorRotr(b1, ROTATION_7_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_7_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_7_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_6_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_6_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_5_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_5_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_5_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_5_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm9];
+ b1 -= kw[dm9 + 1];
+ b2 -= kw[dm9 + 2];
+ b3 -= kw[dm9 + 3];
+ b4 -= kw[dm9 + 4];
+ b5 -= kw[dm9 + 5] + t[dm3];
+ b6 -= kw[dm9 + 6] + t[dm3 + 1];
+ b7 -= kw[dm9 + 7] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b1 = xorRotr(b1, ROTATION_3_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_3_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_3_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_2_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_2_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_1_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_1_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_1_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_1_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5] + t[0];
+ b6 -= kw[6] + t[1];
+ b7 -= kw[7];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ }
+ }
+
+ private static final class Threefish1024Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47;
+ private static final int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37;
+ private static final int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55;
+ private static final int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52;
+ private static final int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13;
+ private static final int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41;
+ private static final int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25;
+
+ private static final int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31;
+ private static final int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30;
+ private static final int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51;
+ private static final int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41;
+ private static final int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46;
+ private static final int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25;
+ private static final int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52;
+ private static final int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20;
+
+ public Threefish1024Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 16 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5];
+ b6 += kw[6];
+ b7 += kw[7];
+ b8 += kw[8];
+ b9 += kw[9];
+ b10 += kw[10];
+ b11 += kw[11];
+ b12 += kw[12];
+ b13 += kw[13] + t[0];
+ b14 += kw[14] + t[1];
+ b15 += kw[15];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_1024 / 4); d += 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_0_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_0_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_0_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_0_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_1_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_1_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_1_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_1_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_1_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_1_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_1_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_1_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_2_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_2_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_2_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_2_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_2_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_2_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_2_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_2_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_3_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_3_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_3_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_3_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_3_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_3_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_3_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_3_7, b12 += b7);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm17];
+ b1 += kw[dm17 + 1];
+ b2 += kw[dm17 + 2];
+ b3 += kw[dm17 + 3];
+ b4 += kw[dm17 + 4];
+ b5 += kw[dm17 + 5];
+ b6 += kw[dm17 + 6];
+ b7 += kw[dm17 + 7];
+ b8 += kw[dm17 + 8];
+ b9 += kw[dm17 + 9];
+ b10 += kw[dm17 + 10];
+ b11 += kw[dm17 + 11];
+ b12 += kw[dm17 + 12];
+ b13 += kw[dm17 + 13] + t[dm3];
+ b14 += kw[dm17 + 14] + t[dm3 + 1];
+ b15 += kw[dm17 + 15] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_4_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_4_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_4_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_4_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_5_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_5_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_5_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_5_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_5_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_5_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_5_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_5_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_6_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_6_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_6_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_6_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_6_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_6_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_6_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_6_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_7_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_7_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_7_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_7_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_7_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_7_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_7_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_7_7, b12 += b7);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm17 + 1];
+ b1 += kw[dm17 + 2];
+ b2 += kw[dm17 + 3];
+ b3 += kw[dm17 + 4];
+ b4 += kw[dm17 + 5];
+ b5 += kw[dm17 + 6];
+ b6 += kw[dm17 + 7];
+ b7 += kw[dm17 + 8];
+ b8 += kw[dm17 + 9];
+ b9 += kw[dm17 + 10];
+ b10 += kw[dm17 + 11];
+ b11 += kw[dm17 + 12];
+ b12 += kw[dm17 + 13];
+ b13 += kw[dm17 + 14] + t[dm3 + 1];
+ b14 += kw[dm17 + 15] + t[dm3 + 2];
+ b15 += kw[dm17 + 16] + d + 1;
+
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ out[8] = b8;
+ out[9] = b9;
+ out[10] = b10;
+ out[11] = b11;
+ out[12] = b12;
+ out[13] = b13;
+ out[14] = b14;
+ out[15] = b15;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm17 + 1];
+ b1 -= kw[dm17 + 2];
+ b2 -= kw[dm17 + 3];
+ b3 -= kw[dm17 + 4];
+ b4 -= kw[dm17 + 5];
+ b5 -= kw[dm17 + 6];
+ b6 -= kw[dm17 + 7];
+ b7 -= kw[dm17 + 8];
+ b8 -= kw[dm17 + 9];
+ b9 -= kw[dm17 + 10];
+ b10 -= kw[dm17 + 11];
+ b11 -= kw[dm17 + 12];
+ b12 -= kw[dm17 + 13];
+ b13 -= kw[dm17 + 14] + t[dm3 + 1];
+ b14 -= kw[dm17 + 15] + t[dm3 + 2];
+ b15 -= kw[dm17 + 16] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_7_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_7_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_7_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_7_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_7_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_7_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_7_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_6_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_6_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_6_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_6_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_6_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_6_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_6_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_6_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_5_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_5_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_5_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_5_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_5_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_5_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_5_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_5_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_4_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_4_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_4_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_4_7, b14);
+ b14 -= b15;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm17];
+ b1 -= kw[dm17 + 1];
+ b2 -= kw[dm17 + 2];
+ b3 -= kw[dm17 + 3];
+ b4 -= kw[dm17 + 4];
+ b5 -= kw[dm17 + 5];
+ b6 -= kw[dm17 + 6];
+ b7 -= kw[dm17 + 7];
+ b8 -= kw[dm17 + 8];
+ b9 -= kw[dm17 + 9];
+ b10 -= kw[dm17 + 10];
+ b11 -= kw[dm17 + 11];
+ b12 -= kw[dm17 + 12];
+ b13 -= kw[dm17 + 13] + t[dm3];
+ b14 -= kw[dm17 + 14] + t[dm3 + 1];
+ b15 -= kw[dm17 + 15] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_3_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_3_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_3_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_3_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_3_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_3_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_3_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_2_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_2_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_2_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_2_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_2_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_2_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_2_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_2_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_1_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_1_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_1_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_1_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_1_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_1_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_1_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_1_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_0_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_0_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_0_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_0_7, b14);
+ b14 -= b15;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5];
+ b6 -= kw[6];
+ b7 -= kw[7];
+ b8 -= kw[8];
+ b9 -= kw[9];
+ b10 -= kw[10];
+ b11 -= kw[11];
+ b12 -= kw[12];
+ b13 -= kw[13] + t[0];
+ b14 -= kw[14] + t[1];
+ b15 -= kw[15];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ state[8] = b8;
+ state[9] = b9;
+ state[10] = b10;
+ state[11] = b11;
+ state[12] = b12;
+ state[13] = b13;
+ state[14] = b14;
+ state[15] = b15;
+ }
+
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java
new file mode 100644
index 000000000..c5b843850
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java
@@ -0,0 +1,680 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides Twofish encryption operations.
+ *
+ * This Java implementation is based on the Java reference
+ * implementation provided by Bruce Schneier and developed
+ * by Raif S. Naffah.
+ */
+public final class TwofishEngine
+ implements BlockCipher
+{
+ private static final byte[][] P = {
+ { // p0
+ (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8,
+ (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76,
+ (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78,
+ (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38,
+ (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98,
+ (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C,
+ (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26,
+ (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48,
+ (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30,
+ (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23,
+ (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59,
+ (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82,
+ (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E,
+ (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C,
+ (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE,
+ (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61,
+ (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5,
+ (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B,
+ (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B,
+ (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1,
+ (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45,
+ (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66,
+ (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56,
+ (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7,
+ (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5,
+ (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA,
+ (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF,
+ (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71,
+ (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD,
+ (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8,
+ (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D,
+ (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+ (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED,
+ (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2,
+ (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11,
+ (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90,
+ (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF,
+ (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB,
+ (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B,
+ (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF,
+ (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE,
+ (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B,
+ (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46,
+ (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64,
+ (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F,
+ (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A,
+ (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A,
+ (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A,
+ (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29,
+ (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02,
+ (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17,
+ (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D,
+ (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74,
+ (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72,
+ (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12,
+ (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34,
+ (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68,
+ (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8,
+ (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40,
+ (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4,
+ (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0,
+ (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00,
+ (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42,
+ (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 },
+ { // p1
+ (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4,
+ (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8,
+ (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B,
+ (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B,
+ (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD,
+ (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1,
+ (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B,
+ (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F,
+ (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B,
+ (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D,
+ (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E,
+ (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5,
+ (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14,
+ (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3,
+ (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54,
+ (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51,
+ (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A,
+ (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96,
+ (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10,
+ (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C,
+ (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7,
+ (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70,
+ (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB,
+ (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8,
+ (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF,
+ (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC,
+ (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF,
+ (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2,
+ (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82,
+ (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9,
+ (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97,
+ (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17,
+ (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D,
+ (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3,
+ (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C,
+ (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E,
+ (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F,
+ (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49,
+ (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21,
+ (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9,
+ (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD,
+ (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01,
+ (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F,
+ (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48,
+ (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E,
+ (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19,
+ (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57,
+ (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64,
+ (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE,
+ (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5,
+ (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44,
+ (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69,
+ (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15,
+ (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E,
+ (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34,
+ (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC,
+ (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B,
+ (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB,
+ (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52,
+ (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9,
+ (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4,
+ (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2,
+ (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56,
+ (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 }
+ };
+
+ /**
+ * Define the fixed p0/p1 permutations used in keyed S-box lookup.
+ * By changing the following constant definitions, the S-boxes will
+ * automatically get changed in the Twofish engine.
+ */
+ private static final int P_00 = 1;
+ private static final int P_01 = 0;
+ private static final int P_02 = 0;
+ private static final int P_03 = P_01 ^ 1;
+ private static final int P_04 = 1;
+
+ private static final int P_10 = 0;
+ private static final int P_11 = 0;
+ private static final int P_12 = 1;
+ private static final int P_13 = P_11 ^ 1;
+ private static final int P_14 = 0;
+
+ private static final int P_20 = 1;
+ private static final int P_21 = 1;
+ private static final int P_22 = 0;
+ private static final int P_23 = P_21 ^ 1;
+ private static final int P_24 = 0;
+
+ private static final int P_30 = 0;
+ private static final int P_31 = 1;
+ private static final int P_32 = 1;
+ private static final int P_33 = P_31 ^ 1;
+ private static final int P_34 = 1;
+
+ /* Primitive polynomial for GF(256) */
+ private static final int GF256_FDBK = 0x169;
+ private static final int GF256_FDBK_2 = GF256_FDBK / 2;
+ private static final int GF256_FDBK_4 = GF256_FDBK / 4;
+
+ private static final int RS_GF_FDBK = 0x14D; // field generator
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static final int ROUNDS = 16;
+ private static final int MAX_ROUNDS = 16; // bytes = 128 bits
+ private static final int BLOCK_SIZE = 16; // bytes = 128 bits
+ private static final int MAX_KEY_BITS = 256;
+
+ private static final int INPUT_WHITEN=0;
+ private static final int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4
+ private static final int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8
+
+ private static final int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40
+
+ private static final int SK_STEP = 0x02020202;
+ private static final int SK_BUMP = 0x01010101;
+ private static final int SK_ROTL = 9;
+
+ private boolean encrypting = false;
+
+ private int[] gMDS0 = new int[MAX_KEY_BITS];
+ private int[] gMDS1 = new int[MAX_KEY_BITS];
+ private int[] gMDS2 = new int[MAX_KEY_BITS];
+ private int[] gMDS3 = new int[MAX_KEY_BITS];
+
+ /**
+ * gSubKeys[] and gSBox[] are eventually used in the
+ * encryption and decryption methods.
+ */
+ private int[] gSubKeys;
+ private int[] gSBox;
+
+ private int k64Cnt = 0;
+
+ private byte[] workingKey = null;
+
+ public TwofishEngine()
+ {
+ // calculate the MDS matrix
+ int[] m1 = new int[2];
+ int[] mX = new int[2];
+ int[] mY = new int[2];
+ int j;
+
+ for (int i=0; i< MAX_KEY_BITS ; i++)
+ {
+ j = P[0][i] & 0xff;
+ m1[0] = j;
+ mX[0] = Mx_X(j) & 0xff;
+ mY[0] = Mx_Y(j) & 0xff;
+
+ j = P[1][i] & 0xff;
+ m1[1] = j;
+ mX[1] = Mx_X(j) & 0xff;
+ mY[1] = Mx_Y(j) & 0xff;
+
+ gMDS0[i] = m1[P_00] | mX[P_00] << 8 |
+ mY[P_00] << 16 | mY[P_00] << 24;
+
+ gMDS1[i] = mY[P_10] | mY[P_10] << 8 |
+ mX[P_10] << 16 | m1[P_10] << 24;
+
+ gMDS2[i] = mX[P_20] | mY[P_20] << 8 |
+ m1[P_20] << 16 | mY[P_20] << 24;
+
+ gMDS3[i] = mX[P_30] | m1[P_30] << 8 |
+ mY[P_30] << 16 | mX[P_30] << 24;
+ }
+ }
+
+ /**
+ * initialise a Twofish cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.workingKey = ((KeyParameter)params).getKey();
+ this.k64Cnt = (this.workingKey.length / 8); // pre-padded ?
+ setKey(this.workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Twofish init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Twofish";
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Twofish 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()
+ {
+ if (this.workingKey != null)
+ {
+ setKey(this.workingKey);
+ }
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private void setKey(byte[] key)
+ {
+ int[] k32e = new int[MAX_KEY_BITS/64]; // 4
+ int[] k32o = new int[MAX_KEY_BITS/64]; // 4
+
+ int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4
+ gSubKeys = new int[TOTAL_SUBKEYS];
+
+ if (k64Cnt < 1)
+ {
+ throw new IllegalArgumentException("Key size less than 64 bits");
+ }
+
+ if (k64Cnt > 4)
+ {
+ throw new IllegalArgumentException("Key size larger than 256 bits");
+ }
+
+ /*
+ * k64Cnt is the number of 8 byte blocks (64 chunks)
+ * that are in the input key. The input key is a
+ * maximum of 32 bytes (256 bits), so the range
+ * for k64Cnt is 1..4
+ */
+ for (int i=0; i+ *
+ * g(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1 + *+ * where a = primitive root of field generator 0x14D + */ + private int RS_rem(int x) + { + int b = (x >>> 24) & 0xff; + int g2 = ((b << 1) ^ + ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; + int g3 = ((b >>> 1) ^ + ((b & 0x01) != 0 ? (RS_GF_FDBK >>> 1) : 0)) ^ g2 ; + return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); + } + + private int LFSR1(int x) + { + return (x >> 1) ^ + (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); + } + + private int LFSR2(int x) + { + return (x >> 2) ^ + (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ + (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); + } + + private int Mx_X(int x) + { + return x ^ LFSR2(x); + } // 5B + + private int Mx_Y(int x) + { + return x ^ LFSR1(x) ^ LFSR2(x); + } // EF + + private int b0(int x) + { + return x & 0xff; + } + + private int b1(int x) + { + return (x >>> 8) & 0xff; + } + + private int b2(int x) + { + return (x >>> 16) & 0xff; + } + + private int b3(int x) + { + return (x >>> 24) & 0xff; + } + + private int Fe32_0(int x) + { + return gSBox[ 0x000 + 2*(x & 0xff) ] ^ + gSBox[ 0x001 + 2*((x >>> 8) & 0xff) ] ^ + gSBox[ 0x200 + 2*((x >>> 16) & 0xff) ] ^ + gSBox[ 0x201 + 2*((x >>> 24) & 0xff) ]; + } + + private int Fe32_3(int x) + { + return gSBox[ 0x000 + 2*((x >>> 24) & 0xff) ] ^ + gSBox[ 0x001 + 2*(x & 0xff) ] ^ + gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^ + gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ]; + } + + private int BytesTo32Bits(byte[] b, int p) + { + return ((b[p] & 0xff)) | + ((b[p+1] & 0xff) << 8) | + ((b[p+2] & 0xff) << 16) | + ((b[p+3] & 0xff) << 24); + } + + private void Bits32ToBytes(int in, byte[] b, int offset) + { + b[offset] = (byte)in; + b[offset + 1] = (byte)(in >> 8); + b[offset + 2] = (byte)(in >> 16); + b[offset + 3] = (byte)(in >> 24); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java new file mode 100644 index 000000000..b4a3f23c3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java @@ -0,0 +1,140 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; + +public class VMPCEngine implements StreamCipher +{ + /* + * variables to hold the state of the VMPC engine during encryption and + * decryption + */ + protected byte n = 0; + protected byte[] P = null; + protected byte s = 0; + + protected byte[] workingIV; + protected byte[] workingKey; + + public String getAlgorithmName() + { + return "VMPC"; + } + + /** + * initialise a VMPC cipher. + * + * @param forEncryption + * whether or not we are for encryption. + * @param params + * the parameters required to set up the cipher. + * @exception IllegalArgumentException + * if the params argument is inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + { + if (!(params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException( + "VMPC init parameters must include an IV"); + } + + ParametersWithIV ivParams = (ParametersWithIV) params; + + if (!(ivParams.getParameters() instanceof KeyParameter)) + { + throw new IllegalArgumentException( + "VMPC init parameters must include a key"); + } + + KeyParameter key = (KeyParameter) ivParams.getParameters(); + + this.workingIV = ivParams.getIV(); + + if (workingIV == null || workingIV.length < 1 || workingIV.length > 768) + { + throw new IllegalArgumentException("VMPC requires 1 to 768 bytes of IV"); + } + + this.workingKey = key.getKey(); + + initKey(this.workingKey, this.workingIV); + } + + protected void initKey(byte[] keyBytes, byte[] ivBytes) + { + s = 0; + P = new byte[256]; + for (int i = 0; i < 256; i++) + { + P[i] = (byte) i; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + n = 0; + } + + public void processBytes(byte[] in, int inOff, int len, byte[] out, + int outOff) + { + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + for (int i = 0; i < len; i++) + { + s = P[(s + P[n & 0xff]) & 0xff]; + byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + // encryption + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + + // xor + out[i + outOff] = (byte) (in[i + inOff] ^ z); + } + } + + public void reset() + { + initKey(this.workingKey, this.workingIV); + } + + public byte returnByte(byte in) + { + s = P[(s + P[n & 0xff]) & 0xff]; + byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + // encryption + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + + // xor + return (byte) (in ^ z); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java new file mode 100644 index 000000000..2e03fdbbb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java @@ -0,0 +1,45 @@ +package org.spongycastle.crypto.engines; + +public class VMPCKSA3Engine extends VMPCEngine +{ + public String getAlgorithmName() + { + return "VMPC-KSA3"; + } + + protected void initKey(byte[] keyBytes, byte[] ivBytes) + { + s = 0; + P = new byte[256]; + for (int i = 0; i < 256; i++) + { + P[i] = (byte) i; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + n = 0; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java new file mode 100644 index 000000000..0acbf101e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java @@ -0,0 +1,65 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.util.Pack; + +/** + * Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce. + *
+ * XSalsa20 requires a 256 bit key, and a 192 bit nonce. + */ +public class XSalsa20Engine extends Salsa20Engine +{ + + public String getAlgorithmName() + { + return "XSalsa20"; + } + + protected int getNonceSize() + { + return 24; + } + + /** + * XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce + * using a core Salsa20 function without input addition to produce 256 bit working key + * and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state. + */ + protected void setKey(byte[] keyBytes, byte[] ivBytes) + { + if (keyBytes.length != 32) + { + throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key"); + } + + // Set key for HSalsa20 + super.setKey(keyBytes, ivBytes); + + // Pack next 64 bits of IV into engine state instead of counter + engineState[8] = Pack.littleEndianToInt(ivBytes, 8); + engineState[9] = Pack.littleEndianToInt(ivBytes, 12); + + // Process engine state to generate Salsa20 key + int[] hsalsa20Out = new int[engineState.length]; + salsaCore(20, engineState, hsalsa20Out); + + // Set new key, removing addition in last round of salsaCore + engineState[1] = hsalsa20Out[0] - engineState[0]; + engineState[2] = hsalsa20Out[5] - engineState[5]; + engineState[3] = hsalsa20Out[10] - engineState[10]; + engineState[4] = hsalsa20Out[15] - engineState[15]; + + engineState[11] = hsalsa20Out[6] - engineState[6]; + engineState[12] = hsalsa20Out[7] - engineState[7]; + engineState[13] = hsalsa20Out[8] - engineState[8]; + engineState[14] = hsalsa20Out[9] - engineState[9]; + + // Last 64 bits of input IV + engineState[6] = Pack.littleEndianToInt(ivBytes, 16); + engineState[7] = Pack.littleEndianToInt(ivBytes, 20); + + // Counter reset + resetCounter(); + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java new file mode 100644 index 000000000..5dcb4ea1b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java @@ -0,0 +1,188 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.params.KeyParameter; + +/** + * An XTEA engine. + */ +public class XTEAEngine + implements BlockCipher +{ + private static final int rounds = 32, + block_size = 8, +// key_size = 16, + delta = 0x9E3779B9; + + /* + * the expanded key array of 4 subkeys + */ + private int[] _S = new int[4], + _sum0 = new int[32], + _sum1 = new int[32]; + private boolean _initialised, + _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public XTEAEngine() + { + _initialised = false; + } + + public String getAlgorithmName() + { + return "XTEA"; + } + + public int getBlockSize() + { + return block_size; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + if (!(params instanceof KeyParameter)) + { + throw new IllegalArgumentException("invalid parameter passed to TEA init - " + params.getClass().getName()); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter)params; + + setKey(p.getKey()); + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (!_initialised) + { + 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"); + } + + return (_forEncryption) ? encryptBlock(in, inOff, out, outOff) + : decryptBlock(in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * Re-key the cipher. + *
+ * @param key the key to be used + */ + private void setKey( + byte[] key) + { + if (key.length != 16) + { + throw new IllegalArgumentException("Key size must be 128 bits."); + } + + int i, j; + for (i = j = 0; i < 4; i++,j+=4) + { + _S[i] = bytesToInt(key, j); + } + + for (i = j = 0; i < rounds; i++) + { + _sum0[i] = (j + _S[j & 3]); + j += delta; + _sum1[i] = (j + _S[j >>> 11 & 3]); + } + } + + private int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(in, inOff); + int v1 = bytesToInt(in, inOff + 4); + + for (int i = 0; i < rounds; i++) + { + v0 += ((v1 << 4 ^ v1 >>> 5) + v1) ^ _sum0[i]; + v1 += ((v0 << 4 ^ v0 >>> 5) + v0) ^ _sum1[i]; + } + + unpackInt(v0, out, outOff); + unpackInt(v1, out, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(in, inOff); + int v1 = bytesToInt(in, inOff + 4); + + for (int i = rounds-1; i >= 0; i--) + { + v1 -= ((v0 << 4 ^ v0 >>> 5) + v0) ^ _sum1[i]; + v0 -= ((v1 << 4 ^ v1 >>> 5) + v1) ^ _sum0[i]; + } + + unpackInt(v0, out, outOff); + unpackInt(v1, out, outOff + 4); + + return block_size; + } + + private int bytesToInt(byte[] in, int inOff) + { + return ((in[inOff++]) << 24) | + ((in[inOff++] & 255) << 16) | + ((in[inOff++] & 255) << 8) | + ((in[inOff] & 255)); + } + + private void unpackInt(int v, byte[] out, int outOff) + { + out[outOff++] = (byte)(v >>> 24); + out[outOff++] = (byte)(v >>> 16); + out[outOff++] = (byte)(v >>> 8); + out[outOff ] = (byte)v; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/examples/DESExample.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/examples/DESExample.java new file mode 100644 index 000000000..d9e3a60da --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/examples/DESExample.java @@ -0,0 +1,419 @@ +package org.spongycastle.crypto.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.engines.DESedeEngine; +import org.spongycastle.crypto.generators.DESedeKeyGenerator; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.DESedeParameters; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.util.encoders.Hex; + +/** + * DESExample is a simple DES based encryptor/decryptor. + *
+ * The program is command line driven, with the input + * and output files specified on the command line. + *
+ * java org.spongycastle.crypto.examples.DESExample infile outfile [keyfile] + *+ * A new key is generated for each encryption, if key is not specified, + * then the example will assume encryption is required, and as output + * create deskey.dat in the current directory. This key is a hex + * encoded byte-stream that is used for the decryption. The output + * file is Hex encoded, 60 characters wide text file. + *
+ * When encrypting; + *
+ * When decrypting; + *
+ * This example shows how to use the light-weight API, DES and + * the filesystem for message encryption and decryption. + * + */ +public class DESExample extends Object +{ + // Encrypting or decrypting ? + private boolean encrypt = true; + + // To hold the initialised DESede cipher + private PaddedBufferedBlockCipher cipher = null; + + // The input stream of bytes to be processed for encryption + private BufferedInputStream in = null; + + // The output stream of bytes to be procssed + private BufferedOutputStream out = null; + + // The key + private byte[] key = null; + + /* + * start the application + */ + public static void main(String[] args) + { + boolean encrypt = true; + String infile = null; + String outfile = null; + String keyfile = null; + + if (args.length < 2) + { + DESExample de = new DESExample(); + System.err.println("Usage: java "+de.getClass().getName()+ + " infile outfile [keyfile]"); + System.exit(1); + } + + keyfile = "deskey.dat"; + infile = args[0]; + outfile = args[1]; + + if (args.length > 2) + { + encrypt = false; + keyfile = args[2]; + } + + DESExample de = new DESExample(infile, outfile, keyfile, encrypt); + de.process(); + } + + // Default constructor, used for the usage message + public DESExample() + { + } + + /* + * Constructor, that takes the arguments appropriate for + * processing the command line directives. + */ + public DESExample( + String infile, + String outfile, + String keyfile, + boolean encrypt) + { + /* + * First, determine that infile & keyfile exist as appropriate. + * + * This will also create the BufferedInputStream as required + * for reading the input file. All input files are treated + * as if they are binary, even if they contain text, it's the + * bytes that are encrypted. + */ + this.encrypt = encrypt; + try + { + in = new BufferedInputStream(new FileInputStream(infile)); + } + catch (FileNotFoundException fnf) + { + System.err.println("Input file not found ["+infile+"]"); + System.exit(1); + } + + try + { + out = new BufferedOutputStream(new FileOutputStream(outfile)); + } + catch (IOException fnf) + { + System.err.println("Output file not created ["+outfile+"]"); + System.exit(1); + } + + if (encrypt) + { + try + { + /* + * The process of creating a new key requires a + * number of steps. + * + * First, create the parameters for the key generator + * which are a secure random number generator, and + * the length of the key (in bits). + */ + SecureRandom sr = null; + try + { + sr = new SecureRandom(); + /* + * This following call to setSeed() makes the + * initialisation of the SecureRandom object + * _very_ fast, but not secure AT ALL. + * + * Remove the line, recreate the class file and + * then run DESExample again to see the difference. + * + * The initialisation of a SecureRandom object + * can take 5 or more seconds depending on the + * CPU that the program is running on. That can + * be annoying during unit testing. + * -- jon + */ + sr.setSeed("www.bouncycastle.org".getBytes()); + } + catch (Exception nsa) + { + System.err.println("Hmmm, no SHA1PRNG, you need the "+ + "Sun implementation"); + System.exit(1); + } + KeyGenerationParameters kgp = new KeyGenerationParameters( + sr, + DESedeParameters.DES_EDE_KEY_LENGTH*8); + + /* + * Second, initialise the key generator with the parameters + */ + DESedeKeyGenerator kg = new DESedeKeyGenerator(); + kg.init(kgp); + + /* + * Third, and finally, generate the key + */ + key = kg.generateKey(); + + /* + * We can now output the key to the file, but first + * hex encode the key so that we can have a look + * at it with a text editor if we so desire + */ + BufferedOutputStream keystream = + new BufferedOutputStream(new FileOutputStream(keyfile)); + byte[] keyhex = Hex.encode(key); + keystream.write(keyhex, 0, keyhex.length); + keystream.flush(); + keystream.close(); + } + catch (IOException createKey) + { + System.err.println("Could not decryption create key file "+ + "["+keyfile+"]"); + System.exit(1); + } + } + else + { + try + { + // read the key, and decode from hex encoding + BufferedInputStream keystream = + new BufferedInputStream(new FileInputStream(keyfile)); + int len = keystream.available(); + byte[] keyhex = new byte[len]; + keystream.read(keyhex, 0, len); + key = Hex.decode(keyhex); + } + catch (IOException ioe) + { + System.err.println("Decryption key file not found, "+ + "or not valid ["+keyfile+"]"); + System.exit(1); + } + } + } + + private void process() + { + /* + * Setup the DESede cipher engine, create a PaddedBufferedBlockCipher + * in CBC mode. + */ + cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(new DESedeEngine())); + + /* + * The input and output streams are currently set up + * appropriately, and the key bytes are ready to be + * used. + * + */ + + if (encrypt) + { + performEncrypt(key); + } + else + { + performDecrypt(key); + } + + // after processing clean up the files + try + { + in.close(); + out.flush(); + out.close(); + } + catch (IOException closing) + { + + } + } + + /* + * This method performs all the encryption and writes + * the cipher text to the buffered output stream created + * previously. + */ + private void performEncrypt(byte[] key) + { + // initialise the cipher with the key bytes, for encryption + cipher.init(true, new KeyParameter(key)); + + /* + * Create some temporary byte arrays for use in + * encryption, make them a reasonable size so that + * we don't spend forever reading small chunks from + * a file. + * + * There is no particular reason for using getBlockSize() + * to determine the size of the input chunk. It just + * was a convenient number for the example. + */ + // int inBlockSize = cipher.getBlockSize() * 5; + int inBlockSize = 47; + int outBlockSize = cipher.getOutputSize(inBlockSize); + + byte[] inblock = new byte[inBlockSize]; + byte[] outblock = new byte[outBlockSize]; + + /* + * now, read the file, and output the chunks + */ + try + { + int inL; + int outL; + byte[] rv = null; + while ((inL=in.read(inblock, 0, inBlockSize)) > 0) + { + outL = cipher.processBytes(inblock, 0, inL, outblock, 0); + /* + * Before we write anything out, we need to make sure + * that we've got something to write out. + */ + if (outL > 0) + { + rv = Hex.encode(outblock, 0, outL); + out.write(rv, 0, rv.length); + out.write('\n'); + } + } + + try + { + /* + * Now, process the bytes that are still buffered + * within the cipher. + */ + outL = cipher.doFinal(outblock, 0); + if (outL > 0) + { + rv = Hex.encode(outblock, 0, outL); + out.write(rv, 0, rv.length); + out.write('\n'); + } + } + catch (CryptoException ce) + { + + } + } + catch (IOException ioeread) + { + ioeread.printStackTrace(); + } + } + + /* + * This method performs all the decryption and writes + * the plain text to the buffered output stream created + * previously. + */ + private void performDecrypt(byte[] key) + { + // initialise the cipher for decryption + cipher.init(false, new KeyParameter(key)); + + /* + * As the decryption is from our preformatted file, + * and we know that it's a hex encoded format, then + * we wrap the InputStream with a BufferedReader + * so that we can read it easily. + */ + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + /* + * now, read the file, and output the chunks + */ + try + { + int outL; + byte[] inblock = null; + byte[] outblock = null; + String rv = null; + while ((rv = br.readLine()) != null) + { + inblock = Hex.decode(rv); + outblock = new byte[cipher.getOutputSize(inblock.length)]; + + outL = cipher.processBytes(inblock, 0, inblock.length, + outblock, 0); + /* + * Before we write anything out, we need to make sure + * that we've got something to write out. + */ + if (outL > 0) + { + out.write(outblock, 0, outL); + } + } + + try + { + /* + * Now, process the bytes that are still buffered + * within the cipher. + */ + outL = cipher.doFinal(outblock, 0); + if (outL > 0) + { + out.write(outblock, 0, outL); + } + } + catch (CryptoException ce) + { + + } + } + catch (IOException ioeread) + { + ioeread.printStackTrace(); + } + } + +} + diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/examples/JPAKEExample.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/examples/JPAKEExample.java new file mode 100644 index 000000000..53654f516 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/examples/JPAKEExample.java @@ -0,0 +1,214 @@ +package org.spongycastle.crypto.examples; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup; +import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroups; +import org.spongycastle.crypto.agreement.jpake.JPAKEParticipant; +import org.spongycastle.crypto.agreement.jpake.JPAKERound1Payload; +import org.spongycastle.crypto.agreement.jpake.JPAKERound2Payload; +import org.spongycastle.crypto.agreement.jpake.JPAKERound3Payload; +import org.spongycastle.crypto.digests.SHA256Digest; + +/** + * An example of a J-PAKE exchange. + *
+ *
+ * In this example, both Alice and Bob are on the same computer (in the same JVM, in fact).
+ * In reality, Alice and Bob would be in different locations,
+ * and would be sending their generated payloads to each other.
+ */
+public class JPAKEExample
+{
+
+ public static void main(String args[]) throws CryptoException
+ {
+ /*
+ * Initialization
+ *
+ * Pick an appropriate prime order group to use throughout the exchange.
+ * Note that both participants must use the same group.
+ */
+ JPAKEPrimeOrderGroup group = JPAKEPrimeOrderGroups.NIST_3072;
+
+ BigInteger p = group.getP();
+ BigInteger q = group.getQ();
+ BigInteger g = group.getG();
+
+ String alicePassword = "password";
+ String bobPassword = "password";
+
+ System.out.println("********* Initialization **********");
+ System.out.println("Public parameters for the cyclic group:");
+ System.out.println("p (" + p.bitLength() + " bits): " + p.toString(16));
+ System.out.println("q (" + q.bitLength() + " bits): " + q.toString(16));
+ System.out.println("g (" + p.bitLength() + " bits): " + g.toString(16));
+ System.out.println("p mod q = " + p.mod(q).toString(16));
+ System.out.println("g^{q} mod p = " + g.modPow(q, p).toString(16));
+ System.out.println("");
+
+ System.out.println("(Secret passwords used by Alice and Bob: " +
+ "\"" + alicePassword + "\" and \"" + bobPassword + "\")\n");
+
+ /*
+ * Both participants must use the same hashing algorithm.
+ */
+ Digest digest = new SHA256Digest();
+ SecureRandom random = new SecureRandom();
+
+ JPAKEParticipant alice = new JPAKEParticipant("alice", alicePassword.toCharArray(), group, digest, random);
+ JPAKEParticipant bob = new JPAKEParticipant("bob", bobPassword.toCharArray(), group, digest, random);
+
+ /*
+ * Round 1
+ *
+ * Alice and Bob each generate a round 1 payload, and send it to each other.
+ */
+
+ JPAKERound1Payload aliceRound1Payload = alice.createRound1PayloadToSend();
+ JPAKERound1Payload bobRound1Payload = bob.createRound1PayloadToSend();
+
+ System.out.println("************ Round 1 **************");
+ System.out.println("Alice sends to Bob: ");
+ System.out.println("g^{x1}=" + aliceRound1Payload.getGx1().toString(16));
+ System.out.println("g^{x2}=" + aliceRound1Payload.getGx2().toString(16));
+ System.out.println("KP{x1}={" + aliceRound1Payload.getKnowledgeProofForX1()[0].toString(16) + "};{" + aliceRound1Payload.getKnowledgeProofForX1()[1].toString(16) + "}");
+ System.out.println("KP{x2}={" + aliceRound1Payload.getKnowledgeProofForX2()[0].toString(16) + "};{" + aliceRound1Payload.getKnowledgeProofForX2()[1].toString(16) + "}");
+ System.out.println("");
+
+ System.out.println("Bob sends to Alice: ");
+ System.out.println("g^{x3}=" + bobRound1Payload.getGx1().toString(16));
+ System.out.println("g^{x4}=" + bobRound1Payload.getGx2().toString(16));
+ System.out.println("KP{x3}={" + bobRound1Payload.getKnowledgeProofForX1()[0].toString(16) + "};{" + bobRound1Payload.getKnowledgeProofForX1()[1].toString(16) + "}");
+ System.out.println("KP{x4}={" + bobRound1Payload.getKnowledgeProofForX2()[0].toString(16) + "};{" + bobRound1Payload.getKnowledgeProofForX2()[1].toString(16) + "}");
+ System.out.println("");
+
+ /*
+ * Each participant must then validate the received payload for round 1
+ */
+
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+ System.out.println("Alice checks g^{x4}!=1: OK");
+ System.out.println("Alice checks KP{x3}: OK");
+ System.out.println("Alice checks KP{x4}: OK");
+ System.out.println("");
+
+ bob.validateRound1PayloadReceived(aliceRound1Payload);
+ System.out.println("Bob checks g^{x2}!=1: OK");
+ System.out.println("Bob checks KP{x1},: OK");
+ System.out.println("Bob checks KP{x2},: OK");
+ System.out.println("");
+
+ /*
+ * Round 2
+ *
+ * Alice and Bob each generate a round 2 payload, and send it to each other.
+ */
+
+ JPAKERound2Payload aliceRound2Payload = alice.createRound2PayloadToSend();
+ JPAKERound2Payload bobRound2Payload = bob.createRound2PayloadToSend();
+
+ System.out.println("************ Round 2 **************");
+ System.out.println("Alice sends to Bob: ");
+ System.out.println("A=" + aliceRound2Payload.getA().toString(16));
+ System.out.println("KP{x2*s}={" + aliceRound2Payload.getKnowledgeProofForX2s()[0].toString(16) + "},{" + aliceRound2Payload.getKnowledgeProofForX2s()[1].toString(16) + "}");
+ System.out.println("");
+
+ System.out.println("Bob sends to Alice");
+ System.out.println("B=" + bobRound2Payload.getA().toString(16));
+ System.out.println("KP{x4*s}={" + bobRound2Payload.getKnowledgeProofForX2s()[0].toString(16) + "},{" + bobRound2Payload.getKnowledgeProofForX2s()[1].toString(16) + "}");
+ System.out.println("");
+
+ /*
+ * Each participant must then validate the received payload for round 2
+ */
+
+ alice.validateRound2PayloadReceived(bobRound2Payload);
+ System.out.println("Alice checks KP{x4*s}: OK\n");
+
+ bob.validateRound2PayloadReceived(aliceRound2Payload);
+ System.out.println("Bob checks KP{x2*s}: OK\n");
+
+ /*
+ * After round 2, each participant computes the keying material.
+ */
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ System.out.println("********* After round 2 ***********");
+ System.out.println("Alice computes key material \t K=" + aliceKeyingMaterial.toString(16));
+ System.out.println("Bob computes key material \t K=" + bobKeyingMaterial.toString(16));
+ System.out.println();
+
+
+ /*
+ * You must derive a session key from the keying material applicable
+ * to whatever encryption algorithm you want to use.
+ */
+
+ BigInteger aliceKey = deriveSessionKey(aliceKeyingMaterial);
+ BigInteger bobKey = deriveSessionKey(bobKeyingMaterial);
+
+ /*
+ * At this point, you can stop and use the session keys if you want.
+ * This is implicit key confirmation.
+ *
+ * If you want to explicitly confirm that the key material matches,
+ * you can continue on and perform round 3.
+ */
+
+ /*
+ * Round 3
+ *
+ * Alice and Bob each generate a round 3 payload, and send it to each other.
+ */
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ System.out.println("************ Round 3 **************");
+ System.out.println("Alice sends to Bob: ");
+ System.out.println("MacTag=" + aliceRound3Payload.getMacTag().toString(16));
+ System.out.println("");
+ System.out.println("Bob sends to Alice: ");
+ System.out.println("MacTag=" + bobRound3Payload.getMacTag().toString(16));
+ System.out.println("");
+
+ /*
+ * Each participant must then validate the received payload for round 3
+ */
+
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ System.out.println("Alice checks MacTag: OK\n");
+
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+ System.out.println("Bob checks MacTag: OK\n");
+
+ System.out.println();
+ System.out.println("MacTags validated, therefore the keying material matches.");
+ }
+
+ private static BigInteger deriveSessionKey(BigInteger keyingMaterial)
+ {
+ /*
+ * You should use a secure key derivation function (KDF) to derive the session key.
+ *
+ * For the purposes of this example, I'm just going to use a hash of the keying material.
+ */
+ SHA256Digest digest = new SHA256Digest();
+
+ byte[] keyByteArray = keyingMaterial.toByteArray();
+
+ byte[] output = new byte[digest.getDigestSize()];
+
+ digest.update(keyByteArray, 0, keyByteArray.length);
+
+ digest.doFinal(output, 0);
+
+ return new BigInteger(output);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/BaseKDFBytesGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/BaseKDFBytesGenerator.java
new file mode 100644
index 000000000..fc0faf50b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/BaseKDFBytesGenerator.java
@@ -0,0 +1,143 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.DigestDerivationFunction;
+import org.spongycastle.crypto.params.ISO18033KDFParameters;
+import org.spongycastle.crypto.params.KDFParameters;
+import org.spongycastle.crypto.util.Pack;
+
+/**
+ * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO
+ * 18033
+ * This implementation is based on ISO 18033/P1363a.
+ */
+public class BaseKDFBytesGenerator
+ implements DigestDerivationFunction
+{
+ private int counterStart;
+ private Digest digest;
+ private byte[] shared;
+ private byte[] iv;
+
+ /**
+ * Construct a KDF Parameters generator.
+ *
+ * + * @param counterStart + * value of counter. + * @param digest + * the digest to be used as the source of derived keys. + */ + protected BaseKDFBytesGenerator(int counterStart, Digest digest) + { + this.counterStart = counterStart; + this.digest = digest; + } + + public void init(DerivationParameters param) + { + if (param instanceof KDFParameters) + { + KDFParameters p = (KDFParameters)param; + + shared = p.getSharedSecret(); + iv = p.getIV(); + } + else if (param instanceof ISO18033KDFParameters) + { + ISO18033KDFParameters p = (ISO18033KDFParameters)param; + + shared = p.getSeed(); + iv = null; + } + else + { + throw new IllegalArgumentException("KDF parameters required for KDF2Generator"); + } + } + + /** + * return the underlying digest. + */ + public Digest getDigest() + { + return digest; + } + + /** + * fill len bytes of the output buffer with bytes generated from the + * derivation function. + * + * @throws IllegalArgumentException + * if the size of the request will cause an overflow. + * @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"); + } + + long oBytes = len; + int outLen = digest.getDigestSize(); + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) + { + throw new IllegalArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.getDigestSize()]; + + byte[] C = new byte[4]; + Pack.intToBigEndian(counterStart, C, 0); + + int counterBase = counterStart & ~0xFF; + + for (int i = 0; i < cThreshold; i++) + { + digest.update(shared, 0, shared.length); + digest.update(C, 0, C.length); + + if (iv != null) + { + digest.update(iv, 0, iv.length); + } + + digest.doFinal(dig, 0); + + if (len > outLen) + { + System.arraycopy(dig, 0, out, outOff, outLen); + outOff += outLen; + len -= outLen; + } + else + { + System.arraycopy(dig, 0, out, outOff, len); + } + + if (++C[3] == 0) + { + counterBase += 0x100; + Pack.intToBigEndian(counterBase, C, 0); + } + } + + digest.reset(); + + return (int)oBytes; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DESKeyGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DESKeyGenerator.java new file mode 100644 index 000000000..ef6894f52 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DESKeyGenerator.java @@ -0,0 +1,48 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.CipherKeyGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.DESParameters; + +public class DESKeyGenerator + extends CipherKeyGenerator +{ + /** + * initialise the key generator - if strength is set to zero + * the key generated will be 64 bits in size, otherwise + * strength can be 64 or 56 bits (if you don't count the parity bits). + * + * @param param the parameters to be used for key generation + */ + public void init( + KeyGenerationParameters param) + { + super.init(param); + + if (strength == 0 || strength == (56 / 8)) + { + strength = DESParameters.DES_KEY_LENGTH; + } + else if (strength != DESParameters.DES_KEY_LENGTH) + { + throw new IllegalArgumentException("DES key must be " + + (DESParameters.DES_KEY_LENGTH * 8) + + " bits long."); + } + } + + public byte[] generateKey() + { + byte[] newKey = new byte[DESParameters.DES_KEY_LENGTH]; + + do + { + random.nextBytes(newKey); + + DESParameters.setOddParity(newKey); + } + while (DESParameters.isWeakKey(newKey, 0)); + + return newKey; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DESedeKeyGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DESedeKeyGenerator.java new file mode 100644 index 000000000..c22cc172d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DESedeKeyGenerator.java @@ -0,0 +1,56 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.DESedeParameters; + +public class DESedeKeyGenerator + extends DESKeyGenerator +{ + /** + * initialise the key generator - if strength is set to zero + * the key generated will be 192 bits in size, otherwise + * strength can be 128 or 192 (or 112 or 168 if you don't count + * parity bits), depending on whether you wish to do 2-key or 3-key + * triple DES. + * + * @param param the parameters to be used for key generation + */ + public void init( + KeyGenerationParameters param) + { + this.random = param.getRandom(); + this.strength = (param.getStrength() + 7) / 8; + + if (strength == 0 || strength == (168 / 8)) + { + strength = DESedeParameters.DES_EDE_KEY_LENGTH; + } + else if (strength == (112 / 8)) + { + strength = 2 * DESedeParameters.DES_KEY_LENGTH; + } + else if (strength != DESedeParameters.DES_EDE_KEY_LENGTH + && strength != (2 * DESedeParameters.DES_KEY_LENGTH)) + { + throw new IllegalArgumentException("DESede key must be " + + (DESedeParameters.DES_EDE_KEY_LENGTH * 8) + " or " + + (2 * 8 * DESedeParameters.DES_KEY_LENGTH) + + " bits long."); + } + } + + public byte[] generateKey() + { + byte[] newKey = new byte[strength]; + + do + { + random.nextBytes(newKey); + + DESedeParameters.setOddParity(newKey); + } + while (DESedeParameters.isWeakKey(newKey, 0, newKey.length)); + + return newKey; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHBasicKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHBasicKeyPairGenerator.java new file mode 100644 index 000000000..be5e34f87 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHBasicKeyPairGenerator.java @@ -0,0 +1,42 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.DHKeyGenerationParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; + +import java.math.BigInteger; + +/** + * a basic Diffie-Hellman key pair generator. + * + * This generates keys consistent for use with the basic algorithm for + * Diffie-Hellman. + */ +public class DHBasicKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + private DHKeyGenerationParameters param; + + public void init( + KeyGenerationParameters param) + { + this.param = (DHKeyGenerationParameters)param; + } + + public AsymmetricCipherKeyPair generateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE; + DHParameters dhp = param.getParameters(); + + BigInteger x = helper.calculatePrivate(dhp, param.getRandom()); + BigInteger y = helper.calculatePublic(dhp, x); + + return new AsymmetricCipherKeyPair( + new DHPublicKeyParameters(y, dhp), + new DHPrivateKeyParameters(x, dhp)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHKeyGeneratorHelper.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHKeyGeneratorHelper.java new file mode 100644 index 000000000..b987e5b1c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHKeyGeneratorHelper.java @@ -0,0 +1,51 @@ +package org.spongycastle.crypto.generators; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.util.BigIntegers; + +class DHKeyGeneratorHelper +{ + static final DHKeyGeneratorHelper INSTANCE = new DHKeyGeneratorHelper(); + + private static final BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger TWO = BigInteger.valueOf(2); + + private DHKeyGeneratorHelper() + { + } + + BigInteger calculatePrivate(DHParameters dhParams, SecureRandom random) + { + BigInteger p = dhParams.getP(); + int limit = dhParams.getL(); + + if (limit != 0) + { + return new BigInteger(limit, random).setBit(limit - 1); + } + + BigInteger min = TWO; + int m = dhParams.getM(); + if (m != 0) + { + min = ONE.shiftLeft(m - 1); + } + + BigInteger max = p.subtract(TWO); + BigInteger q = dhParams.getQ(); + if (q != null) + { + max = q.subtract(TWO); + } + + return BigIntegers.createRandomInRange(min, max, random); + } + + BigInteger calculatePublic(DHParameters dhParams, BigInteger x) + { + return dhParams.getG().modPow(x, dhParams.getP()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHKeyPairGenerator.java new file mode 100644 index 000000000..f87b416fa --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHKeyPairGenerator.java @@ -0,0 +1,42 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.DHKeyGenerationParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; + +import java.math.BigInteger; + +/** + * a Diffie-Hellman key pair generator. + * + * This generates keys consistent for use in the MTI/A0 key agreement protocol + * as described in "Handbook of Applied Cryptography", Pages 516-519. + */ +public class DHKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + private DHKeyGenerationParameters param; + + public void init( + KeyGenerationParameters param) + { + this.param = (DHKeyGenerationParameters)param; + } + + public AsymmetricCipherKeyPair generateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE; + DHParameters dhp = param.getParameters(); + + BigInteger x = helper.calculatePrivate(dhp, param.getRandom()); + BigInteger y = helper.calculatePublic(dhp, x); + + return new AsymmetricCipherKeyPair( + new DHPublicKeyParameters(y, dhp), + new DHPrivateKeyParameters(x, dhp)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHParametersGenerator.java new file mode 100644 index 000000000..894c3804b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHParametersGenerator.java @@ -0,0 +1,52 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.params.DHParameters; + +import java.math.BigInteger; +import java.security.SecureRandom; + +public class DHParametersGenerator +{ + private int size; + private int certainty; + private SecureRandom random; + + private static final BigInteger TWO = BigInteger.valueOf(2); + + /** + * Initialise the parameters generator. + * + * @param size bit length for the prime p + * @param certainty level of certainty for the prime number tests + * @param random a source of randomness + */ + public void init( + int size, + int certainty, + SecureRandom random) + { + this.size = size; + this.certainty = certainty; + this.random = random; + } + + /** + * which generates the p and g values from the given parameters, + * returning the DHParameters object. + *
+ * Note: can take a while... + */ + public DHParameters generateParameters() + { + // + // find a safe prime p where p = 2*q + 1, where p and q are prime. + // + BigInteger[] safePrimes = DHParametersHelper.generateSafePrimes(size, certainty, random); + + BigInteger p = safePrimes[0]; + BigInteger q = safePrimes[1]; + BigInteger g = DHParametersHelper.selectGenerator(p, q, random); + + return new DHParameters(p, g, q, TWO, null); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHParametersHelper.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHParametersHelper.java new file mode 100644 index 000000000..af6db5012 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DHParametersHelper.java @@ -0,0 +1,73 @@ +package org.spongycastle.crypto.generators; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.util.BigIntegers; + +class DHParametersHelper +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger TWO = BigInteger.valueOf(2); + + /* + * Finds a pair of prime BigInteger's {p, q: p = 2q + 1} + * + * (see: Handbook of Applied Cryptography 4.86) + */ + static BigInteger[] generateSafePrimes(int size, int certainty, SecureRandom random) + { + BigInteger p, q; + int qLength = size - 1; + + for (;;) + { + q = new BigInteger(qLength, 2, random); + + // p <- 2q + 1 + p = q.shiftLeft(1).add(ONE); + + if (p.isProbablePrime(certainty) && (certainty <= 2 || q.isProbablePrime(certainty))) + { + break; + } + } + + return new BigInteger[] { p, q }; + } + + /* + * Select a high order element of the multiplicative group Zp* + * + * p and q must be s.t. p = 2*q + 1, where p and q are prime (see generateSafePrimes) + */ + static BigInteger selectGenerator(BigInteger p, BigInteger q, SecureRandom random) + { + BigInteger pMinusTwo = p.subtract(TWO); + BigInteger g; + + /* + * (see: Handbook of Applied Cryptography 4.80) + */ +// do +// { +// g = BigIntegers.createRandomInRange(TWO, pMinusTwo, random); +// } +// while (g.modPow(TWO, p).equals(ONE) || g.modPow(q, p).equals(ONE)); + + + /* + * RFC 2631 2.2.1.2 (and see: Handbook of Applied Cryptography 4.81) + */ + do + { + BigInteger h = BigIntegers.createRandomInRange(TWO, pMinusTwo, random); + + g = h.modPow(TWO, p); + } + while (g.equals(ONE)); + + + return g; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSAKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSAKeyPairGenerator.java new file mode 100644 index 000000000..609f2f3a1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSAKeyPairGenerator.java @@ -0,0 +1,61 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.DSAKeyGenerationParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * a DSA key pair generator. + * + * This generates DSA keys in line with the method described + * in FIPS 186-3 B.1 FFC Key Pair Generation. + */ +public class DSAKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + + private DSAKeyGenerationParameters param; + + public void init( + KeyGenerationParameters param) + { + this.param = (DSAKeyGenerationParameters)param; + } + + public AsymmetricCipherKeyPair generateKeyPair() + { + DSAParameters dsaParams = param.getParameters(); + + BigInteger x = generatePrivateKey(dsaParams.getQ(), param.getRandom()); + BigInteger y = calculatePublicKey(dsaParams.getP(), dsaParams.getG(), x); + + return new AsymmetricCipherKeyPair( + new DSAPublicKeyParameters(y, dsaParams), + new DSAPrivateKeyParameters(x, dsaParams)); + } + + private static BigInteger generatePrivateKey(BigInteger q, SecureRandom random) + { + // TODO Prefer this method? (change test cases that used fixed random) + // B.1.1 Key Pair Generation Using Extra Random Bits +// BigInteger c = new BigInteger(q.bitLength() + 64, random); +// return c.mod(q.subtract(ONE)).add(ONE); + + // B.1.2 Key Pair Generation by Testing Candidates + return BigIntegers.createRandomInRange(ONE, q.subtract(ONE), random); + } + + private static BigInteger calculatePublicKey(BigInteger p, BigInteger g, BigInteger x) + { + return g.modPow(x, p); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSAParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSAParametersGenerator.java new file mode 100644 index 000000000..d89a776eb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSAParametersGenerator.java @@ -0,0 +1,387 @@ +package org.spongycastle.crypto.generators; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.params.DSAParameterGenerationParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAValidationParameters; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.encoders.Hex; + +/** + * Generate suitable parameters for DSA, in line with FIPS 186-2, or FIPS 186-3. + */ +public class DSAParametersGenerator +{ + private Digest digest; + private int L, N; + private int certainty; + private SecureRandom random; + + private static final BigInteger ZERO = BigInteger.valueOf(0); + private static final BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger TWO = BigInteger.valueOf(2); + + private boolean use186_3; + private int usageIndex; + + public DSAParametersGenerator() + { + this(new SHA1Digest()); + } + + public DSAParametersGenerator(Digest digest) + { + this.digest = digest; + } + + /** + * initialise the key generator. + * + * @param size size of the key (range 2^512 -> 2^1024 - 64 bit increments) + * @param certainty measure of robustness of prime (for FIPS 186-2 compliance this should be at least 80). + * @param random random byte source. + */ + public void init( + int size, + int certainty, + SecureRandom random) + { + this.use186_3 = false; + this.L = size; + this.N = getDefaultN(size); + this.certainty = certainty; + this.random = random; + } + + /** + * Initialise the key generator for DSA 2. + *
+ * Use this init method if you need to generate parameters for DSA 2 keys. + *
+ * + * @param params DSA 2 key generation parameters. + */ + public void init( + DSAParameterGenerationParameters params) + { + // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1? + this.use186_3 = true; + this.L = params.getL(); + this.N = params.getN(); + this.certainty = params.getCertainty(); + this.random = params.getRandom(); + this.usageIndex = params.getUsageIndex(); + + if ((L < 1024 || L > 3072) || L % 1024 != 0) + { + throw new IllegalArgumentException("L values must be between 1024 and 3072 and a multiple of 1024"); + } + else if (L == 1024 && N != 160) + { + throw new IllegalArgumentException("N must be 160 for L = 1024"); + } + else if (L == 2048 && (N != 224 && N != 256)) + { + throw new IllegalArgumentException("N must be 224 or 256 for L = 2048"); + } + else if (L == 3072 && N != 256) + { + throw new IllegalArgumentException("N must be 256 for L = 3072"); + } + + if (digest.getDigestSize() * 8 < N) + { + throw new IllegalStateException("Digest output size too small for value of N"); + } + } + + /** + * which generates the p and g values from the given parameters, + * returning the DSAParameters object. + *+ * Note: can take a while... + */ + public DSAParameters generateParameters() + { + return (use186_3) + ? generateParameters_FIPS186_3() + : generateParameters_FIPS186_2(); + } + + private DSAParameters generateParameters_FIPS186_2() + { + byte[] seed = new byte[20]; + byte[] part1 = new byte[20]; + byte[] part2 = new byte[20]; + byte[] u = new byte[20]; + int n = (L - 1) / 160; + byte[] w = new byte[L / 8]; + + if (!(digest instanceof SHA1Digest)) + { + throw new IllegalStateException("can only use SHA-1 for generating FIPS 186-2 parameters"); + } + + for (;;) + { + random.nextBytes(seed); + + hash(digest, seed, part1); + System.arraycopy(seed, 0, part2, 0, seed.length); + inc(part2); + hash(digest, part2, part2); + + for (int i = 0; i != u.length; i++) + { + u[i] = (byte)(part1[i] ^ part2[i]); + } + + u[0] |= (byte)0x80; + u[19] |= (byte)0x01; + + BigInteger q = new BigInteger(1, u); + + if (!q.isProbablePrime(certainty)) + { + continue; + } + + byte[] offset = Arrays.clone(seed); + inc(offset); + + for (int counter = 0; counter < 4096; ++counter) + { + for (int k = 0; k < n; k++) + { + inc(offset); + hash(digest, offset, part1); + System.arraycopy(part1, 0, w, w.length - (k + 1) * part1.length, part1.length); + } + + inc(offset); + hash(digest, offset, part1); + System.arraycopy(part1, part1.length - ((w.length - (n) * part1.length)), w, 0, w.length - n * part1.length); + + w[0] |= (byte)0x80; + + BigInteger x = new BigInteger(1, w); + + BigInteger c = x.mod(q.shiftLeft(1)); + + BigInteger p = x.subtract(c.subtract(ONE)); + + if (p.bitLength() != L) + { + continue; + } + + if (p.isProbablePrime(certainty)) + { + BigInteger g = calculateGenerator_FIPS186_2(p, q, random); + + return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter)); + } + } + } + } + + private static BigInteger calculateGenerator_FIPS186_2(BigInteger p, BigInteger q, SecureRandom r) + { + BigInteger e = p.subtract(ONE).divide(q); + BigInteger pSub2 = p.subtract(TWO); + + for (;;) + { + BigInteger h = BigIntegers.createRandomInRange(TWO, pSub2, r); + BigInteger g = h.modPow(e, p); + if (g.bitLength() > 1) + { + return g; + } + } + } + + /** + * generate suitable parameters for DSA, in line with + * FIPS 186-3 A.1 Generation of the FFC Primes p and q. + */ + private DSAParameters generateParameters_FIPS186_3() + { +// A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function + // FIXME This should be configurable (digest size in bits must be >= N) + Digest d = digest; + int outlen = d.getDigestSize() * 8; + +// 1. Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2). If +// the pair is not in the list, then return INVALID. + // Note: checked at initialisation + +// 2. If (seedlen < N), then return INVALID. + // FIXME This should be configurable (must be >= N) + int seedlen = N; + byte[] seed = new byte[seedlen / 8]; + +// 3. n = ceiling(L / outlen) - 1. + int n = (L - 1) / outlen; + +// 4. b = L - 1 - (n * outlen). + int b = (L - 1) % outlen; + + byte[] output = new byte[d.getDigestSize()]; + for (;;) + { +// 5. Get an arbitrary sequence of seedlen bits as the domain_parameter_seed. + random.nextBytes(seed); + +// 6. U = Hash (domain_parameter_seed) mod 2^(Nâ1). + hash(d, seed, output); + + BigInteger U = new BigInteger(1, output).mod(ONE.shiftLeft(N - 1)); + +// 7. q = 2^(Nâ1) + U + 1 â ( U mod 2). + BigInteger q = ONE.shiftLeft(N - 1).add(U).add(ONE).subtract(U.mod(TWO)); + +// 8. Test whether or not q is prime as specified in Appendix C.3. + // TODO Review C.3 for primality checking + if (!q.isProbablePrime(certainty)) + { +// 9. If q is not a prime, then go to step 5. + continue; + } + +// 10. offset = 1. + // Note: 'offset' value managed incrementally + byte[] offset = Arrays.clone(seed); + +// 11. For counter = 0 to (4L â 1) do + int counterLimit = 4 * L; + for (int counter = 0; counter < counterLimit; ++counter) + { +// 11.1 For j = 0 to n do +// Vj = Hash ((domain_parameter_seed + offset + j) mod 2^seedlen). +// 11.2 W = V0 + (V1 â 2^outlen) + ... + (V^(nâ1) â 2^((nâ1) â outlen)) + ((Vn mod 2^b) â 2^(n â outlen)). + // TODO Assemble w as a byte array + BigInteger W = ZERO; + for (int j = 0, exp = 0; j <= n; ++j, exp += outlen) + { + inc(offset); + hash(d, offset, output); + + BigInteger Vj = new BigInteger(1, output); + if (j == n) + { + Vj = Vj.mod(ONE.shiftLeft(b)); + } + + W = W.add(Vj.shiftLeft(exp)); + } + +// 11.3 X = W + 2^(Lâ1). Comment: 0 †W < 2Lâ1; hence, 2Lâ1 †X < 2L. + BigInteger X = W.add(ONE.shiftLeft(L - 1)); + +// 11.4 c = X mod 2q. + BigInteger c = X.mod(q.shiftLeft(1)); + +// 11.5 p = X - (c - 1). Comment: p ⥠1 (mod 2q). + BigInteger p = X.subtract(c.subtract(ONE)); + +// 11.6 If (p < 2^(L - 1)), then go to step 11.9 + if (p.bitLength() != L) + { + continue; + } + +// 11.7 Test whether or not p is prime as specified in Appendix C.3. + // TODO Review C.3 for primality checking + if (p.isProbablePrime(certainty)) + { +// 11.8 If p is determined to be prime, then return VALID and the values of p, q and +// (optionally) the values of domain_parameter_seed and counter. + if (usageIndex >= 0) + { + BigInteger g = calculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, usageIndex); + if (g != null) + { + return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter, usageIndex)); + } + } + + BigInteger g = calculateGenerator_FIPS186_3_Unverifiable(p, q, random); + + return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter)); + } + +// 11.9 offset = offset + n + 1. Comment: Increment offset; then, as part of +// the loop in step 11, increment counter; if +// counter < 4L, repeat steps 11.1 through 11.8. + // Note: 'offset' value already incremented in inner loop + } +// 12. Go to step 5. + } + } + + private static BigInteger calculateGenerator_FIPS186_3_Unverifiable(BigInteger p, BigInteger q, + SecureRandom r) + { + return calculateGenerator_FIPS186_2(p, q, r); + } + + private static BigInteger calculateGenerator_FIPS186_3_Verifiable(Digest d, BigInteger p, BigInteger q, + byte[] seed, int index) + { +// A.2.3 Verifiable Canonical Generation of the Generator g + BigInteger e = p.subtract(ONE).divide(q); + byte[] ggen = Hex.decode("6767656E"); + + // 7. U = domain_parameter_seed || "ggen" || index || count. + byte[] U = new byte[seed.length + ggen.length + 1 + 2]; + System.arraycopy(seed, 0, U, 0, seed.length); + System.arraycopy(ggen, 0, U, seed.length, ggen.length); + U[U.length - 3] = (byte)index; + + byte[] w = new byte[d.getDigestSize()]; + for (int count = 1; count < (1 << 16); ++count) + { + inc(U); + hash(d, U, w); + BigInteger W = new BigInteger(1, w); + BigInteger g = W.modPow(e, p); + if (g.compareTo(TWO) >= 0) + { + return g; + } + } + + return null; + } + + private static void hash(Digest d, byte[] input, byte[] output) + { + d.update(input, 0, input.length); + d.doFinal(output, 0); + } + + private static int getDefaultN(int L) + { + return L > 1024 ? 256 : 160; + } + + private static void inc(byte[] buf) + { + for (int i = buf.length - 1; i >= 0; --i) + { + byte b = (byte)((buf[i] + 1) & 0xff); + buf[i] = b; + + if (b != 0) + { + break; + } + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSTU4145KeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSTU4145KeyPairGenerator.java new file mode 100644 index 000000000..47740485d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/DSTU4145KeyPairGenerator.java @@ -0,0 +1,21 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; + +public class DSTU4145KeyPairGenerator + extends ECKeyPairGenerator +{ + public AsymmetricCipherKeyPair generateKeyPair() + { + AsymmetricCipherKeyPair pair = super.generateKeyPair(); + + ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate(); + + pub = new ECPublicKeyParameters(pub.getQ().negate(), pub.getParameters()); + + return new AsymmetricCipherKeyPair(pub, priv); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ECKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ECKeyPairGenerator.java new file mode 100644 index 000000000..1b59a7b60 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ECKeyPairGenerator.java @@ -0,0 +1,58 @@ +package org.spongycastle.crypto.generators; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.math.ec.ECConstants; +import org.spongycastle.math.ec.ECPoint; + +public class ECKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator, ECConstants +{ + ECDomainParameters params; + SecureRandom random; + + public void init( + KeyGenerationParameters param) + { + ECKeyGenerationParameters ecP = (ECKeyGenerationParameters)param; + + this.random = ecP.getRandom(); + this.params = ecP.getDomainParameters(); + + if (this.random == null) + { + this.random = new SecureRandom(); + } + } + + /** + * Given the domain parameters this routine generates an EC key + * pair in accordance with X9.62 section 5.2.1 pages 26, 27. + */ + public AsymmetricCipherKeyPair generateKeyPair() + { + BigInteger n = params.getN(); + int nBitLength = n.bitLength(); + BigInteger d; + + do + { + d = new BigInteger(nBitLength, random); + } + while (d.equals(ZERO) || (d.compareTo(n) >= 0)); + + ECPoint Q = params.getG().multiply(d); + + return new AsymmetricCipherKeyPair( + new ECPublicKeyParameters(Q, params), + new ECPrivateKeyParameters(d, params)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ElGamalKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ElGamalKeyPairGenerator.java new file mode 100644 index 000000000..cb0a9fcf5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ElGamalKeyPairGenerator.java @@ -0,0 +1,44 @@ +package org.spongycastle.crypto.generators; + +import java.math.BigInteger; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.ElGamalKeyGenerationParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; + +/** + * a ElGamal key pair generator. + *
+ * This generates keys consistent for use with ElGamal as described in + * page 164 of "Handbook of Applied Cryptography". + */ +public class ElGamalKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + private ElGamalKeyGenerationParameters param; + + public void init( + KeyGenerationParameters param) + { + this.param = (ElGamalKeyGenerationParameters)param; + } + + public AsymmetricCipherKeyPair generateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE; + ElGamalParameters egp = param.getParameters(); + DHParameters dhp = new DHParameters(egp.getP(), egp.getG(), null, egp.getL()); + + BigInteger x = helper.calculatePrivate(dhp, param.getRandom()); + BigInteger y = helper.calculatePublic(dhp, x); + + return new AsymmetricCipherKeyPair( + new ElGamalPublicKeyParameters(y, egp), + new ElGamalPrivateKeyParameters(x, egp)); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ElGamalParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ElGamalParametersGenerator.java new file mode 100644 index 000000000..863a8f4a9 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/ElGamalParametersGenerator.java @@ -0,0 +1,43 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.params.ElGamalParameters; + +import java.math.BigInteger; +import java.security.SecureRandom; + +public class ElGamalParametersGenerator +{ + private int size; + private int certainty; + private SecureRandom random; + + public void init( + int size, + int certainty, + SecureRandom random) + { + this.size = size; + this.certainty = certainty; + this.random = random; + } + + /** + * which generates the p and g values from the given parameters, + * returning the ElGamalParameters object. + *
+ * Note: can take a while...
+ */
+ public ElGamalParameters generateParameters()
+ {
+ //
+ // find a safe prime p where p = 2*q + 1, where p and q are prime.
+ //
+ BigInteger[] safePrimes = DHParametersHelper.generateSafePrimes(size, certainty, random);
+
+ BigInteger p = safePrimes[0];
+ BigInteger q = safePrimes[1];
+ BigInteger g = DHParametersHelper.selectGenerator(p, q, random);
+
+ return new ElGamalParameters(p, g);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/EphemeralKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/EphemeralKeyPairGenerator.java
new file mode 100644
index 000000000..d3048b3be
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/EphemeralKeyPairGenerator.java
@@ -0,0 +1,26 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.EphemeralKeyPair;
+import org.spongycastle.crypto.KeyEncoder;
+
+public class EphemeralKeyPairGenerator
+{
+ private AsymmetricCipherKeyPairGenerator gen;
+ private KeyEncoder keyEncoder;
+
+ public EphemeralKeyPairGenerator(AsymmetricCipherKeyPairGenerator gen, KeyEncoder keyEncoder)
+ {
+ this.gen = gen;
+ this.keyEncoder = keyEncoder;
+ }
+
+ public EphemeralKeyPair generate()
+ {
+ AsymmetricCipherKeyPair eph = gen.generateKeyPair();
+
+ // Encode the ephemeral public key
+ return new EphemeralKeyPair(eph, keyEncoder);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/GOST3410KeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/GOST3410KeyPairGenerator.java
new file mode 100644
index 000000000..55bcdbca9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/GOST3410KeyPairGenerator.java
@@ -0,0 +1,57 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.params.GOST3410KeyGenerationParameters;
+import org.spongycastle.crypto.params.GOST3410Parameters;
+import org.spongycastle.crypto.params.GOST3410PrivateKeyParameters;
+import org.spongycastle.crypto.params.GOST3410PublicKeyParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * a GOST3410 key pair generator.
+ * This generates GOST3410 keys in line with the method described
+ * in GOST R 34.10-94.
+ */
+public class GOST3410KeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+ {
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private GOST3410KeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (GOST3410KeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ BigInteger p, q, a, x, y;
+ GOST3410Parameters GOST3410Params = param.getParameters();
+ SecureRandom random = param.getRandom();
+
+ q = GOST3410Params.getQ();
+ p = GOST3410Params.getP();
+ a = GOST3410Params.getA();
+
+ do
+ {
+ x = new BigInteger(256, random);
+ }
+ while (x.equals(ZERO) || x.compareTo(q) >= 0);
+
+ //
+ // calculate the public key.
+ //
+ y = a.modPow(x, p);
+
+ return new AsymmetricCipherKeyPair(
+ new GOST3410PublicKeyParameters(y, GOST3410Params),
+ new GOST3410PrivateKeyParameters(x, GOST3410Params));
+ }
+ }
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/GOST3410ParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/GOST3410ParametersGenerator.java
new file mode 100644
index 000000000..c4e57127c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/GOST3410ParametersGenerator.java
@@ -0,0 +1,541 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.params.GOST3410Parameters;
+import org.spongycastle.crypto.params.GOST3410ValidationParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * generate suitable parameters for GOST3410.
+ */
+public class GOST3410ParametersGenerator
+{
+ private int size;
+ private int typeproc;
+ private SecureRandom init_random;
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /**
+ * initialise the key generator.
+ *
+ * @param size size of the key
+ * @param typeproc type procedure A,B = 1; A',B' - else
+ * @param random random byte source.
+ */
+ public void init(
+ int size,
+ int typeproc,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.typeproc = typeproc;
+ this.init_random = random;
+ }
+
+ //Procedure A
+ private int procedure_A(int x0, int c, BigInteger[] pq, int size)
+ {
+ //Verify and perform condition: 0
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public KDF1BytesGenerator(
+ Digest digest)
+ {
+ super(0, digest);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDF2BytesGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDF2BytesGenerator.java
new file mode 100644
index 000000000..bcdfc096f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDF2BytesGenerator.java
@@ -0,0 +1,24 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * KDF2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public KDF2BytesGenerator(
+ Digest digest)
+ {
+ super(1, digest);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFCounterBytesGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFCounterBytesGenerator.java
new file mode 100644
index 000000000..6341b16f9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFCounterBytesGenerator.java
@@ -0,0 +1,152 @@
+package org.spongycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.MacDerivationFunction;
+import org.spongycastle.crypto.params.KDFCounterParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFCounterBytesGenerator
+ implements MacDerivationFunction
+{
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // please refer to the standard for the meaning of the variable names
+ // all field lengths are in bytes, not in bits as specified by the standard
+
+ // fields set by the constructor
+ private final Mac prf;
+ private final int h;
+
+ // fields set by init
+ private byte[] fixedInputData;
+ private int maxSizeExcl;
+ // ios is i defined as an octet string (the binary representation)
+ private byte[] ios;
+
+ // operational
+ private int generatedBytes;
+ // k is used as buffer for all K(i) values
+ private byte[] k;
+
+
+ public KDFCounterBytesGenerator(Mac prf)
+ {
+ this.prf = prf;
+ this.h = prf.getMacSize();
+ this.k = new byte[h];
+ }
+
+
+ public void init(DerivationParameters param)
+ {
+ if (!(param instanceof KDFCounterParameters))
+ {
+ throw new IllegalArgumentException("Wrong type of arguments given");
+ }
+
+ KDFCounterParameters kdfParams = (KDFCounterParameters)param;
+
+ // --- init mac based PRF ---
+
+ this.prf.init(new KeyParameter(kdfParams.getKI()));
+
+ // --- set arguments ---
+
+ this.fixedInputData = kdfParams.getFixedInputData();
+
+ int r = kdfParams.getR();
+ this.ios = new byte[r / 8];
+
+ BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+ this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+ Integer.MAX_VALUE : maxSize.intValue();
+
+ // --- set operational state ---
+
+ generatedBytes = 0;
+ }
+
+
+ public Mac getMac()
+ {
+ return prf;
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ int generatedBytesAfter = generatedBytes + len;
+ if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+ {
+ throw new DataLengthException(
+ "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+ }
+
+ if (generatedBytes % h == 0)
+ {
+ generateNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInK = generatedBytes % h;
+ int leftInK = h - generatedBytes % h;
+ int toCopy = Math.min(leftInK, toGenerate);
+ System.arraycopy(k, posInK, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ generateNext();
+ toCopy = Math.min(h, toGenerate);
+ System.arraycopy(k, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+
+ private void generateNext()
+ {
+ int i = generatedBytes / h + 1;
+
+ // encode i into counter buffer
+ switch (ios.length)
+ {
+ case 4:
+ ios[0] = (byte)(i >>> 24);
+ // fall through
+ case 3:
+ ios[ios.length - 3] = (byte)(i >>> 16);
+ // fall through
+ case 2:
+ ios[ios.length - 2] = (byte)(i >>> 8);
+ // fall through
+ case 1:
+ ios[ios.length - 1] = (byte)i;
+ break;
+ default:
+ throw new IllegalStateException("Unsupported size of counter i");
+ }
+
+
+ // special case for K(0): K(0) is empty, so no update
+ prf.update(ios, 0, ios.length);
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(k, 0);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java
new file mode 100644
index 000000000..1716dc087
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java
@@ -0,0 +1,181 @@
+package org.spongycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.MacDerivationFunction;
+import org.spongycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFDoublePipelineIterationBytesGenerator
+ implements MacDerivationFunction
+{
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // please refer to the standard for the meaning of the variable names
+ // all field lengths are in bytes, not in bits as specified by the standard
+
+ // fields set by the constructor
+ private final Mac prf;
+ private final int h;
+
+ // fields set by init
+ private byte[] fixedInputData;
+ private int maxSizeExcl;
+ // ios is i defined as an octet string (the binary representation)
+ private byte[] ios;
+ private boolean useCounter;
+
+ // operational
+ private int generatedBytes;
+ // k is used as buffer for all K(i) values
+ private byte[] a;
+ private byte[] k;
+
+
+ public KDFDoublePipelineIterationBytesGenerator(Mac prf)
+ {
+ this.prf = prf;
+ this.h = prf.getMacSize();
+ this.a = new byte[h];
+ this.k = new byte[h];
+ }
+
+ public void init(DerivationParameters params)
+ {
+ if (!(params instanceof KDFDoublePipelineIterationParameters))
+ {
+ throw new IllegalArgumentException("Wrong type of arguments given");
+ }
+
+ KDFDoublePipelineIterationParameters dpiParams = (KDFDoublePipelineIterationParameters)params;
+
+ // --- init mac based PRF ---
+
+ this.prf.init(new KeyParameter(dpiParams.getKI()));
+
+ // --- set arguments ---
+
+ this.fixedInputData = dpiParams.getFixedInputData();
+
+ int r = dpiParams.getR();
+ this.ios = new byte[r / 8];
+
+ if (dpiParams.useCounter())
+ {
+ // this is more conservative than the spec
+ BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+ this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+ Integer.MAX_VALUE : maxSize.intValue();
+ }
+ else
+ {
+ this.maxSizeExcl = Integer.MAX_VALUE;
+ }
+
+ this.useCounter = dpiParams.useCounter();
+
+ // --- set operational state ---
+
+ generatedBytes = 0;
+ }
+
+ public Mac getMac()
+ {
+ return prf;
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ int generatedBytesAfter = generatedBytes + len;
+ if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+ {
+ throw new DataLengthException(
+ "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+ }
+
+ if (generatedBytes % h == 0)
+ {
+ generateNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInK = generatedBytes % h;
+ int leftInK = h - generatedBytes % h;
+ int toCopy = Math.min(leftInK, toGenerate);
+ System.arraycopy(k, posInK, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ generateNext();
+ toCopy = Math.min(h, toGenerate);
+ System.arraycopy(k, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+
+ private void generateNext()
+ {
+
+ if (generatedBytes == 0)
+ {
+ // --- step 4 ---
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(a, 0);
+ }
+ else
+ {
+ // --- step 5a ---
+ prf.update(a, 0, a.length);
+ prf.doFinal(a, 0);
+ }
+
+ // --- step 5b ---
+ prf.update(a, 0, a.length);
+
+ if (useCounter)
+ {
+ int i = generatedBytes / h + 1;
+
+ // encode i into counter buffer
+ switch (ios.length)
+ {
+ case 4:
+ ios[0] = (byte)(i >>> 24);
+ // fall through
+ case 3:
+ ios[ios.length - 3] = (byte)(i >>> 16);
+ // fall through
+ case 2:
+ ios[ios.length - 2] = (byte)(i >>> 8);
+ // fall through
+ case 1:
+ ios[ios.length - 1] = (byte)i;
+ break;
+ default:
+ throw new IllegalStateException("Unsupported size of counter i");
+ }
+ prf.update(ios, 0, ios.length);
+ }
+
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(k, 0);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFFeedbackBytesGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFFeedbackBytesGenerator.java
new file mode 100644
index 000000000..6410ec918
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/KDFFeedbackBytesGenerator.java
@@ -0,0 +1,175 @@
+package org.spongycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.MacDerivationFunction;
+import org.spongycastle.crypto.params.KDFFeedbackParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFFeedbackBytesGenerator
+ implements MacDerivationFunction
+{
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // please refer to the standard for the meaning of the variable names
+ // all field lengths are in bytes, not in bits as specified by the standard
+
+ // fields set by the constructor
+ private final Mac prf;
+ private final int h;
+
+ // fields set by init
+ private byte[] fixedInputData;
+ private int maxSizeExcl;
+ // ios is i defined as an octet string (the binary representation)
+ private byte[] ios;
+ private byte[] iv;
+ private boolean useCounter;
+
+ // operational
+ private int generatedBytes;
+ // k is used as buffer for all K(i) values
+ private byte[] k;
+
+
+ public KDFFeedbackBytesGenerator(Mac prf)
+ {
+ this.prf = prf;
+ this.h = prf.getMacSize();
+ this.k = new byte[h];
+ }
+
+ public void init(DerivationParameters params)
+ {
+ if (!(params instanceof KDFFeedbackParameters))
+ {
+ throw new IllegalArgumentException("Wrong type of arguments given");
+ }
+
+ KDFFeedbackParameters feedbackParams = (KDFFeedbackParameters)params;
+
+ // --- init mac based PRF ---
+
+ this.prf.init(new KeyParameter(feedbackParams.getKI()));
+
+ // --- set arguments ---
+
+ this.fixedInputData = feedbackParams.getFixedInputData();
+
+ int r = feedbackParams.getR();
+ this.ios = new byte[r / 8];
+
+ if (feedbackParams.useCounter())
+ {
+ // this is more conservative than the spec
+ BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+ this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+ Integer.MAX_VALUE : maxSize.intValue();
+ }
+ else
+ {
+ this.maxSizeExcl = Integer.MAX_VALUE;
+ }
+
+ this.iv = feedbackParams.getIV();
+ this.useCounter = feedbackParams.useCounter();
+
+ // --- set operational state ---
+
+ generatedBytes = 0;
+ }
+
+ public Mac getMac()
+ {
+ return prf;
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ int generatedBytesAfter = generatedBytes + len;
+ if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+ {
+ throw new DataLengthException(
+ "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+ }
+
+ if (generatedBytes % h == 0)
+ {
+ generateNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInK = generatedBytes % h;
+ int leftInK = h - generatedBytes % h;
+ int toCopy = Math.min(leftInK, toGenerate);
+ System.arraycopy(k, posInK, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ generateNext();
+ toCopy = Math.min(h, toGenerate);
+ System.arraycopy(k, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+
+ private void generateNext()
+ {
+
+ // TODO enable IV
+ if (generatedBytes == 0)
+ {
+ prf.update(iv, 0, iv.length);
+ }
+ else
+ {
+ prf.update(k, 0, k.length);
+ }
+
+ if (useCounter)
+ {
+ int i = generatedBytes / h + 1;
+
+ // encode i into counter buffer
+ switch (ios.length)
+ {
+ case 4:
+ ios[0] = (byte)(i >>> 24);
+ // fall through
+ case 3:
+ ios[ios.length - 3] = (byte)(i >>> 16);
+ // fall through
+ case 2:
+ ios[ios.length - 2] = (byte)(i >>> 8);
+ // fall through
+ case 1:
+ ios[ios.length - 1] = (byte)i;
+ break;
+ default:
+ throw new IllegalStateException("Unsupported size of counter i");
+ }
+ prf.update(ios, 0, ios.length);
+ }
+
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(k, 0);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/MGF1BytesGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/MGF1BytesGenerator.java
new file mode 100644
index 000000000..03cb7ba50
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/MGF1BytesGenerator.java
@@ -0,0 +1,114 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.MGFParameters;
+
+/**
+ * Generator for MGF1 as defined in PKCS 1v2
+ */
+public class MGF1BytesGenerator
+ implements DerivationFunction
+{
+ private Digest digest;
+ private byte[] seed;
+ private int hLen;
+
+ /**
+ * @param digest the digest to be used as the source of generated bytes
+ */
+ public MGF1BytesGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ this.hLen = digest.getDigestSize();
+ }
+
+ public void init(
+ DerivationParameters param)
+ {
+ if (!(param instanceof MGFParameters))
+ {
+ throw new IllegalArgumentException("MGF parameters required for MGF1Generator");
+ }
+
+ MGFParameters p = (MGFParameters)param;
+
+ seed = p.getSeed();
+ }
+
+ /**
+ * 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 = 0;
+
+ digest.reset();
+
+ if (len > hLen)
+ {
+ do
+ {
+ ItoOSP(counter, C);
+
+ digest.update(seed, 0, seed.length);
+ digest.update(C, 0, C.length);
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + counter * hLen, hLen);
+ }
+ while (++counter < (len / hLen));
+ }
+
+ if ((counter * hLen) < len)
+ {
+ ItoOSP(counter, C);
+
+ digest.update(seed, 0, seed.length);
+ digest.update(C, 0, C.length);
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + counter * hLen, len - (counter * hLen));
+ }
+
+ return len;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/NaccacheSternKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
new file mode 100644
index 000000000..5aa60026f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
@@ -0,0 +1,365 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.params.NaccacheSternKeyGenerationParameters;
+import org.spongycastle.crypto.params.NaccacheSternKeyParameters;
+import org.spongycastle.crypto.params.NaccacheSternPrivateKeyParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+/**
+ * Key generation parameters for NaccacheStern cipher. For details on this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+ private static int[] smallPrimes =
+ {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
+ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
+ 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331,
+ 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
+ 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557
+ };
+
+ private NaccacheSternKeyGenerationParameters param;
+
+ private static final BigInteger ONE = BigInteger.valueOf(1); // JDK 1.1 compatibility
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator#init(org.spongycastle.crypto.KeyGenerationParameters)
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ this.param = (NaccacheSternKeyGenerationParameters)param;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair()
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ int strength = param.getStrength();
+ SecureRandom rand = param.getRandom();
+ int certainty = param.getCertainty();
+ boolean debug = param.isDebug();
+
+ if (debug)
+ {
+ System.out.println("Fetching first " + param.getCntSmallPrimes() + " primes.");
+ }
+
+ Vector smallPrimes = findFirstPrimes(param.getCntSmallPrimes());
+ smallPrimes = permuteList(smallPrimes, rand);
+
+ BigInteger u = ONE;
+ BigInteger v = ONE;
+
+ for (int i = 0; i < smallPrimes.size() / 2; i++)
+ {
+ u = u.multiply((BigInteger)smallPrimes.elementAt(i));
+ }
+ for (int i = smallPrimes.size() / 2; i < smallPrimes.size(); i++)
+ {
+ v = v.multiply((BigInteger)smallPrimes.elementAt(i));
+ }
+
+ BigInteger sigma = u.multiply(v);
+
+ // n = (2 a u p_ + 1 ) ( 2 b v q_ + 1)
+ // -> |n| = strength
+ // |2| = 1 in bits
+ // -> |a| * |b| = |n| - |u| - |v| - |p_| - |q_| - |2| -|2|
+ // remainingStrength = strength - sigma.bitLength() - p_.bitLength() -
+ // q_.bitLength() - 1 -1
+ int remainingStrength = strength - sigma.bitLength() - 48;
+ BigInteger a = generatePrime(remainingStrength / 2 + 1, certainty, rand);
+ BigInteger b = generatePrime(remainingStrength / 2 + 1, certainty, rand);
+
+ BigInteger p_;
+ BigInteger q_;
+ BigInteger p;
+ BigInteger q;
+ long tries = 0;
+ if (debug)
+ {
+ System.out.println("generating p and q");
+ }
+
+ BigInteger _2au = a.multiply(u).shiftLeft(1);
+ BigInteger _2bv = b.multiply(v).shiftLeft(1);
+
+ for (;;)
+ {
+ tries++;
+
+ p_ = generatePrime(24, certainty, rand);
+
+ p = p_.multiply(_2au).add(ONE);
+
+ if (!p.isProbablePrime(certainty))
+ {
+ continue;
+ }
+
+ for (;;)
+ {
+ q_ = generatePrime(24, certainty, rand);
+
+ if (p_.equals(q_))
+ {
+ continue;
+ }
+
+ q = q_.multiply(_2bv).add(ONE);
+
+ if (q.isProbablePrime(certainty))
+ {
+ break;
+ }
+ }
+
+ if (!sigma.gcd(p_.multiply(q_)).equals(ONE))
+ {
+ // System.out.println("sigma.gcd(p_.mult(q_)) != 1!\n p_: " + p_
+ // +"\n q_: "+ q_ );
+ continue;
+ }
+
+ if (p.multiply(q).bitLength() < strength)
+ {
+ if (debug)
+ {
+ System.out.println("key size too small. Should be " + strength + " but is actually "
+ + p.multiply(q).bitLength());
+ }
+ continue;
+ }
+ break;
+ }
+
+ if (debug)
+ {
+ System.out.println("needed " + tries + " tries to generate p and q.");
+ }
+
+ BigInteger n = p.multiply(q);
+ BigInteger phi_n = p.subtract(ONE).multiply(q.subtract(ONE));
+ BigInteger g;
+ tries = 0;
+ if (debug)
+ {
+ System.out.println("generating g");
+ }
+ for (;;)
+ {
+
+ Vector gParts = new Vector();
+ for (int ind = 0; ind != smallPrimes.size(); ind++)
+ {
+ BigInteger i = (BigInteger)smallPrimes.elementAt(ind);
+ BigInteger e = phi_n.divide(i);
+
+ for (;;)
+ {
+ tries++;
+ g = new BigInteger(strength, certainty, rand);
+ if (g.modPow(e, n).equals(ONE))
+ {
+ continue;
+ }
+ gParts.addElement(g);
+ break;
+ }
+ }
+ g = ONE;
+ for (int i = 0; i < smallPrimes.size(); i++)
+ {
+ g = g.multiply(((BigInteger)gParts.elementAt(i)).modPow(sigma.divide((BigInteger)smallPrimes.elementAt(i)), n)).mod(n);
+ }
+
+ // make sure that g is not divisible by p_i or q_i
+ boolean divisible = false;
+ for (int i = 0; i < smallPrimes.size(); i++)
+ {
+ if (g.modPow(phi_n.divide((BigInteger)smallPrimes.elementAt(i)), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/" + smallPrimes.elementAt(i) + "\n g: " + g);
+ }
+ divisible = true;
+ break;
+ }
+ }
+
+ if (divisible)
+ {
+ continue;
+ }
+
+ // make sure that g has order > phi_n/4
+
+ if (g.modPow(phi_n.divide(BigInteger.valueOf(4)), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/4\n g:" + g);
+ }
+ continue;
+ }
+
+ if (g.modPow(phi_n.divide(p_), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/p'\n g: " + g);
+ }
+ continue;
+ }
+ if (g.modPow(phi_n.divide(q_), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/q'\n g: " + g);
+ }
+ continue;
+ }
+ if (g.modPow(phi_n.divide(a), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/a\n g: " + g);
+ }
+ continue;
+ }
+ if (g.modPow(phi_n.divide(b), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/b\n g: " + g);
+ }
+ continue;
+ }
+ break;
+ }
+ if (debug)
+ {
+ System.out.println("needed " + tries + " tries to generate g");
+ System.out.println();
+ System.out.println("found new NaccacheStern cipher variables:");
+ System.out.println("smallPrimes: " + smallPrimes);
+ System.out.println("sigma:...... " + sigma + " (" + sigma.bitLength() + " bits)");
+ System.out.println("a:.......... " + a);
+ System.out.println("b:.......... " + b);
+ System.out.println("p':......... " + p_);
+ System.out.println("q':......... " + q_);
+ System.out.println("p:.......... " + p);
+ System.out.println("q:.......... " + q);
+ System.out.println("n:.......... " + n);
+ System.out.println("phi(n):..... " + phi_n);
+ System.out.println("g:.......... " + g);
+ System.out.println();
+ }
+
+ return new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.bitLength()),
+ new NaccacheSternPrivateKeyParameters(g, n, sigma.bitLength(), smallPrimes, phi_n));
+ }
+
+ private static BigInteger generatePrime(
+ int bitLength,
+ int certainty,
+ SecureRandom rand)
+ {
+ BigInteger p_ = new BigInteger(bitLength, certainty, rand);
+ while (p_.bitLength() != bitLength)
+ {
+ p_ = new BigInteger(bitLength, certainty, rand);
+ }
+ return p_;
+ }
+
+ /**
+ * Generates a permuted ArrayList from the original one. The original List
+ * is not modified
+ *
+ * @param arr
+ * the ArrayList to be permuted
+ * @param rand
+ * the source of Randomness for permutation
+ * @return a new ArrayList with the permuted elements.
+ */
+ private static Vector permuteList(
+ Vector arr,
+ SecureRandom rand)
+ {
+ Vector retval = new Vector();
+ Vector tmp = new Vector();
+ for (int i = 0; i < arr.size(); i++)
+ {
+ tmp.addElement(arr.elementAt(i));
+ }
+ retval.addElement(tmp.elementAt(0));
+ tmp.removeElementAt(0);
+ while (tmp.size() != 0)
+ {
+ retval.insertElementAt(tmp.elementAt(0), getInt(rand, retval.size() + 1));
+ tmp.removeElementAt(0);
+ }
+ return retval;
+ }
+
+ private static int getInt(
+ SecureRandom rand,
+ int n)
+ {
+ if ((n & -n) == n)
+ {
+ return (int)((n * (long)(rand.nextInt() & 0x7fffffff)) >> 31);
+ }
+
+ int bits, val;
+ do
+ {
+ bits = rand.nextInt() & 0x7fffffff;
+ val = bits % n;
+ }
+ while (bits - val + (n-1) < 0);
+
+ return val;
+ }
+
+ /**
+ * Finds the first 'count' primes starting with 3
+ *
+ * @param count
+ * the number of primes to find
+ * @return a vector containing the found primes as Integer
+ */
+ private static Vector findFirstPrimes(
+ int count)
+ {
+ Vector primes = new Vector(count);
+
+ for (int i = 0; i != count; i++)
+ {
+ primes.addElement(BigInteger.valueOf(smallPrimes[i]));
+ }
+
+ return primes;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
new file mode 100644
index 000000000..70e35927d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
@@ -0,0 +1,131 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.digests.MD5Digest;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as usd by OpenSSL.
+ *
+ * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
+ * iteration count of 1.
+ *
+ */
+public class OpenSSLPBEParametersGenerator
+ extends PBEParametersGenerator
+{
+ private Digest digest = new MD5Digest();
+
+ /**
+ * Construct a OpenSSL Parameters generator.
+ */
+ public OpenSSLPBEParametersGenerator()
+ {
+ }
+
+ /**
+ * Initialise - note the iteration count for this algorithm is fixed at 1.
+ *
+ * @param password password to use.
+ * @param salt salt to use.
+ */
+ public void init(
+ byte[] password,
+ byte[] salt)
+ {
+ super.init(password, salt, 1);
+ }
+
+ /**
+ * the derived key function, the ith hash of the password and the salt.
+ */
+ private byte[] generateDerivedKey(
+ int bytesNeeded)
+ {
+ byte[] buf = new byte[digest.getDigestSize()];
+ byte[] key = new byte[bytesNeeded];
+ int offset = 0;
+
+ for (;;)
+ {
+ digest.update(password, 0, password.length);
+ digest.update(salt, 0, salt.length);
+
+ digest.doFinal(buf, 0);
+
+ int len = (bytesNeeded > buf.length) ? buf.length : bytesNeeded;
+ System.arraycopy(buf, 0, key, offset, len);
+ offset += len;
+
+ // check if we need any more
+ bytesNeeded -= len;
+ if (bytesNeeded == 0)
+ {
+ break;
+ }
+
+ // do another round
+ digest.reset();
+ digest.update(buf, 0, buf.length);
+ }
+
+ return key;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ * @exception IllegalArgumentException if keySize + ivSize is larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize + ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ return generateDerivedParameters(keySize);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS12ParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS12ParametersGenerator.java
new file mode 100644
index 000000000..ee40465e9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS12ParametersGenerator.java
@@ -0,0 +1,220 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0.
+ *
+ * The document this implementation is based on can be found at
+ *
+ * RSA's PKCS12 Page
+ */
+public class PKCS12ParametersGenerator
+ extends PBEParametersGenerator
+{
+ public static final int KEY_MATERIAL = 1;
+ public static final int IV_MATERIAL = 2;
+ public static final int MAC_MATERIAL = 3;
+
+ private Digest digest;
+
+ private int u;
+ private int v;
+
+ /**
+ * Construct a PKCS 12 Parameters generator. This constructor will
+ * accept any digest which also implements ExtendedDigest.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ * @exception IllegalArgumentException if an unknown digest is passed in.
+ */
+ public PKCS12ParametersGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ if (digest instanceof ExtendedDigest)
+ {
+ u = digest.getDigestSize();
+ v = ((ExtendedDigest)digest).getByteLength();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported");
+ }
+ }
+
+ /**
+ * add a + b + 1, returning the result in a. The a value is treated
+ * as a BigInteger of length (b.length * 8) bits. The result is
+ * modulo 2^b.length in case of overflow.
+ */
+ private void adjust(
+ byte[] a,
+ int aOff,
+ byte[] b)
+ {
+ int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1;
+
+ a[aOff + b.length - 1] = (byte)x;
+ x >>>= 8;
+
+ for (int i = b.length - 2; i >= 0; i--)
+ {
+ x += (b[i] & 0xff) + (a[aOff + i] & 0xff);
+ a[aOff + i] = (byte)x;
+ x >>>= 8;
+ }
+ }
+
+ /**
+ * generation of a derived key ala PKCS12 V1.0.
+ */
+ private byte[] generateDerivedKey(
+ int idByte,
+ int n)
+ {
+ byte[] D = new byte[v];
+ byte[] dKey = new byte[n];
+
+ for (int i = 0; i != D.length; i++)
+ {
+ D[i] = (byte)idByte;
+ }
+
+ byte[] S;
+
+ if ((salt != null) && (salt.length != 0))
+ {
+ S = new byte[v * ((salt.length + v - 1) / v)];
+
+ for (int i = 0; i != S.length; i++)
+ {
+ S[i] = salt[i % salt.length];
+ }
+ }
+ else
+ {
+ S = new byte[0];
+ }
+
+ byte[] P;
+
+ if ((password != null) && (password.length != 0))
+ {
+ P = new byte[v * ((password.length + v - 1) / v)];
+
+ for (int i = 0; i != P.length; i++)
+ {
+ P[i] = password[i % password.length];
+ }
+ }
+ else
+ {
+ P = new byte[0];
+ }
+
+ byte[] I = new byte[S.length + P.length];
+
+ System.arraycopy(S, 0, I, 0, S.length);
+ System.arraycopy(P, 0, I, S.length, P.length);
+
+ byte[] B = new byte[v];
+ int c = (n + u - 1) / u;
+ byte[] A = new byte[u];
+
+ for (int i = 1; i <= c; i++)
+ {
+ digest.update(D, 0, D.length);
+ digest.update(I, 0, I.length);
+ digest.doFinal(A, 0);
+ for (int j = 1; j < iterationCount; j++)
+ {
+ digest.update(A, 0, A.length);
+ digest.doFinal(A, 0);
+ }
+
+ for (int j = 0; j != B.length; j++)
+ {
+ B[j] = A[j % A.length];
+ }
+
+ for (int j = 0; j != I.length / v; j++)
+ {
+ adjust(I, j * v, B);
+ }
+
+ if (i == c)
+ {
+ System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u));
+ }
+ else
+ {
+ System.arraycopy(A, 0, dKey, (i - 1) * u, A.length);
+ }
+ }
+
+ return dKey;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize);
+
+ byte[] iv = generateDerivedKey(IV_MATERIAL, ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(MAC_MATERIAL, keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS5S1ParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS5S1ParametersGenerator.java
new file mode 100644
index 000000000..3a6415f25
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS5S1ParametersGenerator.java
@@ -0,0 +1,119 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 1.
+ * Note this generator is limited to the size of the hash produced by the
+ * digest used to drive it.
+ *
+ * The document this implementation is based on can be found at
+ *
+ * RSA's PKCS5 Page
+ */
+public class PKCS5S1ParametersGenerator
+ extends PBEParametersGenerator
+{
+ private Digest digest;
+
+ /**
+ * Construct a PKCS 5 Scheme 1 Parameters generator.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public PKCS5S1ParametersGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ }
+
+ /**
+ * the derived key function, the ith hash of the password and the salt.
+ */
+ private byte[] generateDerivedKey()
+ {
+ byte[] digestBytes = new byte[digest.getDigestSize()];
+
+ digest.update(password, 0, password.length);
+ digest.update(salt, 0, salt.length);
+
+ digest.doFinal(digestBytes, 0);
+ for (int i = 1; i < iterationCount; i++)
+ {
+ digest.update(digestBytes, 0, digestBytes.length);
+ digest.doFinal(digestBytes, 0);
+ }
+
+ return digestBytes;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ if (keySize > digest.getDigestSize())
+ {
+ throw new IllegalArgumentException(
+ "Can't generate a derived key " + keySize + " bytes long.");
+ }
+
+ byte[] dKey = generateDerivedKey();
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ * @exception IllegalArgumentException if keySize + ivSize is larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ if ((keySize + ivSize) > digest.getDigestSize())
+ {
+ throw new IllegalArgumentException(
+ "Can't generate a derived key " + (keySize + ivSize) + " bytes long.");
+ }
+
+ byte[] dKey = generateDerivedKey();
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ return generateDerivedParameters(keySize);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS5S2ParametersGenerator.java
new file mode 100644
index 000000000..59b5a187c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/PKCS5S2ParametersGenerator.java
@@ -0,0 +1,153 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2.
+ * This generator uses a SHA-1 HMac as the calculation function.
+ *
+ * The document this implementation is based on can be found at
+ *
+ * RSA's PKCS5 Page
+ */
+public class PKCS5S2ParametersGenerator
+ extends PBEParametersGenerator
+{
+ private Mac hMac;
+ private byte[] state;
+
+ /**
+ * construct a PKCS5 Scheme 2 Parameters generator.
+ */
+ public PKCS5S2ParametersGenerator()
+ {
+ this(new SHA1Digest());
+ }
+
+ public PKCS5S2ParametersGenerator(Digest digest)
+ {
+ hMac = new HMac(digest);
+ state = new byte[hMac.getMacSize()];
+ }
+
+ private void F(
+ byte[] S,
+ int c,
+ byte[] iBuf,
+ byte[] out,
+ int outOff)
+ {
+ if (c == 0)
+ {
+ throw new IllegalArgumentException("iteration count must be at least 1.");
+ }
+
+ if (S != null)
+ {
+ hMac.update(S, 0, S.length);
+ }
+
+ hMac.update(iBuf, 0, iBuf.length);
+ hMac.doFinal(state, 0);
+
+ System.arraycopy(state, 0, out, outOff, state.length);
+
+ for (int count = 1; count < c; count++)
+ {
+ hMac.update(state, 0, state.length);
+ hMac.doFinal(state, 0);
+
+ for (int j = 0; j != state.length; j++)
+ {
+ out[outOff + j] ^= state[j];
+ }
+ }
+ }
+
+ private byte[] generateDerivedKey(
+ int dkLen)
+ {
+ int hLen = hMac.getMacSize();
+ int l = (dkLen + hLen - 1) / hLen;
+ byte[] iBuf = new byte[4];
+ byte[] outBytes = new byte[l * hLen];
+ int outPos = 0;
+
+ CipherParameters param = new KeyParameter(password);
+
+ hMac.init(param);
+
+ for (int i = 1; i <= l; i++)
+ {
+ // Increment the value in 'iBuf'
+ int pos = 3;
+ while (++iBuf[pos] == 0)
+ {
+ --pos;
+ }
+
+ F(salt, iterationCount, iBuf, outBytes, outPos);
+ outPos += hLen;
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize + ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ return generateDerivedParameters(keySize);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/Poly1305KeyGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/Poly1305KeyGenerator.java
new file mode 100644
index 000000000..5eebc497f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/Poly1305KeyGenerator.java
@@ -0,0 +1,117 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.CipherKeyGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.macs.Poly1305;
+
+/**
+ * Generates keys for the Poly1305 MAC.
+ *
+ * Poly1305 keys are 256 bit keys consisting of a 128 bit secret key used for the underlying block
+ * cipher followed by a 128 bit {@code r} value used for the polynomial portion of the Mac.
+ * Note: the size of the MAC must be at least 16 bits (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @deprecated use CBCBlockCipherMac
+ */
+ public BlockCipherMac(
+ BlockCipher cipher,
+ int macSizeInBits)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ //
+ // pad with zeroes
+ //
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CBCBlockCipherMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CBCBlockCipherMac.java
new file mode 100644
index 000000000..fd8a4f143
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CBCBlockCipherMac.java
@@ -0,0 +1,229 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.paddings.BlockCipherPadding;
+
+/**
+ * standard CBC Block Cipher MAC - if no padding is specified the default of
+ * pad of zeroes is used.
+ */
+public class CBCBlockCipherMac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private BlockCipher cipher;
+ private BlockCipherPadding padding;
+
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a CBC block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher)
+ {
+ this(cipher, (cipher.getBlockSize() * 8) / 2, null);
+ }
+
+ /**
+ * create a standard MAC based on a CBC block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher,
+ BlockCipherPadding padding)
+ {
+ this(cipher, (cipher.getBlockSize() * 8) / 2, padding);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CBC mode as the basis for the
+ * MAC generation.
+ *
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher,
+ int macSizeInBits)
+ {
+ this(cipher, macSizeInBits, null);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CBC mode as the basis for the
+ * MAC generation.
+ *
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher,
+ int macSizeInBits,
+ BlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ if (padding == null)
+ {
+ //
+ // pad with zeroes
+ //
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ padding.addPadding(buf, bufOff);
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CFBBlockCipherMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CFBBlockCipherMac.java
new file mode 100644
index 000000000..920b1fa62
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CFBBlockCipherMac.java
@@ -0,0 +1,388 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.paddings.BlockCipherPadding;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+ */
+class MacCFBBlockCipher
+{
+ private byte[] IV;
+ private byte[] cfbV;
+ private byte[] cfbOutV;
+
+ private int blockSize;
+ private BlockCipher cipher = null;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public MacCFBBlockCipher(
+ BlockCipher cipher,
+ int bitBlockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = bitBlockSize / 8;
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.cfbV = new byte[cipher.getBlockSize()];
+ this.cfbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param param the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ cipher.init(true, ivParam.getParameters());
+ }
+ else
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // XOR the cfbV with the plaintext producing the cipher text
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, cfbV, 0, IV.length);
+
+ cipher.reset();
+ }
+
+ void getMacBlock(
+ byte[] mac)
+ {
+ cipher.processBlock(cfbV, 0, mac, 0);
+ }
+}
+
+public class CFBBlockCipherMac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private MacCFBBlockCipher cipher;
+ private BlockCipherPadding padding = null;
+
+
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a CFB block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher, with
+ * the CFB mode set to 8 bits.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher)
+ {
+ this(cipher, 8, (cipher.getBlockSize() * 8) / 2, null);
+ }
+
+ /**
+ * create a standard MAC based on a CFB block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher, with
+ * the CFB mode set to 8 bits.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher,
+ BlockCipherPadding padding)
+ {
+ this(cipher, 8, (cipher.getBlockSize() * 8) / 2, padding);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CFB mode as the basis for the
+ * MAC generation.
+ *
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param cfbBitSize the size of an output block produced by the CFB mode.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher,
+ int cfbBitSize,
+ int macSizeInBits)
+ {
+ this(cipher, cfbBitSize, macSizeInBits, null);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CFB mode as the basis for the
+ * MAC generation.
+ *
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param cfbBitSize the size of an output block produced by the CFB mode.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding a padding to be used.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher,
+ int cfbBitSize,
+ int macSizeInBits,
+ BlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ mac = new byte[cipher.getBlockSize()];
+
+ this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ buf = new byte[this.cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(params);
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ //
+ // pad with zeroes
+ //
+ if (this.padding == null)
+ {
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+ }
+ else
+ {
+ padding.addPadding(buf, bufOff);
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ cipher.getMacBlock(mac);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CMac.java
new file mode 100644
index 000000000..c1947d063
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/CMac.java
@@ -0,0 +1,254 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.paddings.ISO7816d4Padding;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
+ *
+ * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC
+ *
+ * CMAC is a NIST recomendation - see
+ * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ *
+ * CMAC/OMAC1 is a blockcipher-based message authentication code designed and
+ * analyzed by Tetsu Iwata and Kaoru Kurosawa.
+ *
+ * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message
+ * Authentication Code). OMAC stands for One-Key CBC MAC.
+ *
+ * It supports 128- or 64-bits block ciphers, with any key size, and returns
+ * a MAC with dimension less or equal to the block size of the underlying
+ * cipher.
+ *
+ * GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac
+ * is processed as additional authenticated data with the underlying GCM block cipher).
+ */
+public class GMac implements Mac
+{
+ private final GCMBlockCipher cipher;
+ private final int macSizeBits;
+
+ /**
+ * Creates a GMAC based on the operation of a block cipher in GCM mode.
+ *
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public ISO9797Alg3Mac(
+ BlockCipher cipher,
+ int macSizeInBits)
+ {
+ this(cipher, macSizeInBits, null);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses single DES CBC mode as the basis for the
+ * MAC generation. The final block is decrypted and then encrypted using the
+ * middle and right part of the key.
+ *
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public ISO9797Alg3Mac(
+ BlockCipher cipher,
+ int macSizeInBits,
+ BlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ if (!(cipher instanceof DESEngine))
+ {
+ throw new IllegalArgumentException("cipher must be instance of DESEngine");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "ISO9797Alg3";
+ }
+
+ public void init(CipherParameters params)
+ {
+ reset();
+
+ if (!(params instanceof KeyParameter || params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "params must be an instance of KeyParameter or ParametersWithIV");
+ }
+
+ // KeyParameter must contain a double or triple length DES key,
+ // however the underlying cipher is a single DES. The middle and
+ // right key are used only in the final step.
+
+ KeyParameter kp;
+
+ if (params instanceof KeyParameter)
+ {
+ kp = (KeyParameter)params;
+ }
+ else
+ {
+ kp = (KeyParameter)((ParametersWithIV)params).getParameters();
+ }
+
+ KeyParameter key1;
+ byte[] keyvalue = kp.getKey();
+
+ if (keyvalue.length == 16)
+ { // Double length DES key
+ key1 = new KeyParameter(keyvalue, 0, 8);
+ this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
+ this.lastKey3 = key1;
+ }
+ else if (keyvalue.length == 24)
+ { // Triple length DES key
+ key1 = new KeyParameter(keyvalue, 0, 8);
+ this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
+ this.lastKey3 = new KeyParameter(keyvalue, 16, 8);
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Key must be either 112 or 168 bit long");
+ }
+
+ if (params instanceof ParametersWithIV)
+ {
+ cipher.init(true, new ParametersWithIV(key1, ((ParametersWithIV)params).getIV()));
+ }
+ else
+ {
+ cipher.init(true, key1);
+ }
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ if (padding == null)
+ {
+ //
+ // pad with zeroes
+ //
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ padding.addPadding(buf, bufOff);
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ // Added to code from base class
+ DESEngine deseng = new DESEngine();
+
+ deseng.init(false, this.lastKey2);
+ deseng.processBlock(mac, 0, mac, 0);
+
+ deseng.init(true, this.lastKey3);
+ deseng.processBlock(mac, 0, mac, 0);
+ // ****
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/OldHMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/OldHMac.java
new file mode 100644
index 000000000..ff6808388
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/OldHMac.java
@@ -0,0 +1,138 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * HMAC implementation based on RFC2104
+ *
+ * H(K XOR opad, H(K XOR ipad, text))
+ */
+public class OldHMac
+implements Mac
+{
+ private final static int BLOCK_LENGTH = 64;
+
+ private final static byte IPAD = (byte)0x36;
+ private final static byte OPAD = (byte)0x5C;
+
+ private Digest digest;
+ private int digestSize;
+ private byte[] inputPad = new byte[BLOCK_LENGTH];
+ private byte[] outputPad = new byte[BLOCK_LENGTH];
+
+ /**
+ * @deprecated uses incorrect pad for SHA-512 and SHA-384 use HMac.
+ */
+ public OldHMac(
+ Digest digest)
+ {
+ this.digest = digest;
+ digestSize = digest.getDigestSize();
+ }
+
+ public String getAlgorithmName()
+ {
+ return digest.getAlgorithmName() + "/HMAC";
+ }
+
+ public Digest getUnderlyingDigest()
+ {
+ return digest;
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ digest.reset();
+
+ byte[] key = ((KeyParameter)params).getKey();
+
+ if (key.length > BLOCK_LENGTH)
+ {
+ digest.update(key, 0, key.length);
+ digest.doFinal(inputPad, 0);
+ for (int i = digestSize; i < inputPad.length; i++)
+ {
+ inputPad[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(key, 0, inputPad, 0, key.length);
+ for (int i = key.length; i < inputPad.length; i++)
+ {
+ inputPad[i] = 0;
+ }
+ }
+
+ outputPad = new byte[inputPad.length];
+ System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length);
+
+ for (int i = 0; i < inputPad.length; i++)
+ {
+ inputPad[i] ^= IPAD;
+ }
+
+ for (int i = 0; i < outputPad.length; i++)
+ {
+ outputPad[i] ^= OPAD;
+ }
+
+ digest.update(inputPad, 0, inputPad.length);
+ }
+
+ public int getMacSize()
+ {
+ return digestSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ digest.update(in);
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ digest.update(in, inOff, len);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ byte[] tmp = new byte[digestSize];
+ digest.doFinal(tmp, 0);
+
+ digest.update(outputPad, 0, outputPad.length);
+ digest.update(tmp, 0, tmp.length);
+
+ int len = digest.doFinal(out, outOff);
+
+ reset();
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * reset the underlying digest.
+ */
+ digest.reset();
+
+ /*
+ * reinitialize the digest.
+ */
+ digest.update(inputPad, 0, inputPad.length);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java
new file mode 100644
index 000000000..ca1558d82
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java
@@ -0,0 +1,279 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.util.Pack;
+
+/**
+ * Poly1305 message authentication code, designed by D. J. Bernstein.
+ *
+ * Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
+ * consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
+ * effective key bits) used in the authenticator.
+ *
+ * The polynomial calculation in this implementation is adapted from the public domain poly1305-donna-unrolled C implementation
+ * by Andrew M (@floodyberry).
+ * @see Poly1305KeyGenerator
+ */
+public class Poly1305
+ implements Mac
+{
+ private static final int BLOCK_SIZE = 16;
+
+ private final BlockCipher cipher;
+
+ private final byte[] singleByte = new byte[1];
+
+ // Initialised state
+
+ /** Polynomial key */
+ private int r0, r1, r2, r3, r4;
+
+ /** Precomputed 5 * r[1..4] */
+ private int s1, s2, s3, s4;
+
+ /** Encrypted nonce */
+ private int k0, k1, k2, k3;
+
+ // Accumulating state
+
+ /** Current block of buffered input */
+ private final byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ /** Polynomial accumulator */
+ private int h0, h1, h2, h3, h4;
+
+ /**
+ * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+ */
+ public Poly1305(final BlockCipher cipher)
+ {
+ if (cipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ /**
+ * Initialises the Poly1305 MAC.
+ *
+ * @param a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
+ * a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.
+ */
+ public void init(final CipherParameters params)
+ throws IllegalArgumentException
+ {
+ final byte[] nonce;
+ final byte[] key;
+ if ((params instanceof ParametersWithIV) && ((ParametersWithIV)params).getParameters() instanceof KeyParameter)
+ {
+ nonce = ((ParametersWithIV)params).getIV();
+ key = ((KeyParameter)((ParametersWithIV)params).getParameters()).getKey();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Poly1305 requires a key and and IV.");
+ }
+
+ setKey(key, nonce);
+ reset();
+ }
+
+ private void setKey(final byte[] key, final byte[] nonce)
+ {
+ if (nonce.length != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
+ }
+ Poly1305KeyGenerator.checkKey(key);
+
+ // Extract r portion of key
+ int t0 = Pack.littleEndianToInt(key, BLOCK_SIZE + 0);
+ int t1 = Pack.littleEndianToInt(key, BLOCK_SIZE + 4);
+ int t2 = Pack.littleEndianToInt(key, BLOCK_SIZE + 8);
+ int t3 = Pack.littleEndianToInt(key, BLOCK_SIZE + 12);
+
+ r0 = t0 & 0x3ffffff; t0 >>>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>>= 8;
+ r4 = t3 & 0x00fffff;
+
+ // Precompute multipliers
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ // Compute encrypted nonce
+ final byte[] cipherKey = new byte[BLOCK_SIZE];
+ System.arraycopy(key, 0, cipherKey, 0, cipherKey.length);
+
+ cipher.init(true, new KeyParameter(cipherKey));
+ cipher.processBlock(nonce, 0, cipherKey, 0);
+
+ k0 = Pack.littleEndianToInt(cipherKey, 0);
+ k1 = Pack.littleEndianToInt(cipherKey, 4);
+ k2 = Pack.littleEndianToInt(cipherKey, 8);
+ k3 = Pack.littleEndianToInt(cipherKey, 12);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Poly1305-" + cipher.getAlgorithmName();
+ }
+
+ public int getMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void update(final byte in)
+ throws IllegalStateException
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(final byte[] in, final int inOff, final int len)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), BLOCK_SIZE - currentBlockOffset);
+ System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+ copied += toCopy;
+ currentBlockOffset += toCopy;
+ }
+
+ }
+
+ private void processBlock()
+ {
+ if (currentBlockOffset < BLOCK_SIZE)
+ {
+ currentBlock[currentBlockOffset] = 1;
+ for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
+ {
+ currentBlock[i] = 0;
+ }
+ }
+
+ final long t0 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 0);
+ final long t1 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 4);
+ final long t2 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 8);
+ final long t3 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 12);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += (((t1 << 32) | t0) >>> 26) & 0x3ffffff;
+ h2 += (((t2 << 32) | t1) >>> 20) & 0x3ffffff;
+ h3 += (((t3 << 32) | t2) >>> 14) & 0x3ffffff;
+ h4 += (t3 >>> 8);
+
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ h4 += (1 << 24);
+ }
+
+ long tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ long tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ long tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ long tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ long tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ long b;
+ h0 = (int)tp0 & 0x3ffffff; b = (tp0 >>> 26);
+ tp1 += b; h1 = (int)tp1 & 0x3ffffff; b = ((tp1 >>> 26) & 0xffffffff);
+ tp2 += b; h2 = (int)tp2 & 0x3ffffff; b = ((tp2 >>> 26) & 0xffffffff);
+ tp3 += b; h3 = (int)tp3 & 0x3ffffff; b = (tp3 >>> 26);
+ tp4 += b; h4 = (int)tp4 & 0x3ffffff; b = (tp4 >>> 26);
+ h0 += b * 5;
+ }
+
+ public int doFinal(final byte[] out, final int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ if (outOff + BLOCK_SIZE > out.length)
+ {
+ throw new DataLengthException("Output buffer is too short.");
+ }
+
+ if (currentBlockOffset > 0)
+ {
+ // Process padded final block
+ processBlock();
+ }
+
+ long f0, f1, f2, f3;
+
+ int b = h0 >>> 26;
+ h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >>> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >>> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >>> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >>> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5;
+
+ int g0, g1, g2, g3, g4;
+ g0 = h0 + 5; b = g0 >>> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >>> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >>> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >>> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >>> 31) - 1;
+ int nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = (((h0 ) | (h1 << 26)) & 0xffffffffl) + (0xffffffffL & k0);
+ f1 = (((h1 >>> 6 ) | (h2 << 20)) & 0xffffffffl) + (0xffffffffL & k1);
+ f2 = (((h2 >>> 12) | (h3 << 14)) & 0xffffffffl) + (0xffffffffL & k2);
+ f3 = (((h3 >>> 18) | (h4 << 8 )) & 0xffffffffl) + (0xffffffffL & k3);
+
+ Pack.intToLittleEndian((int)f0, out, outOff);
+ f1 += (f0 >>> 32);
+ Pack.intToLittleEndian((int)f1, out, outOff + 4);
+ f2 += (f1 >>> 32);
+ Pack.intToLittleEndian((int)f2, out, outOff + 8);
+ f3 += (f2 >>> 32);
+ Pack.intToLittleEndian((int)f3, out, outOff + 12);
+
+ reset();
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ currentBlockOffset = 0;
+
+ h0 = h1 = h2 = h3 = h4 = 0;
+ }
+
+ private static final long mul32x32_64(int i1, int i2)
+ {
+ return ((long)i1) * i2;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/SipHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/SipHash.java
new file mode 100644
index 000000000..140576471
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/SipHash.java
@@ -0,0 +1,192 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe
+ * Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf).
+ *
+ * Note: this mode is a packet mode - it needs all the data up front.
+ */
+public class CCMBlockCipher
+ implements AEADBlockCipher
+{
+ private BlockCipher cipher;
+ private int blockSize;
+ private boolean forEncryption;
+ private byte[] nonce;
+ private byte[] initialAssociatedText;
+ private int macSize;
+ private CipherParameters keyParam;
+ private byte[] macBlock;
+ private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream();
+ private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
+
+ /**
+ * Basic constructor.
+ *
+ * @param c the block cipher to be used.
+ */
+ public CCMBlockCipher(BlockCipher c)
+ {
+ this.cipher = c;
+ this.blockSize = c.getBlockSize();
+ this.macBlock = new byte[blockSize];
+
+ if (blockSize != 16)
+ {
+ throw new IllegalArgumentException("cipher required with a block size of 16.");
+ }
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ CipherParameters cipherParameters;
+ if (params instanceof AEADParameters)
+ {
+ AEADParameters param = (AEADParameters)params;
+
+ nonce = param.getNonce();
+ initialAssociatedText = param.getAssociatedText();
+ macSize = param.getMacSize() / 8;
+ cipherParameters = param.getKey();
+ }
+ else if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV)params;
+
+ nonce = param.getIV();
+ initialAssociatedText = null;
+ macSize = macBlock.length / 2;
+ cipherParameters = param.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to CCM");
+ }
+
+ // NOTE: Very basic support for key re-use, but no performance gain from it
+ if (cipherParameters != null)
+ {
+ keyParam = cipherParameters;
+ }
+
+ if (nonce == null || nonce.length < 7 || nonce.length > 13)
+ {
+ throw new IllegalArgumentException("nonce must have length from 7 to 13 octets");
+ }
+
+ reset();
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CCM";
+ }
+
+ public void processAADByte(byte in)
+ {
+ associatedText.write(in);
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ // TODO: Process AAD online
+ associatedText.write(in, inOff, len);
+ }
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ data.write(in);
+
+ return 0;
+ }
+
+ public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ data.write(in, inOff, inLen);
+
+ return 0;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException
+ {
+ int len = processPacket(data.getBuffer(), 0, data.size(), out, outOff);
+
+ reset();
+
+ return len;
+ }
+
+ public void reset()
+ {
+ cipher.reset();
+ associatedText.reset();
+ data.reset();
+ }
+
+ /**
+ * Returns a byte array containing the mac calculated as part of the
+ * last encrypt or decrypt operation.
+ *
+ * @return the last mac calculated.
+ */
+ public byte[] getMac()
+ {
+ byte[] mac = new byte[macSize];
+
+ System.arraycopy(macBlock, 0, mac, 0, mac.length);
+
+ return mac;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ return 0;
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + data.size();
+
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ /**
+ * Process a packet of data for either CCM decryption or encryption.
+ *
+ * @param in data for processing.
+ * @param inOff offset at which data starts in the input array.
+ * @param inLen length of the data in the input array.
+ * @return a byte array containing the processed input..
+ * @throws IllegalStateException if the cipher is not appropriately set up.
+ * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+ */
+ public byte[] processPacket(byte[] in, int inOff, int inLen)
+ throws IllegalStateException, InvalidCipherTextException
+ {
+ byte[] output;
+
+ if (forEncryption)
+ {
+ output = new byte[inLen + macSize];
+ }
+ else
+ {
+ if (inLen < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ output = new byte[inLen - macSize];
+ }
+
+ processPacket(in, inOff, inLen, output, 0);
+
+ return output;
+ }
+
+ /**
+ * Process a packet of data for either CCM decryption or encryption.
+ *
+ * @param in data for processing.
+ * @param inOff offset at which data starts in the input array.
+ * @param inLen length of the data in the input array.
+ * @param output output array.
+ * @param outOff offset into output array to start putting processed bytes.
+ * @return the number of bytes added to output.
+ * @throws IllegalStateException if the cipher is not appropriately set up.
+ * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+ * @throws DataLengthException if output buffer too short.
+ */
+ public int processPacket(byte[] in, int inOff, int inLen, byte[] output, int outOff)
+ throws IllegalStateException, InvalidCipherTextException, DataLengthException
+ {
+ // TODO: handle null keyParam (e.g. via RepeatedKeySpec)
+ // Need to keep the CTR and CBC Mac parts around and reset
+ if (keyParam == null)
+ {
+ throw new IllegalStateException("CCM cipher unitialized.");
+ }
+
+ int n = nonce.length;
+ int q = 15 - n;
+ if (q < 4)
+ {
+ int limitLen = 1 << (8 * q);
+ if (inLen >= limitLen)
+ {
+ throw new IllegalStateException("CCM packet too large for choice of q.");
+ }
+ }
+
+ byte[] iv = new byte[blockSize];
+ iv[0] = (byte)((q - 1) & 0x7);
+ System.arraycopy(nonce, 0, iv, 1, nonce.length);
+
+ BlockCipher ctrCipher = new SICBlockCipher(cipher);
+ ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
+
+ int outputLen;
+ int inIndex = inOff;
+ int outIndex = outOff;
+
+ if (forEncryption)
+ {
+ outputLen = inLen + macSize;
+ if (output.length < (outputLen + outOff))
+ {
+ throw new DataLengthException("Output buffer too short.");
+ }
+
+ calculateMac(in, inOff, inLen, macBlock);
+
+ ctrCipher.processBlock(macBlock, 0, macBlock, 0); // S0
+
+ while (inIndex < (inOff + inLen - blockSize)) // S1...
+ {
+ ctrCipher.processBlock(in, inIndex, output, outIndex);
+ outIndex += blockSize;
+ inIndex += blockSize;
+ }
+
+ byte[] block = new byte[blockSize];
+
+ System.arraycopy(in, inIndex, block, 0, inLen + inOff - inIndex);
+
+ ctrCipher.processBlock(block, 0, block, 0);
+
+ System.arraycopy(block, 0, output, outIndex, inLen + inOff - inIndex);
+
+ System.arraycopy(macBlock, 0, output, outOff + inLen, macSize);
+ }
+ else
+ {
+ if (inLen < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ outputLen = inLen - macSize;
+ if (output.length < (outputLen + outOff))
+ {
+ throw new DataLengthException("Output buffer too short.");
+ }
+
+ System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize);
+
+ ctrCipher.processBlock(macBlock, 0, macBlock, 0);
+
+ for (int i = macSize; i != macBlock.length; i++)
+ {
+ macBlock[i] = 0;
+ }
+
+ while (inIndex < (inOff + outputLen - blockSize))
+ {
+ ctrCipher.processBlock(in, inIndex, output, outIndex);
+ outIndex += blockSize;
+ inIndex += blockSize;
+ }
+
+ byte[] block = new byte[blockSize];
+
+ System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff));
+
+ ctrCipher.processBlock(block, 0, block, 0);
+
+ System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
+
+ byte[] calculatedMacBlock = new byte[blockSize];
+
+ calculateMac(output, outOff, outputLen, calculatedMacBlock);
+
+ if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
+ {
+ throw new InvalidCipherTextException("mac check in CCM failed");
+ }
+ }
+
+ return outputLen;
+ }
+
+ private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
+ {
+ Mac cMac = new CBCBlockCipherMac(cipher, macSize * 8);
+
+ cMac.init(keyParam);
+
+ //
+ // build b0
+ //
+ byte[] b0 = new byte[16];
+
+ if (hasAssociatedText())
+ {
+ b0[0] |= 0x40;
+ }
+
+ b0[0] |= (((cMac.getMacSize() - 2) / 2) & 0x7) << 3;
+
+ b0[0] |= ((15 - nonce.length) - 1) & 0x7;
+
+ System.arraycopy(nonce, 0, b0, 1, nonce.length);
+
+ int q = dataLen;
+ int count = 1;
+ while (q > 0)
+ {
+ b0[b0.length - count] = (byte)(q & 0xff);
+ q >>>= 8;
+ count++;
+ }
+
+ cMac.update(b0, 0, b0.length);
+
+ //
+ // process associated text
+ //
+ if (hasAssociatedText())
+ {
+ int extra;
+
+ int textLength = getAssociatedTextLength();
+ if (textLength < ((1 << 16) - (1 << 8)))
+ {
+ cMac.update((byte)(textLength >> 8));
+ cMac.update((byte)textLength);
+
+ extra = 2;
+ }
+ else // can't go any higher than 2^32
+ {
+ cMac.update((byte)0xff);
+ cMac.update((byte)0xfe);
+ cMac.update((byte)(textLength >> 24));
+ cMac.update((byte)(textLength >> 16));
+ cMac.update((byte)(textLength >> 8));
+ cMac.update((byte)textLength);
+
+ extra = 6;
+ }
+
+ if (initialAssociatedText != null)
+ {
+ cMac.update(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ if (associatedText.size() > 0)
+ {
+ cMac.update(associatedText.getBuffer(), 0, associatedText.size());
+ }
+
+ extra = (extra + textLength) % 16;
+ if (extra != 0)
+ {
+ for (int i = extra; i != 16; i++)
+ {
+ cMac.update((byte)0x00);
+ }
+ }
+ }
+
+ //
+ // add the text
+ //
+ cMac.update(data, dataOff, dataLen);
+
+ return cMac.doFinal(macBlock, 0);
+ }
+
+ private int getAssociatedTextLength()
+ {
+ return associatedText.size() + ((initialAssociatedText == null) ? 0 : initialAssociatedText.length);
+ }
+
+ private boolean hasAssociatedText()
+ {
+ return getAssociatedTextLength() > 0;
+ }
+
+ private class ExposedByteArrayOutputStream
+ extends ByteArrayOutputStream
+ {
+ public ExposedByteArrayOutputStream()
+ {
+ }
+
+ public byte[] getBuffer()
+ {
+ return this.buf;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CFBBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CFBBlockCipher.java
new file mode 100644
index 000000000..c22e34876
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CFBBlockCipher.java
@@ -0,0 +1,269 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+
+/**
+ * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+ */
+public class CFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] cfbV;
+ private byte[] cfbOutV;
+
+ private int blockSize;
+ private BlockCipher cipher = null;
+ private boolean encrypting;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param bitBlockSize the block size in bits (note: a multiple of 8)
+ */
+ public CFBBlockCipher(
+ BlockCipher cipher,
+ int bitBlockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = bitBlockSize / 8;
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.cfbV = new byte[cipher.getBlockSize()];
+ this.cfbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.encrypting = encrypting;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ reset();
+
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
+ cipher.init(true, params);
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // XOR the cfbV with the plaintext producing the ciphertext
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize, blockSize);
+
+ //
+ // XOR the cfbV with the ciphertext producing the plaintext
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Return the current state of the initialisation vector.
+ *
+ * @return current IV
+ */
+ public byte[] getCurrentIV()
+ {
+ return Arrays.clone(cfbV);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, cfbV, 0, IV.length);
+
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CTSBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CTSBlockCipher.java
new file mode 100644
index 000000000..d56fd10bc
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CTSBlockCipher.java
@@ -0,0 +1,287 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+ * be used to produce cipher text which is the same length as the plain text.
+ */
+public class CTSBlockCipher
+ extends BufferedBlockCipher
+{
+ private int blockSize;
+
+ /**
+ * Create a buffered block cipher that uses Cipher Text Stealing
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public CTSBlockCipher(
+ BlockCipher cipher)
+ {
+ if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher) || (cipher instanceof SICBlockCipher))
+ {
+ // TODO: This is broken - need to introduce marker interface to differentiate block cipher primitive from mode?
+ throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
+ }
+
+ this.cipher = cipher;
+
+ blockSize = cipher.getBlockSize();
+
+ buf = new byte[blockSize * 2];
+ bufOff = 0;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(
+ int len)
+ {
+ return len + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if necessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+ }
+
+ buf[bufOff++] = in;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, blockSize);
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
+ * case the exception will never get thrown).
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ if (bufOff + outOff > out.length)
+ {
+ throw new DataLengthException("output buffer to small in doFinal");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int len = bufOff - blockSize;
+ byte[] block = new byte[blockSize];
+
+ if (forEncryption)
+ {
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
+ cipher.processBlock(buf, 0, block, 0);
+
+ if (bufOff > blockSize)
+ {
+ for (int i = bufOff; i != buf.length; i++)
+ {
+ buf[i] = block[i - blockSize];
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ buf[i] ^= block[i - blockSize];
+ }
+
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, blockSize, out, outOff);
+ }
+ else
+ {
+ cipher.processBlock(buf, blockSize, out, outOff);
+ }
+
+ System.arraycopy(block, 0, out, outOff + blockSize, len);
+ }
+ else
+ {
+ System.arraycopy(block, 0, out, outOff, blockSize);
+ }
+ }
+ else
+ {
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
+ byte[] lastBlock = new byte[blockSize];
+
+ if (bufOff > blockSize)
+ {
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, 0, block, 0);
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+ }
+
+ System.arraycopy(buf, blockSize, block, 0, len);
+
+ cipher.processBlock(block, 0, out, outOff);
+ System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+
+ System.arraycopy(block, 0, out, outOff, blockSize);
+ }
+ }
+
+ int offset = bufOff;
+
+ reset();
+
+ return offset;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/EAXBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/EAXBlockCipher.java
new file mode 100644
index 000000000..93826a573
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/EAXBlockCipher.java
@@ -0,0 +1,370 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.macs.CMac;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and
+ * Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
+ *
+ * http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
+ *
+ * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block
+ * cipher to encrypt and authenticate data. It's on-line (the length of a
+ * message isn't needed to begin processing it), has good performances, it's
+ * simple and provably secure (provided the underlying block cipher is secure).
+ *
+ * Of course, this implementations is NOT thread-safe.
+ */
+public class EAXBlockCipher
+ implements AEADBlockCipher
+{
+ private static final byte nTAG = 0x0;
+
+ private static final byte hTAG = 0x1;
+
+ private static final byte cTAG = 0x2;
+
+ private SICBlockCipher cipher;
+
+ private boolean forEncryption;
+
+ private int blockSize;
+
+ private Mac mac;
+
+ private byte[] nonceMac;
+ private byte[] associatedTextMac;
+ private byte[] macBlock;
+
+ private int macSize;
+ private byte[] bufBlock;
+ private int bufOff;
+
+ private boolean cipherInitialized;
+ private byte[] initialAssociatedText;
+
+ /**
+ * Constructor that accepts an instance of a block cipher engine.
+ *
+ * @param cipher the engine to use
+ */
+ public EAXBlockCipher(BlockCipher cipher)
+ {
+ blockSize = cipher.getBlockSize();
+ mac = new CMac(cipher);
+ macBlock = new byte[blockSize];
+ bufBlock = new byte[blockSize * 2];
+ associatedTextMac = new byte[mac.getMacSize()];
+ nonceMac = new byte[mac.getMacSize()];
+ this.cipher = new SICBlockCipher(cipher);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName() + "/EAX";
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher.getUnderlyingCipher();
+ }
+
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ byte[] nonce;
+ CipherParameters keyParam;
+
+ if (params instanceof AEADParameters)
+ {
+ AEADParameters param = (AEADParameters)params;
+
+ nonce = param.getNonce();
+ initialAssociatedText = param.getAssociatedText();
+ macSize = param.getMacSize() / 8;
+ keyParam = param.getKey();
+ }
+ else if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV)params;
+
+ nonce = param.getIV();
+ initialAssociatedText = null;
+ macSize = mac.getMacSize() / 2;
+ keyParam = param.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to EAX");
+ }
+
+ byte[] tag = new byte[blockSize];
+
+ // Key reuse implemented in CBC mode of underlying CMac
+ mac.init(keyParam);
+
+ tag[blockSize - 1] = nTAG;
+ mac.update(tag, 0, blockSize);
+ mac.update(nonce, 0, nonce.length);
+ mac.doFinal(nonceMac, 0);
+
+ // Same BlockCipher underlies this and the mac, so reuse last key on cipher
+ cipher.init(true, new ParametersWithIV(null, nonceMac));
+
+ reset();
+ }
+
+ private void initCipher()
+ {
+ if (cipherInitialized)
+ {
+ return;
+ }
+
+ cipherInitialized = true;
+
+ mac.doFinal(associatedTextMac, 0);
+
+ byte[] tag = new byte[blockSize];
+ tag[blockSize - 1] = cTAG;
+ mac.update(tag, 0, blockSize);
+ }
+
+ private void calculateMac()
+ {
+ byte[] outC = new byte[blockSize];
+ mac.doFinal(outC, 0);
+
+ for (int i = 0; i < macBlock.length; i++)
+ {
+ macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]);
+ }
+ }
+
+ public void reset()
+ {
+ reset(true);
+ }
+
+ private void reset(
+ boolean clearMac)
+ {
+ cipher.reset(); // TODO Redundant since the mac will reset it?
+ mac.reset();
+
+ bufOff = 0;
+ Arrays.fill(bufBlock, (byte)0);
+
+ if (clearMac)
+ {
+ Arrays.fill(macBlock, (byte)0);
+ }
+
+ byte[] tag = new byte[blockSize];
+ tag[blockSize - 1] = hTAG;
+ mac.update(tag, 0, blockSize);
+
+ cipherInitialized = false;
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ public void processAADByte(byte in)
+ {
+ if (cipherInitialized)
+ {
+ throw new IllegalStateException("AAD data cannot be added after encryption/decription processing has begun.");
+ }
+ mac.update(in);
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ if (cipherInitialized)
+ {
+ throw new IllegalStateException("AAD data cannot be added after encryption/decryption processing has begun.");
+ }
+ mac.update(in, inOff, len);
+ }
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ initCipher();
+
+ return process(in, out, outOff);
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ initCipher();
+
+ int resultLen = 0;
+
+ for (int i = 0; i != len; i++)
+ {
+ resultLen += process(in[inOff + i], out, outOff + resultLen);
+ }
+
+ return resultLen;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException
+ {
+ initCipher();
+
+ int extra = bufOff;
+ byte[] tmp = new byte[bufBlock.length];
+
+ bufOff = 0;
+
+ if (forEncryption)
+ {
+ if (out.length < (outOff + extra))
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+ cipher.processBlock(bufBlock, 0, tmp, 0);
+ cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
+
+ System.arraycopy(tmp, 0, out, outOff, extra);
+
+ mac.update(tmp, 0, extra);
+
+ calculateMac();
+
+ System.arraycopy(macBlock, 0, out, outOff + extra, macSize);
+
+ reset(false);
+
+ return extra + macSize;
+ }
+ else
+ {
+ if (extra < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ if (extra > macSize)
+ {
+ mac.update(bufBlock, 0, extra - macSize);
+
+ cipher.processBlock(bufBlock, 0, tmp, 0);
+ cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
+
+ System.arraycopy(tmp, 0, out, outOff, extra - macSize);
+ }
+
+ calculateMac();
+
+ if (!verifyMac(bufBlock, extra - macSize))
+ {
+ throw new InvalidCipherTextException("mac check in EAX failed");
+ }
+
+ reset(false);
+
+ return extra - macSize;
+ }
+ }
+
+ public byte[] getMac()
+ {
+ byte[] mac = new byte[macSize];
+
+ System.arraycopy(macBlock, 0, mac, 0, macSize);
+
+ return mac;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % blockSize;
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ private int process(byte b, byte[] out, int outOff)
+ {
+ bufBlock[bufOff++] = b;
+
+ if (bufOff == bufBlock.length)
+ {
+ // TODO Could move the processByte(s) calls to here
+// initCipher();
+
+ int size;
+
+ if (forEncryption)
+ {
+ size = cipher.processBlock(bufBlock, 0, out, outOff);
+
+ mac.update(out, outOff, blockSize);
+ }
+ else
+ {
+ mac.update(bufBlock, 0, blockSize);
+
+ size = cipher.processBlock(bufBlock, 0, out, outOff);
+ }
+
+ bufOff = blockSize;
+ System.arraycopy(bufBlock, blockSize, bufBlock, 0, blockSize);
+
+ return size;
+ }
+
+ return 0;
+ }
+
+ private boolean verifyMac(byte[] mac, int off)
+ {
+ int nonEqual = 0;
+
+ for (int i = 0; i < macSize; i++)
+ {
+ nonEqual |= (macBlock[i] ^ mac[off + i]);
+ }
+
+ return nonEqual == 0;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GCFBBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GCFBBlockCipher.java
new file mode 100644
index 000000000..41e26fc17
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GCFBBlockCipher.java
@@ -0,0 +1,109 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.ParametersWithSBox;
+
+/**
+ * An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357.
+ */
+public class GCFBBlockCipher
+ implements BlockCipher
+{
+ private static final byte[] C =
+ {
+ 0x69, 0x00, 0x72, 0x22, 0x64, (byte)0xC9, 0x04, 0x23,
+ (byte)0x8D, 0x3A, (byte)0xDB, (byte)0x96, 0x46, (byte)0xE9, 0x2A, (byte)0xC4,
+ 0x18, (byte)0xFE, (byte)0xAC, (byte)0x94, 0x00, (byte)0xED, 0x07, 0x12,
+ (byte)0xC0, (byte)0x86, (byte)0xDC, (byte)0xC2, (byte)0xEF, 0x4C, (byte)0xA9, 0x2B
+ };
+
+ private final CFBBlockCipher cfbEngine;
+
+ private KeyParameter key;
+ private long counter = 0;
+ private boolean forEncryption;
+
+ public GCFBBlockCipher(BlockCipher engine)
+ {
+ this.cfbEngine = new CFBBlockCipher(engine, engine.getBlockSize() * 8);
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ counter = 0;
+ cfbEngine.init(forEncryption, params);
+
+ this.forEncryption = forEncryption;
+
+ if (params instanceof ParametersWithIV)
+ {
+ params = ((ParametersWithIV)params).getParameters();
+ }
+
+ if (params instanceof ParametersWithRandom)
+ {
+ params = ((ParametersWithRandom)params).getParameters();
+ }
+
+ if (params instanceof ParametersWithSBox)
+ {
+ params = ((ParametersWithSBox)params).getParameters();
+ }
+
+ key = (KeyParameter)params;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "G" + cfbEngine.getAlgorithmName();
+ }
+
+ public int getBlockSize()
+ {
+ return cfbEngine.getBlockSize();
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (counter > 0 && counter % 1024 == 0)
+ {
+ BlockCipher base = cfbEngine.getUnderlyingCipher();
+
+ base.init(false, key);
+
+ byte[] nextKey = new byte[32];
+
+ base.processBlock(C, 0, nextKey, 0);
+ base.processBlock(C, 8, nextKey, 8);
+ base.processBlock(C, 16, nextKey, 16);
+ base.processBlock(C, 24, nextKey, 24);
+
+ key = new KeyParameter(nextKey);
+
+ byte[] iv = new byte[8];
+
+ base.init(true, key);
+
+ base.processBlock(cfbEngine.getCurrentIV(), 0, iv, 0);
+
+ cfbEngine.init(forEncryption, new ParametersWithIV(key, iv));
+ }
+
+ counter += cfbEngine.getBlockSize();
+
+ return cfbEngine.processBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ counter = 0;
+ cfbEngine.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GCMBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GCMBlockCipher.java
new file mode 100644
index 000000000..419bb0325
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GCMBlockCipher.java
@@ -0,0 +1,574 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.modes.gcm.GCMExponentiator;
+import org.spongycastle.crypto.modes.gcm.GCMMultiplier;
+import org.spongycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
+import org.spongycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Implements the Galois/Counter mode (GCM) detailed in
+ * NIST Special Publication 800-38D.
+ */
+public class GCMBlockCipher
+ implements AEADBlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+
+ // not final due to a compiler bug
+ private BlockCipher cipher;
+ private GCMMultiplier multiplier;
+ private GCMExponentiator exp;
+
+ // These fields are set by init and not modified by processing
+ private boolean forEncryption;
+ private int macSize;
+ private byte[] nonce;
+ private byte[] initialAssociatedText;
+ private byte[] H;
+ private byte[] J0;
+
+ // These fields are modified during processing
+ private byte[] bufBlock;
+ private byte[] macBlock;
+ private byte[] S, S_at, S_atPre;
+ private byte[] counter;
+ private int bufOff;
+ private long totalLength;
+ private byte[] atBlock;
+ private int atBlockPos;
+ private long atLength;
+ private long atLengthPre;
+
+ public GCMBlockCipher(BlockCipher c)
+ {
+ this(c, null);
+ }
+
+ public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
+ {
+ if (c.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException(
+ "cipher required with a block size of " + BLOCK_SIZE + ".");
+ }
+
+ if (m == null)
+ {
+ // TODO Consider a static property specifying default multiplier
+ m = new Tables8kGCMMultiplier();
+ }
+
+ this.cipher = c;
+ this.multiplier = m;
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/GCM";
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+ this.macBlock = null;
+
+ KeyParameter keyParam;
+
+ if (params instanceof AEADParameters)
+ {
+ AEADParameters param = (AEADParameters)params;
+
+ nonce = param.getNonce();
+ initialAssociatedText = param.getAssociatedText();
+
+ int macSizeBits = param.getMacSize();
+ if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+ }
+
+ macSize = macSizeBits / 8;
+ keyParam = param.getKey();
+ }
+ else if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV)params;
+
+ nonce = param.getIV();
+ initialAssociatedText = null;
+ macSize = 16;
+ keyParam = (KeyParameter)param.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to GCM");
+ }
+
+ int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize);
+ this.bufBlock = new byte[bufLength];
+
+ if (nonce == null || nonce.length < 1)
+ {
+ throw new IllegalArgumentException("IV must be at least 1 byte");
+ }
+
+ // TODO This should be configurable by init parameters
+ // (but must be 16 if nonce length not 12) (BLOCK_SIZE?)
+// this.tagLength = 16;
+
+ // Cipher always used in forward mode
+ // if keyParam is null we're reusing the last key.
+ if (keyParam != null)
+ {
+ cipher.init(true, keyParam);
+
+ this.H = new byte[BLOCK_SIZE];
+ cipher.processBlock(H, 0, H, 0);
+
+ // GCMMultiplier tables don't change unless the key changes (and are expensive to init)
+ multiplier.init(H);
+ exp = null;
+ }
+
+ this.J0 = new byte[BLOCK_SIZE];
+
+ if (nonce.length == 12)
+ {
+ System.arraycopy(nonce, 0, J0, 0, nonce.length);
+ this.J0[BLOCK_SIZE - 1] = 0x01;
+ }
+ else
+ {
+ gHASH(J0, nonce, nonce.length);
+ byte[] X = new byte[BLOCK_SIZE];
+ Pack.longToBigEndian((long)nonce.length * 8, X, 8);
+ gHASHBlock(J0, X);
+ }
+
+ this.S = new byte[BLOCK_SIZE];
+ this.S_at = new byte[BLOCK_SIZE];
+ this.S_atPre = new byte[BLOCK_SIZE];
+ this.atBlock = new byte[BLOCK_SIZE];
+ this.atBlockPos = 0;
+ this.atLength = 0;
+ this.atLengthPre = 0;
+ this.counter = Arrays.clone(J0);
+ this.bufOff = 0;
+ this.totalLength = 0;
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ public byte[] getMac()
+ {
+ return Arrays.clone(macBlock);
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % BLOCK_SIZE;
+ }
+
+ public void processAADByte(byte in)
+ {
+ atBlock[atBlockPos] = in;
+ if (++atBlockPos == BLOCK_SIZE)
+ {
+ // Hash each block as it fills
+ gHASHBlock(S_at, atBlock);
+ atBlockPos = 0;
+ atLength += BLOCK_SIZE;
+ }
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ atBlock[atBlockPos] = in[inOff + i];
+ if (++atBlockPos == BLOCK_SIZE)
+ {
+ // Hash each block as it fills
+ gHASHBlock(S_at, atBlock);
+ atBlockPos = 0;
+ atLength += BLOCK_SIZE;
+ }
+ }
+ }
+
+ private void initCipher()
+ {
+ if (atLength > 0)
+ {
+ System.arraycopy(S_at, 0, S_atPre, 0, BLOCK_SIZE);
+ atLengthPre = atLength;
+ }
+
+ // Finish hash for partial AAD block
+ if (atBlockPos > 0)
+ {
+ gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
+ atLengthPre += atBlockPos;
+ }
+
+ if (atLengthPre > 0)
+ {
+ System.arraycopy(S_atPre, 0, S, 0, BLOCK_SIZE);
+ }
+ }
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ bufBlock[bufOff] = in;
+ if (++bufOff == bufBlock.length)
+ {
+ outputBlock(out, outOff);
+ return BLOCK_SIZE;
+ }
+ return 0;
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ int resultLen = 0;
+
+ for (int i = 0; i < len; ++i)
+ {
+ bufBlock[bufOff] = in[inOff + i];
+ if (++bufOff == bufBlock.length)
+ {
+ outputBlock(out, outOff + resultLen);
+ resultLen += BLOCK_SIZE;
+ }
+ }
+
+ return resultLen;
+ }
+
+ private void outputBlock(byte[] output, int offset)
+ {
+ if (totalLength == 0)
+ {
+ initCipher();
+ }
+ gCTRBlock(bufBlock, output, offset);
+ if (forEncryption)
+ {
+ bufOff = 0;
+ }
+ else
+ {
+ System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+ bufOff = macSize;
+ }
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException
+ {
+ if (totalLength == 0)
+ {
+ initCipher();
+ }
+
+ int extra = bufOff;
+ if (!forEncryption)
+ {
+ if (extra < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ extra -= macSize;
+ }
+
+ if (extra > 0)
+ {
+ gCTRPartial(bufBlock, 0, extra, out, outOff);
+ }
+
+ atLength += atBlockPos;
+
+ if (atLength > atLengthPre)
+ {
+ /*
+ * Some AAD was sent after the cipher started. We determine the difference b/w the hash value
+ * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
+ * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
+ * partial) cipher-text blocks produced, and adjust the current hash.
+ */
+
+ // Finish hash for partial AAD block
+ if (atBlockPos > 0)
+ {
+ gHASHPartial(S_at, atBlock, 0, atBlockPos);
+ }
+
+ // Find the difference between the AAD hashes
+ if (atLengthPre > 0)
+ {
+ xor(S_at, S_atPre);
+ }
+
+ // Number of cipher-text blocks produced
+ long c = ((totalLength * 8) + 127) >>> 7;
+
+ // Calculate the adjustment factor
+ byte[] H_c = new byte[16];
+ if (exp == null)
+ {
+ exp = new Tables1kGCMExponentiator();
+ exp.init(H);
+ }
+ exp.exponentiateX(c, H_c);
+
+ // Carry the difference forward
+ multiply(S_at, H_c);
+
+ // Adjust the current hash
+ xor(S, S_at);
+ }
+
+ // Final gHASH
+ byte[] X = new byte[BLOCK_SIZE];
+ Pack.longToBigEndian(atLength * 8, X, 0);
+ Pack.longToBigEndian(totalLength * 8, X, 8);
+
+ gHASHBlock(S, X);
+
+ // TODO Fix this if tagLength becomes configurable
+ // T = MSBt(GCTRk(J0,S))
+ byte[] tag = new byte[BLOCK_SIZE];
+ cipher.processBlock(J0, 0, tag, 0);
+ xor(tag, S);
+
+ int resultLen = extra;
+
+ // We place into macBlock our calculated value for T
+ this.macBlock = new byte[macSize];
+ System.arraycopy(tag, 0, macBlock, 0, macSize);
+
+ if (forEncryption)
+ {
+ // Append T to the message
+ System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize);
+ resultLen += macSize;
+ }
+ else
+ {
+ // Retrieve the T value from the message and compare to calculated one
+ byte[] msgMac = new byte[macSize];
+ System.arraycopy(bufBlock, extra, msgMac, 0, macSize);
+ if (!Arrays.constantTimeAreEqual(this.macBlock, msgMac))
+ {
+ throw new InvalidCipherTextException("mac check in GCM failed");
+ }
+ }
+
+ reset(false);
+
+ return resultLen;
+ }
+
+ public void reset()
+ {
+ reset(true);
+ }
+
+ private void reset(
+ boolean clearMac)
+ {
+ cipher.reset();
+
+ S = new byte[BLOCK_SIZE];
+ S_at = new byte[BLOCK_SIZE];
+ S_atPre = new byte[BLOCK_SIZE];
+ atBlock = new byte[BLOCK_SIZE];
+ atBlockPos = 0;
+ atLength = 0;
+ atLengthPre = 0;
+ counter = Arrays.clone(J0);
+ bufOff = 0;
+ totalLength = 0;
+
+ if (bufBlock != null)
+ {
+ Arrays.fill(bufBlock, (byte)0);
+ }
+
+ if (clearMac)
+ {
+ macBlock = null;
+ }
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ private void gCTRBlock(byte[] block, byte[] out, int outOff)
+ {
+ byte[] tmp = getNextCounterBlock();
+
+ xor(tmp, block);
+ System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE);
+
+ gHASHBlock(S, forEncryption ? tmp : block);
+
+ totalLength += BLOCK_SIZE;
+ }
+
+ private void gCTRPartial(byte[] buf, int off, int len, byte[] out, int outOff)
+ {
+ byte[] tmp = getNextCounterBlock();
+
+ xor(tmp, buf, off, len);
+ System.arraycopy(tmp, 0, out, outOff, len);
+
+ gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
+
+ totalLength += len;
+ }
+
+ private void gHASH(byte[] Y, byte[] b, int len)
+ {
+ for (int pos = 0; pos < len; pos += BLOCK_SIZE)
+ {
+ int num = Math.min(len - pos, BLOCK_SIZE);
+ gHASHPartial(Y, b, pos, num);
+ }
+ }
+
+ private void gHASHBlock(byte[] Y, byte[] b)
+ {
+ xor(Y, b);
+ multiplier.multiplyH(Y);
+ }
+
+ private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
+ {
+ xor(Y, b, off, len);
+ multiplier.multiplyH(Y);
+ }
+
+ private byte[] getNextCounterBlock()
+ {
+ for (int i = 15; i >= 12; --i)
+ {
+ byte b = (byte)((counter[i] + 1) & 0xff);
+ counter[i] = b;
+
+ if (b != 0)
+ {
+ break;
+ }
+ }
+
+ byte[] tmp = new byte[BLOCK_SIZE];
+ // TODO Sure would be nice if ciphers could operate on int[]
+ cipher.processBlock(counter, 0, tmp, 0);
+ return tmp;
+ }
+
+ private static void multiply(byte[] block, byte[] val)
+ {
+ byte[] tmp = Arrays.clone(block);
+ byte[] c = new byte[16];
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = val[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(c, tmp);
+ }
+
+ boolean lsb = (tmp[15] & 1) != 0;
+ shiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+// xor(v, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
+
+ System.arraycopy(c, 0, block, 0, 16);
+ }
+
+ private static void shiftRight(byte[] block)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ block[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+
+ private static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ private static void xor(byte[] block, byte[] val, int off, int len)
+ {
+ while (len-- > 0)
+ {
+ block[len] ^= val[off + len];
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GOFBBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GOFBBlockCipher.java
new file mode 100644
index 000000000..41fe48609
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/GOFBBlockCipher.java
@@ -0,0 +1,237 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements the GOST 28147 OFB counter mode (GCTR).
+ */
+public class GOFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] ofbV;
+ private byte[] ofbOutV;
+
+ private final int blockSize;
+ private final BlockCipher cipher;
+
+ boolean firstStep = true;
+ int N3;
+ int N4;
+ static final int C1 = 16843012; //00000001000000010000000100000100
+ static final int C2 = 16843009; //00000001000000010000000100000001
+
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * counter mode (must have a 64 bit block size).
+ */
+ public GOFBBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.getBlockSize();
+
+ if (blockSize != 8)
+ {
+ throw new IllegalArgumentException("GCTR only for 64 bit block ciphers");
+ }
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.ofbV = new byte[cipher.getBlockSize()];
+ this.ofbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting, //ignored by this CTR mode
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ firstStep = true;
+ N3 = 0;
+ N4 = 0;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if params is null we reuse the current working key.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ reset();
+
+ // if params is null we reuse the current working key.
+ if (params != null)
+ {
+ cipher.init(true, params);
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/GCTR"
+ * and the block size in bits
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/GCTR";
+ }
+
+
+ /**
+ * return the block size we are operating at (in bytes).
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (firstStep)
+ {
+ firstStep = false;
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+ N3 = bytesToint(ofbOutV, 0);
+ N4 = bytesToint(ofbOutV, 4);
+ }
+ N3 += C2;
+ N4 += C1;
+ intTobytes(N3, ofbV, 0);
+ intTobytes(N4, ofbV, 4);
+
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+
+ //
+ // XOR the ofbV with the plaintext producing the cipher text (and
+ // the next input block).
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
+ System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the feedback vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ firstStep = true;
+ N3 = 0;
+ N4 = 0;
+ System.arraycopy(IV, 0, ofbV, 0, IV.length);
+
+ cipher.reset();
+ }
+
+ //array of bytes to type int
+ private int bytesToint(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff + 3] << 24) & 0xff000000) + ((in[inOff + 2] << 16) & 0xff0000) +
+ ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private void intTobytes(
+ int num,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 3] = (byte)(num >>> 24);
+ out[outOff + 2] = (byte)(num >>> 16);
+ out[outOff + 1] = (byte)(num >>> 8);
+ out[outOff] = (byte)num;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OCBBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OCBBlockCipher.java
new file mode 100644
index 000000000..c0a4fcf0f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OCBBlockCipher.java
@@ -0,0 +1,565 @@
+package org.spongycastle.crypto.modes;
+
+import java.util.Vector;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+
+/**
+ * An implementation of the "work in progress" Internet-Draft The OCB Authenticated-Encryption
+ * Algorithm, licensed per:
+ *
+ * This version applies the CTS algorithm from one block up, rather than following the errata update issued in 2004, where CTS mode is applied
+ * from greater than 1 block up and the first block is processed using CBC mode.
+ *
+ * For further info see RFC 2440.
+ */
+public class OpenPGPCFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] FR;
+ private byte[] FRE;
+
+ private BlockCipher cipher;
+
+ private int count;
+ private int blockSize;
+ private boolean forEncryption;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ */
+ public OpenPGPCFBBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+
+ this.blockSize = cipher.getBlockSize();
+ this.IV = new byte[blockSize];
+ this.FR = new byte[blockSize];
+ this.FRE = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/PGPCFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/OpenPGPCFB";
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ count = 0;
+
+ System.arraycopy(IV, 0, FR, 0, FR.length);
+
+ cipher.reset();
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ reset();
+
+ cipher.init(true, params);
+ }
+
+ /**
+ * Encrypt one byte of data according to CFB mode.
+ * @param data the byte to encrypt
+ * @param blockOff offset in the current block
+ * @return the encrypted byte
+ */
+ private byte encryptByte(byte data, int blockOff)
+ {
+ return (byte)(FRE[blockOff] ^ data);
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count > blockSize)
+ {
+ FR[blockSize - 2] = out[outOff] = encryptByte(in[inOff], blockSize - 2);
+ FR[blockSize - 1] = out[outOff + 1] = encryptByte(in[inOff + 1], blockSize - 1);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ FR[n - 2] = out[outOff + n] = encryptByte(in[inOff + n], n - 2);
+ }
+ }
+ else if (count == 0)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ count += blockSize;
+ }
+ else if (count == blockSize)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ out[outOff] = encryptByte(in[inOff], 0);
+ out[outOff + 1] = encryptByte(in[inOff + 1], 1);
+
+ //
+ // do reset
+ //
+ System.arraycopy(FR, 2, FR, 0, blockSize - 2);
+ System.arraycopy(out, outOff, FR, blockSize - 2, 2);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ FR[n - 2] = out[outOff + n] = encryptByte(in[inOff + n], n - 2);
+ }
+
+ count += blockSize;
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count > blockSize)
+ {
+ byte inVal = in[inOff];
+ FR[blockSize - 2] = inVal;
+ out[outOff] = encryptByte(inVal, blockSize - 2);
+
+ inVal = in[inOff + 1];
+ FR[blockSize - 1] = inVal;
+ out[outOff + 1] = encryptByte(inVal, blockSize - 1);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ inVal = in[inOff + n];
+ FR[n - 2] = inVal;
+ out[outOff + n] = encryptByte(inVal, n - 2);
+ }
+ }
+ else if (count == 0)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = in[inOff + n];
+ out[n] = encryptByte(in[inOff + n], n);
+ }
+
+ count += blockSize;
+ }
+ else if (count == blockSize)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ byte inVal1 = in[inOff];
+ byte inVal2 = in[inOff + 1];
+ out[outOff ] = encryptByte(inVal1, 0);
+ out[outOff + 1] = encryptByte(inVal2, 1);
+
+ System.arraycopy(FR, 2, FR, 0, blockSize - 2);
+
+ FR[blockSize - 2] = inVal1;
+ FR[blockSize - 1] = inVal2;
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ byte inVal = in[inOff + n];
+ FR[n - 2] = inVal;
+ out[outOff + n] = encryptByte(inVal, n - 2);
+ }
+
+ count += blockSize;
+ }
+
+ return blockSize;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/PGPCFBBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/PGPCFBBlockCipher.java
new file mode 100644
index 000000000..370ce40d9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/PGPCFBBlockCipher.java
@@ -0,0 +1,455 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode on top of a simple cipher. For further info see RFC 2440.
+ */
+public class PGPCFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] FR;
+ private byte[] FRE;
+ private byte[] tmp;
+
+ private BlockCipher cipher;
+
+ private int count;
+ private int blockSize;
+ private boolean forEncryption;
+
+ private boolean inlineIv; // if false we don't need to prepend an IV
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param inlineIv if true this is for PGP CFB with a prepended iv.
+ */
+ public PGPCFBBlockCipher(
+ BlockCipher cipher,
+ boolean inlineIv)
+ {
+ this.cipher = cipher;
+ this.inlineIv = inlineIv;
+
+ this.blockSize = cipher.getBlockSize();
+ this.IV = new byte[blockSize];
+ this.FR = new byte[blockSize];
+ this.FRE = new byte[blockSize];
+ this.tmp = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/PGPCFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ if (inlineIv)
+ {
+ return cipher.getAlgorithmName() + "/PGPCFBwithIV";
+ }
+ else
+ {
+ return cipher.getAlgorithmName() + "/PGPCFB";
+ }
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (inlineIv)
+ {
+ return (forEncryption) ? encryptBlockWithIV(in, inOff, out, outOff) : decryptBlockWithIV(in, inOff, out, outOff);
+ }
+ else
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ count = 0;
+
+ for (int i = 0; i != FR.length; i++)
+ {
+ if (inlineIv)
+ {
+ FR[i] = 0;
+ }
+ else
+ {
+ FR[i] = IV[i]; // if simple mode, key is IV (even if this is zero)
+ }
+ }
+
+ cipher.reset();
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ cipher.init(true, ivParam.getParameters());
+ }
+ else
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+ }
+
+ /**
+ * Encrypt one byte of data according to CFB mode.
+ * @param data the byte to encrypt
+ * @param blockOff where am i in the current block, determines when to resync the block
+ * @returns the encrypted byte
+ */
+ private byte encryptByte(byte data, int blockOff)
+ {
+ return (byte)(FRE[blockOff] ^ data);
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlockWithIV(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if (count == 0)
+ {
+ if ((outOff + 2 * blockSize + 2) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(IV[n], n);
+ }
+
+ System.arraycopy(out, outOff, FR, 0, blockSize);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ out[outOff + blockSize] = encryptByte(IV[blockSize - 2], 0);
+ out[outOff + blockSize + 1] = encryptByte(IV[blockSize - 1], 1);
+
+ System.arraycopy(out, outOff + 2, FR, 0, blockSize);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + blockSize + 2 + n] = encryptByte(in[inOff + n], n);
+ }
+
+ System.arraycopy(out, outOff + blockSize + 2, FR, 0, blockSize);
+
+ count += 2 * blockSize + 2;
+
+ return 2 * blockSize + 2;
+ }
+ else if (count >= blockSize + 2)
+ {
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ System.arraycopy(out, outOff, FR, 0, blockSize);
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlockWithIV(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count == 0)
+ {
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = in[inOff + n];
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ count += blockSize;
+
+ return 0;
+ }
+ else if (count == blockSize)
+ {
+ // copy in buffer so that this mode works if in and out are the same
+ System.arraycopy(in, inOff, tmp, 0, blockSize);
+
+ System.arraycopy(FR, 2, FR, 0, blockSize - 2);
+
+ FR[blockSize - 2] = tmp[0];
+ FR[blockSize - 1] = tmp[1];
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize - 2; n++)
+ {
+ out[outOff + n] = encryptByte(tmp[n + 2], n);
+ }
+
+ System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
+
+ count += 2;
+
+ return blockSize - 2;
+ }
+ else if (count >= blockSize + 2)
+ {
+ // copy in buffer so that this mode works if in and out are the same
+ System.arraycopy(in, inOff, tmp, 0, blockSize);
+
+ out[outOff + 0] = encryptByte(tmp[0], blockSize - 2);
+ out[outOff + 1] = encryptByte(tmp[1], blockSize - 1);
+
+ System.arraycopy(tmp, 0, FR, blockSize - 2, 2);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize - 2; n++)
+ {
+ out[outOff + n + 2] = encryptByte(tmp[n + 2], n);
+ }
+
+ System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
+
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = out[outOff + n];
+ }
+
+ return blockSize;
+
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = in[inOff + n];
+ }
+
+ return blockSize;
+
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/PaddedBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/PaddedBlockCipher.java
new file mode 100644
index 000000000..23d6de745
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/PaddedBlockCipher.java
@@ -0,0 +1,253 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion with PKCS5/PKCS7 padding. The PaddedBlockCipher
+ * outputs a block only when the buffer is full and more data is being added,
+ * or on a doFinal (unless the current block in the buffer is a pad block).
+ * The padding mechanism used is the one outlined in PKCS5/PKCS7.
+ *
+ * @deprecated use org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher instead.
+ */
+public class PaddedBlockCipher
+ extends BufferedBlockCipher
+{
+ /**
+ * Create a buffered block cipher with, or without, padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public PaddedBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ if (forEncryption)
+ {
+ return total + buf.length;
+ }
+
+ return total;
+ }
+
+ return total - leftOver + buf.length;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > buf.length)
+ {
+ resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer. If the buffer is currently
+ * full and padding needs to be added a call to doFinal will produce
+ * 2 * getBlockSize() bytes.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output or we are decrypting and the input is not block size aligned.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+
+ if (forEncryption)
+ {
+ if (bufOff == blockSize)
+ {
+ if ((outOff + 2 * blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ //
+ // add PKCS7 padding
+ //
+ byte code = (byte)(blockSize - bufOff);
+
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = code;
+ bufOff++;
+ }
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ resultLen = cipher.processBlock(buf, 0, buf, 0);
+ bufOff = 0;
+ }
+ else
+ {
+ throw new DataLengthException("last block incomplete in decryption");
+ }
+
+ //
+ // remove PKCS7 padding
+ //
+ int count = buf[blockSize - 1] & 0xff;
+
+ if ((count < 0) || (count > blockSize))
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ resultLen -= count;
+
+ System.arraycopy(buf, 0, out, outOff, resultLen);
+ }
+
+ reset();
+
+ return resultLen;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/SICBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/SICBlockCipher.java
new file mode 100644
index 000000000..f228baf35
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/SICBlockCipher.java
@@ -0,0 +1,113 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implements the Segmented Integer Counter (SIC) mode on top of a simple
+ * block cipher. This mode is also known as CTR mode.
+ */
+public class SICBlockCipher
+ implements BlockCipher
+{
+ private final BlockCipher cipher;
+ private final int blockSize;
+
+ private byte[] IV;
+ private byte[] counter;
+ private byte[] counterOut;
+
+
+ /**
+ * Basic constructor.
+ *
+ * @param c the block cipher to be used.
+ */
+ public SICBlockCipher(BlockCipher c)
+ {
+ this.cipher = c;
+ this.blockSize = cipher.getBlockSize();
+ this.IV = new byte[blockSize];
+ this.counter = new byte[blockSize];
+ this.counterOut = new byte[blockSize];
+ }
+
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+
+ public void init(
+ boolean forEncryption, //ignored by this CTR mode
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("SIC mode requires ParametersWithIV");
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/SIC";
+ }
+
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ cipher.processBlock(counter, 0, counterOut, 0);
+
+ //
+ // XOR the counterOut with the plaintext producing the cipher text
+ //
+ for (int i = 0; i < counterOut.length; i++)
+ {
+ out[outOff + i] = (byte)(counterOut[i] ^ in[inOff + i]);
+ }
+
+ // increment counter by 1.
+ for (int i = counter.length - 1; i >= 0 && ++counter[i] == 0; i--)
+ {
+ ; // do nothing - pre-increment and test for 0 in counter does the job.
+ }
+
+ return counter.length;
+ }
+
+
+ public void reset()
+ {
+ System.arraycopy(IV, 0, counter, 0, counter.length);
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/BasicGCMExponentiator.java
new file mode 100644
index 000000000..b84fe6f0f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -0,0 +1,36 @@
+package org.spongycastle.crypto.modes.gcm;
+
+import org.spongycastle.util.Arrays;
+
+public class BasicGCMExponentiator implements GCMExponentiator
+{
+ private int[] x;
+
+ public void init(byte[] x)
+ {
+ this.x = GCMUtil.asInts(x);
+ }
+
+ public void exponentiateX(long pow, byte[] output)
+ {
+ // Initial value is little-endian 1
+ int[] y = GCMUtil.oneAsInts();
+
+ if (pow > 0)
+ {
+ int[] powX = Arrays.clone(x);
+ do
+ {
+ if ((pow & 1L) != 0)
+ {
+ GCMUtil.multiply(y, powX);
+ }
+ GCMUtil.multiply(powX, powX);
+ pow >>>= 1;
+ }
+ while (pow > 0);
+ }
+
+ GCMUtil.asBytes(y, output);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/BasicGCMMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/BasicGCMMultiplier.java
new file mode 100644
index 000000000..33866a9a4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/BasicGCMMultiplier.java
@@ -0,0 +1,18 @@
+package org.spongycastle.crypto.modes.gcm;
+
+import org.spongycastle.util.Arrays;
+
+public class BasicGCMMultiplier implements GCMMultiplier
+{
+ private byte[] H;
+
+ public void init(byte[] H)
+ {
+ this.H = Arrays.clone(H);
+ }
+
+ public void multiplyH(byte[] x)
+ {
+ GCMUtil.multiply(x, H);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMExponentiator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMExponentiator.java
new file mode 100644
index 000000000..1ed19fbdc
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMExponentiator.java
@@ -0,0 +1,7 @@
+package org.spongycastle.crypto.modes.gcm;
+
+public interface GCMExponentiator
+{
+ void init(byte[] x);
+ void exponentiateX(long pow, byte[] output);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMMultiplier.java
new file mode 100644
index 000000000..74afee2f6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMMultiplier.java
@@ -0,0 +1,7 @@
+package org.spongycastle.crypto.modes.gcm;
+
+public interface GCMMultiplier
+{
+ void init(byte[] H);
+ void multiplyH(byte[] x);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMUtil.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMUtil.java
new file mode 100644
index 000000000..ffb21997c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/GCMUtil.java
@@ -0,0 +1,457 @@
+package org.spongycastle.crypto.modes.gcm;
+
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+
+abstract class GCMUtil
+{
+ private static final int E1 = 0xe1000000;
+ private static final byte E1B = (byte)0xe1;
+ private static final long E1L = (E1 & 0xFFFFFFFFL) << 24;
+
+ private static int[] generateLookup()
+ {
+ int[] lookup = new int[256];
+
+ for (int c = 0; c < 256; ++c)
+ {
+ int v = 0;
+ for (int i = 7; i >= 0; --i)
+ {
+ if ((c & (1 << i)) != 0)
+ {
+ v ^= (E1 >>> (7 - i));
+ }
+ }
+ lookup[c] = v;
+ }
+
+ return lookup;
+ }
+
+ private static final int[] LOOKUP = generateLookup();
+
+ static byte[] oneAsBytes()
+ {
+ byte[] tmp = new byte[16];
+ tmp[0] = (byte)0x80;
+ return tmp;
+ }
+
+ static int[] oneAsInts()
+ {
+ int[] tmp = new int[4];
+ tmp[0] = 1 << 31;
+ return tmp;
+ }
+
+ static long[] oneAsLongs()
+ {
+ long[] tmp = new long[2];
+ tmp[0] = 1L << 63;
+ return tmp;
+ }
+
+ static byte[] asBytes(int[] x)
+ {
+ byte[] z = new byte[16];
+ Pack.intToBigEndian(x, z, 0);
+ return z;
+ }
+
+ static void asBytes(int[] x, byte[] z)
+ {
+ Pack.intToBigEndian(x, z, 0);
+ }
+
+ static byte[] asBytes(long[] x)
+ {
+ byte[] z = new byte[16];
+ Pack.longToBigEndian(x, z, 0);
+ return z;
+ }
+
+ static void asBytes(long[] x, byte[] z)
+ {
+ Pack.longToBigEndian(x, z, 0);
+ }
+
+ static int[] asInts(byte[] x)
+ {
+ int[] z = new int[4];
+ Pack.bigEndianToInt(x, 0, z);
+ return z;
+ }
+
+ static void asInts(byte[] x, int[] z)
+ {
+ Pack.bigEndianToInt(x, 0, z);
+ }
+
+ static long[] asLongs(byte[] x)
+ {
+ long[] z = new long[2];
+ Pack.bigEndianToLong(x, 0, z);
+ return z;
+ }
+
+ static void asLongs(byte[] x, long[] z)
+ {
+ Pack.bigEndianToLong(x, 0, z);
+ }
+
+ static void multiply(byte[] x, byte[] y)
+ {
+ byte[] r0 = Arrays.clone(x);
+ byte[] r1 = new byte[16];
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = y[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(r1, r0);
+ }
+
+ if (shiftRight(r0) != 0)
+ {
+ r0[0] ^= E1B;
+ }
+ }
+ }
+
+ System.arraycopy(r1, 0, x, 0, 16);
+ }
+
+ static void multiply(int[] x, int[] y)
+ {
+ int[] r0 = Arrays.clone(x);
+ int[] r1 = new int[4];
+
+ for (int i = 0; i < 4; ++i)
+ {
+ int bits = y[i];
+ for (int j = 31; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(r1, r0);
+ }
+
+ if (shiftRight(r0) != 0)
+ {
+ r0[0] ^= E1;
+ }
+ }
+ }
+
+ System.arraycopy(r1, 0, x, 0, 4);
+ }
+
+ static void multiply(long[] x, long[] y)
+ {
+ long[] r0 = new long[]{ x[0], x[1] };
+ long[] r1 = new long[2];
+
+ for (int i = 0; i < 2; ++i)
+ {
+ long bits = y[i];
+ for (int j = 63; j >= 0; --j)
+ {
+ if ((bits & (1L << j)) != 0)
+ {
+ xor(r1, r0);
+ }
+
+ if (shiftRight(r0) != 0)
+ {
+ r0[0] ^= E1L;
+ }
+ }
+ }
+
+ x[0] = r1[0];
+ x[1] = r1[1];
+ }
+
+ // P is the value with only bit i=1 set
+ static void multiplyP(int[] x)
+ {
+ if (shiftRight(x) != 0)
+ {
+ x[0] ^= E1;
+ }
+ }
+
+ static void multiplyP(int[] x, int[] y)
+ {
+ if (shiftRight(x, y) != 0)
+ {
+ y[0] ^= E1;
+ }
+ }
+
+ // P is the value with only bit i=1 set
+ static void multiplyP8(int[] x)
+ {
+// for (int i = 8; i != 0; --i)
+// {
+// multiplyP(x);
+// }
+
+ int c = shiftRightN(x, 8);
+ x[0] ^= LOOKUP[c >>> 24];
+ }
+
+ static void multiplyP8(int[] x, int[] y)
+ {
+ int c = shiftRightN(x, 8, y);
+ y[0] ^= LOOKUP[c >>> 24];
+ }
+
+ static byte shiftRight(byte[] x)
+ {
+// int c = 0;
+// for (int i = 0; i < 16; ++i)
+// {
+// int b = x[i] & 0xff;
+// x[i] = (byte)((b >>> 1) | c);
+// c = (b & 1) << 7;
+// }
+// return (byte)c;
+
+ int i = 0, c = 0;
+ do
+ {
+ int b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ }
+ while (i < 16);
+ return (byte)c;
+ }
+
+ static byte shiftRight(byte[] x, byte[] z)
+ {
+// int c = 0;
+// for (int i = 0; i < 16; ++i)
+// {
+// int b = x[i] & 0xff;
+// z[i] = (byte) ((b >>> 1) | c);
+// c = (b & 1) << 7;
+// }
+// return (byte) c;
+
+ int i = 0, c = 0;
+ do
+ {
+ int b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ }
+ while (i < 16);
+ return (byte)c;
+ }
+
+ static int shiftRight(int[] x)
+ {
+// int c = 0;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// x[i] = (b >>> 1) | c;
+// c = b << 31;
+// }
+// return c;
+
+ int b = x[0];
+ x[0] = b >>> 1;
+ int c = b << 31;
+ b = x[1];
+ x[1] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[2];
+ x[2] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[3];
+ x[3] = (b >>> 1) | c;
+ return b << 31;
+ }
+
+ static int shiftRight(int[] x, int[] z)
+ {
+// int c = 0;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// z[i] = (b >>> 1) | c;
+// c = b << 31;
+// }
+// return c;
+
+ int b = x[0];
+ z[0] = b >>> 1;
+ int c = b << 31;
+ b = x[1];
+ z[1] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[2];
+ z[2] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[3];
+ z[3] = (b >>> 1) | c;
+ return b << 31;
+ }
+
+ static long shiftRight(long[] x)
+ {
+ long b = x[0];
+ x[0] = b >>> 1;
+ long c = b << 63;
+ b = x[1];
+ x[1] = (b >>> 1) | c;
+ return b << 63;
+ }
+
+ static long shiftRight(long[] x, long[] z)
+ {
+ long b = x[0];
+ z[0] = b >>> 1;
+ long c = b << 63;
+ b = x[1];
+ z[1] = (b >>> 1) | c;
+ return b << 63;
+ }
+
+ static int shiftRightN(int[] x, int n)
+ {
+// int c = 0, nInv = 32 - n;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// x[i] = (b >>> n) | c;
+// c = b << nInv;
+// }
+// return c;
+
+ int b = x[0], nInv = 32 - n;
+ x[0] = b >>> n;
+ int c = b << nInv;
+ b = x[1];
+ x[1] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[2];
+ x[2] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[3];
+ x[3] = (b >>> n) | c;
+ return b << nInv;
+ }
+
+ static int shiftRightN(int[] x, int n, int[] z)
+ {
+// int c = 0, nInv = 32 - n;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// z[i] = (b >>> n) | c;
+// c = b << nInv;
+// }
+// return c;
+
+ int b = x[0], nInv = 32 - n;
+ z[0] = b >>> n;
+ int c = b << nInv;
+ b = x[1];
+ z[1] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[2];
+ z[2] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[3];
+ z[3] = (b >>> n) | c;
+ return b << nInv;
+ }
+
+ static void xor(byte[] x, byte[] y)
+ {
+ int i = 0;
+ do
+ {
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ }
+ while (i < 16);
+ }
+
+ static void xor(byte[] x, byte[] y, int yOff, int yLen)
+ {
+ while (yLen-- > 0)
+ {
+ x[yLen] ^= y[yOff + yLen];
+ }
+ }
+
+ static void xor(byte[] x, byte[] y, byte[] z)
+ {
+ int i = 0;
+ do
+ {
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ }
+ while (i < 16);
+ }
+
+ static void xor(int[] x, int[] y)
+ {
+ x[0] ^= y[0];
+ x[1] ^= y[1];
+ x[2] ^= y[2];
+ x[3] ^= y[3];
+ }
+
+ static void xor(int[] x, int[] y, int[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
+ }
+
+ static void xor(long[] x, long[] y)
+ {
+ x[0] ^= y[0];
+ x[1] ^= y[1];
+ }
+
+ static void xor(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
new file mode 100644
index 000000000..f78188988
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -0,0 +1,58 @@
+package org.spongycastle.crypto.modes.gcm;
+
+import java.util.Vector;
+
+import org.spongycastle.util.Arrays;
+
+public class Tables1kGCMExponentiator implements GCMExponentiator
+{
+ // A lookup table of the power-of-two powers of 'x'
+ // - lookupPowX2[i] = x^(2^i)
+ private Vector lookupPowX2;
+
+ public void init(byte[] x)
+ {
+ int[] y = GCMUtil.asInts(x);
+ if (lookupPowX2 != null && Arrays.areEqual(y, (int[])lookupPowX2.elementAt(0)))
+ {
+ return;
+ }
+
+ lookupPowX2 = new Vector(8);
+ lookupPowX2.addElement(y);
+ }
+
+ public void exponentiateX(long pow, byte[] output)
+ {
+ int[] y = GCMUtil.oneAsInts();
+ int bit = 0;
+ while (pow > 0)
+ {
+ if ((pow & 1L) != 0)
+ {
+ ensureAvailable(bit);
+ GCMUtil.multiply(y, (int[])lookupPowX2.elementAt(bit));
+ }
+ ++bit;
+ pow >>>= 1;
+ }
+
+ GCMUtil.asBytes(y, output);
+ }
+
+ private void ensureAvailable(int bit)
+ {
+ int count = lookupPowX2.size();
+ if (count <= bit)
+ {
+ int[] tmp = (int[])lookupPowX2.elementAt(count - 1);
+ do
+ {
+ tmp = Arrays.clone(tmp);
+ GCMUtil.multiply(tmp, tmp);
+ lookupPowX2.addElement(tmp);
+ }
+ while (++count <= bit);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
new file mode 100644
index 000000000..3c26ea9a7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
@@ -0,0 +1,73 @@
+package org.spongycastle.crypto.modes.gcm;
+
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+
+public class Tables64kGCMMultiplier implements GCMMultiplier
+{
+ private byte[] H;
+ private int[][][] M;
+
+ public void init(byte[] H)
+ {
+ if (M == null)
+ {
+ M = new int[16][256][4];
+ }
+ else if (Arrays.areEqual(this.H, H))
+ {
+ return;
+ }
+
+ this.H = Arrays.clone(H);
+
+ // M[0][0] is ZEROES;
+ GCMUtil.asInts(H, M[0][128]);
+
+ for (int j = 64; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[0][j + j], M[0][j]);
+ }
+
+ int i = 0;
+ for (;;)
+ {
+ for (int j = 2; j < 256; j += j)
+ {
+ for (int k = 1; k < j; ++k)
+ {
+ GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
+ }
+ }
+
+ if (++i == 16)
+ {
+ return;
+ }
+
+ // M[i][0] is ZEROES;
+ for (int j = 128; j > 0; j >>= 1)
+ {
+ GCMUtil.multiplyP8(M[i - 1][j], M[i][j]);
+ }
+ }
+ }
+
+ public void multiplyH(byte[] x)
+ {
+// assert x.Length == 16;
+
+ int[] z = new int[4];
+ for (int i = 15; i >= 0; --i)
+ {
+// GCMUtil.xor(z, M[i][x[i] & 0xff]);
+ int[] m = M[i][x[i] & 0xff];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ }
+
+ Pack.intToBigEndian(z, x, 0);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
new file mode 100644
index 000000000..220370c05
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -0,0 +1,90 @@
+package org.spongycastle.crypto.modes.gcm;
+
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+
+public class Tables8kGCMMultiplier implements GCMMultiplier
+{
+ private byte[] H;
+ private int[][][] M;
+
+ public void init(byte[] H)
+ {
+ if (M == null)
+ {
+ M = new int[32][16][4];
+ }
+ else if (Arrays.areEqual(this.H, H))
+ {
+ return;
+ }
+
+ this.H = Arrays.clone(H);
+
+ // M[0][0] is ZEROES;
+ // M[1][0] is ZEROES;
+ GCMUtil.asInts(H, M[1][8]);
+
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[1][j + j], M[1][j]);
+ }
+
+ GCMUtil.multiplyP(M[1][1], M[0][8]);
+
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[0][j + j], M[0][j]);
+ }
+
+ int i = 0;
+ for (;;)
+ {
+ for (int j = 2; j < 16; j += j)
+ {
+ for (int k = 1; k < j; ++k)
+ {
+ GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
+ }
+ }
+
+ if (++i == 32)
+ {
+ return;
+ }
+
+ if (i > 1)
+ {
+ // M[i][0] is ZEROES;
+ for(int j = 8; j > 0; j >>= 1)
+ {
+ GCMUtil.multiplyP8(M[i - 2][j], M[i][j]);
+ }
+ }
+ }
+ }
+
+ public void multiplyH(byte[] x)
+ {
+// assert x.Length == 16;
+
+ int[] z = new int[4];
+ for (int i = 15; i >= 0; --i)
+ {
+// GCMUtil.xor(z, M[i + i][x[i] & 0x0f]);
+ int[] m = M[i + i][x[i] & 0x0f];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+// GCMUtil.xor(z, M[i + i + 1][(x[i] & 0xf0) >>> 4]);
+ m = M[i + i + 1][(x[i] & 0xf0) >>> 4];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ }
+
+ Pack.intToBigEndian(z, x, 0);
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/paddings/BlockCipherPadding.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/paddings/BlockCipherPadding.java
new file mode 100644
index 000000000..f68915d18
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/paddings/BlockCipherPadding.java
@@ -0,0 +1,48 @@
+package org.spongycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.InvalidCipherTextException;
+
+/**
+ * Block cipher padders are expected to conform to this interface
+ */
+public interface BlockCipherPadding
+{
+ /**
+ * Initialise the padder.
+ *
+ * @param random the source of randomness for the padding, if required.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public String getPaddingName();
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ *
+ * Note: this assumes that the last block of plain text is always
+ * passed to it inside in. i.e. if inOff is zero, indicating the
+ * entire block is to be overwritten with padding the value of in
+ * should be the same as the last block of plain text. The reason
+ * for this is that some modes such as "trailing bit compliment"
+ * base the padding on the last byte of plain text.
+ *
+ * This padding pads the block out with the compliment of the last bit
+ * of the plain text.
+ *
+ * Note: this assumes that the last block of plain text is always
+ * passed to it inside in. i.e. if inOff is zero, indicating the
+ * entire block is to be overwritten with padding the value of in
+ * should be the same as the last block of plain text.
+ *
+ * See "Applied
+ * Cryptography" by Bruce Schneier for more information.
+ *
+ * @return true if the given DES key material is weak or semi-weak,
+ * false otherwise.
+ */
+ public static boolean isWeakKey(
+ byte[] key,
+ int offset)
+ {
+ if (key.length - offset < DES_KEY_LENGTH)
+ {
+ throw new IllegalArgumentException("key material too short.");
+ }
+
+ nextkey: for (int i = 0; i < N_DES_WEAK_KEYS; i++)
+ {
+ for (int j = 0; j < DES_KEY_LENGTH; j++)
+ {
+ if (key[j + offset] != DES_weak_keys[i * DES_KEY_LENGTH + j])
+ {
+ continue nextkey;
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * DES Keys use the LSB as the odd parity bit. This can
+ * be used to check for corrupt keys.
+ *
+ * @param bytes the byte array to set the parity on.
+ */
+ public static void setOddParity(
+ byte[] bytes)
+ {
+ for (int i = 0; i < bytes.length; i++)
+ {
+ int b = bytes[i];
+ bytes[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DESedeParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DESedeParameters.java
new file mode 100644
index 000000000..498371ad4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DESedeParameters.java
@@ -0,0 +1,57 @@
+package org.spongycastle.crypto.params;
+
+public class DESedeParameters
+ extends DESParameters
+{
+ /*
+ * DES-EDE Key length in bytes.
+ */
+ static public final int DES_EDE_KEY_LENGTH = 24;
+
+ public DESedeParameters(
+ byte[] key)
+ {
+ super(key);
+
+ if (isWeakKey(key, 0, key.length))
+ {
+ throw new IllegalArgumentException("attempt to create weak DESede key");
+ }
+ }
+
+ /**
+ * return true if the passed in key is a DES-EDE weak key.
+ *
+ * @param key bytes making up the key
+ * @param offset offset into the byte array the key starts at
+ * @param length number of bytes making up the key
+ */
+ public static boolean isWeakKey(
+ byte[] key,
+ int offset,
+ int length)
+ {
+ for (int i = offset; i < length; i += DES_KEY_LENGTH)
+ {
+ if (DESParameters.isWeakKey(key, i))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * return true if the passed in key is a DES-EDE weak key.
+ *
+ * @param key bytes making up the key
+ * @param offset offset into the byte array the key starts at
+ */
+ public static boolean isWeakKey(
+ byte[] key,
+ int offset)
+ {
+ return isWeakKey(key, offset, key.length - offset);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHKeyGenerationParameters.java
new file mode 100644
index 000000000..246a195ec
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHKeyGenerationParameters.java
@@ -0,0 +1,30 @@
+package org.spongycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class DHKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private DHParameters params;
+
+ public DHKeyGenerationParameters(
+ SecureRandom random,
+ DHParameters params)
+ {
+ super(random, getStrength(params));
+
+ this.params = params;
+ }
+
+ public DHParameters getParameters()
+ {
+ return params;
+ }
+
+ static int getStrength(DHParameters params)
+ {
+ return params.getL() != 0 ? params.getL() : params.getP().bitLength();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHKeyParameters.java
new file mode 100644
index 000000000..f2ac392e1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHKeyParameters.java
@@ -0,0 +1,54 @@
+package org.spongycastle.crypto.params;
+
+
+public class DHKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private DHParameters params;
+
+ protected DHKeyParameters(
+ boolean isPrivate,
+ DHParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public DHParameters getParameters()
+ {
+ return params;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHKeyParameters))
+ {
+ return false;
+ }
+
+ DHKeyParameters dhKey = (DHKeyParameters)obj;
+
+ if (params == null)
+ {
+ return dhKey.getParameters() == null;
+ }
+ else
+ {
+ return params.equals(dhKey.getParameters());
+ }
+ }
+
+ public int hashCode()
+ {
+ int code = isPrivate() ? 0 : 1;
+
+ if (params != null)
+ {
+ code ^= params.hashCode();
+ }
+
+ return code;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHParameters.java
new file mode 100644
index 000000000..d4f03ff34
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHParameters.java
@@ -0,0 +1,189 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class DHParameters
+ implements CipherParameters
+{
+ private static final int DEFAULT_MINIMUM_LENGTH = 160;
+
+ // not final due to compiler bug in "simpler" JDKs
+ private BigInteger g;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger j;
+ private int m;
+ private int l;
+ private DHValidationParameters validation;
+
+ private static int getDefaultMParam(
+ int lParam)
+ {
+ if (lParam == 0)
+ {
+ return DEFAULT_MINIMUM_LENGTH;
+ }
+
+ return lParam < DEFAULT_MINIMUM_LENGTH ? lParam : DEFAULT_MINIMUM_LENGTH;
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g)
+ {
+ this(p, g, null, 0);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q)
+ {
+ this(p, g, q, 0);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int l)
+ {
+ this(p, g, q, getDefaultMParam(l), l, null, null);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int m,
+ int l)
+ {
+ this(p, g, q, m, l, null, null);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ BigInteger j,
+ DHValidationParameters validation)
+ {
+ this(p, g, q, DEFAULT_MINIMUM_LENGTH, 0, j, validation);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int m,
+ int l,
+ BigInteger j,
+ DHValidationParameters validation)
+ {
+ if (l != 0)
+ {
+ BigInteger bigL = BigInteger.valueOf(2L ^ (l - 1));
+ if (bigL.compareTo(p) == 1)
+ {
+ throw new IllegalArgumentException("when l value specified, it must satisfy 2^(l-1) <= p");
+ }
+ if (l < m)
+ {
+ throw new IllegalArgumentException("when l value specified, it may not be less than m value");
+ }
+ }
+
+ this.g = g;
+ this.p = p;
+ this.q = q;
+ this.m = m;
+ this.l = l;
+ this.j = j;
+ this.validation = validation;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ /**
+ * Return the subgroup factor J.
+ *
+ * @return subgroup factor
+ */
+ public BigInteger getJ()
+ {
+ return j;
+ }
+
+ /**
+ * Return the minimum length of the private value.
+ *
+ * @return the minimum length of the private value in bits.
+ */
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * Return the private value length in bits - if set, zero otherwise
+ *
+ * @return the private value length in bits, zero otherwise.
+ */
+ public int getL()
+ {
+ return l;
+ }
+
+ public DHValidationParameters getValidationParameters()
+ {
+ return validation;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHParameters))
+ {
+ return false;
+ }
+
+ DHParameters pm = (DHParameters)obj;
+
+ if (this.getQ() != null)
+ {
+ if (!this.getQ().equals(pm.getQ()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (pm.getQ() != null)
+ {
+ return false;
+ }
+ }
+
+ return pm.getP().equals(p) && pm.getG().equals(g);
+ }
+
+ public int hashCode()
+ {
+ return getP().hashCode() ^ getG().hashCode() ^ (getQ() != null ? getQ().hashCode() : 0);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHPrivateKeyParameters.java
new file mode 100644
index 000000000..6f192b36a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHPrivateKeyParameters.java
@@ -0,0 +1,41 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DHPrivateKeyParameters
+ extends DHKeyParameters
+{
+ private BigInteger x;
+
+ public DHPrivateKeyParameters(
+ BigInteger x,
+ DHParameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public int hashCode()
+ {
+ return x.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHPrivateKeyParameters))
+ {
+ return false;
+ }
+
+ DHPrivateKeyParameters other = (DHPrivateKeyParameters)obj;
+
+ return other.getX().equals(this.x) && super.equals(obj);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHPublicKeyParameters.java
new file mode 100644
index 000000000..3fbdd58ee
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHPublicKeyParameters.java
@@ -0,0 +1,41 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DHPublicKeyParameters
+ extends DHKeyParameters
+{
+ private BigInteger y;
+
+ public DHPublicKeyParameters(
+ BigInteger y,
+ DHParameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ public int hashCode()
+ {
+ return y.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHPublicKeyParameters))
+ {
+ return false;
+ }
+
+ DHPublicKeyParameters other = (DHPublicKeyParameters)obj;
+
+ return other.getY().equals(y) && super.equals(obj);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHValidationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHValidationParameters.java
new file mode 100644
index 000000000..cf385d0c6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DHValidationParameters.java
@@ -0,0 +1,50 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.util.Arrays;
+
+public class DHValidationParameters
+{
+ private byte[] seed;
+ private int counter;
+
+ public DHValidationParameters(
+ byte[] seed,
+ int counter)
+ {
+ this.seed = seed;
+ this.counter = counter;
+ }
+
+ public int getCounter()
+ {
+ return counter;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DHValidationParameters))
+ {
+ return false;
+ }
+
+ DHValidationParameters other = (DHValidationParameters)o;
+
+ if (other.counter != this.counter)
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(this.seed, other.seed);
+ }
+
+ public int hashCode()
+ {
+ return counter ^ Arrays.hashCode(seed);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAKeyGenerationParameters.java
new file mode 100644
index 000000000..eaa8f80dc
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class DSAKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private DSAParameters params;
+
+ public DSAKeyGenerationParameters(
+ SecureRandom random,
+ DSAParameters params)
+ {
+ super(random, params.getP().bitLength() - 1);
+
+ this.params = params;
+ }
+
+ public DSAParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAKeyParameters.java
new file mode 100644
index 000000000..8417a4e92
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAKeyParameters.java
@@ -0,0 +1,21 @@
+package org.spongycastle.crypto.params;
+
+public class DSAKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private DSAParameters params;
+
+ public DSAKeyParameters(
+ boolean isPrivate,
+ DSAParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public DSAParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAParameterGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAParameterGenerationParameters.java
new file mode 100644
index 000000000..8822209c5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAParameterGenerationParameters.java
@@ -0,0 +1,80 @@
+package org.spongycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+public class DSAParameterGenerationParameters
+{
+ public static final int DIGITAL_SIGNATURE_USAGE = 1;
+ public static final int KEY_ESTABLISHMENT_USAGE = 2;
+
+ private final int l;
+ private final int n;
+ private final int usageIndex;
+ private final int certainty;
+ private final SecureRandom random;
+
+ /**
+ * Construct without a usage index, this will do a random construction of G.
+ *
+ * @param L desired length of prime P in bits (the effective key size).
+ * @param N desired length of prime Q in bits.
+ * @param certainty certainty level for prime number generation.
+ * @param random the source of randomness to use.
+ */
+ public DSAParameterGenerationParameters(
+ int L,
+ int N,
+ int certainty,
+ SecureRandom random)
+ {
+ this(L, N, certainty, random, -1);
+ }
+
+ /**
+ * Construct for a specific usage index - this has the effect of using verifiable canonical generation of G.
+ *
+ * @param L desired length of prime P in bits (the effective key size).
+ * @param N desired length of prime Q in bits.
+ * @param certainty certainty level for prime number generation.
+ * @param random the source of randomness to use.
+ * @param usageIndex a valid usage index.
+ */
+ public DSAParameterGenerationParameters(
+ int L,
+ int N,
+ int certainty,
+ SecureRandom random,
+ int usageIndex)
+ {
+ this.l = L;
+ this.n = N;
+ this.certainty = certainty;
+ this.usageIndex = usageIndex;
+ this.random = random;
+ }
+
+ public int getL()
+ {
+ return l;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getCertainty()
+ {
+ return certainty;
+ }
+
+ public SecureRandom getRandom()
+ {
+ return random;
+ }
+
+ public int getUsageIndex()
+ {
+ return usageIndex;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAParameters.java
new file mode 100644
index 000000000..b1baf0673
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAParameters.java
@@ -0,0 +1,74 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class DSAParameters
+ implements CipherParameters
+{
+ private BigInteger g;
+ private BigInteger q;
+ private BigInteger p;
+ private DSAValidationParameters validation;
+
+ public DSAParameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g)
+ {
+ this.g = g;
+ this.p = p;
+ this.q = q;
+ }
+
+ public DSAParameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ DSAValidationParameters params)
+ {
+ this.g = g;
+ this.p = p;
+ this.q = q;
+ this.validation = params;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public DSAValidationParameters getValidationParameters()
+ {
+ return validation;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DSAParameters))
+ {
+ return false;
+ }
+
+ DSAParameters pm = (DSAParameters)obj;
+
+ return (pm.getP().equals(p) && pm.getQ().equals(q) && pm.getG().equals(g));
+ }
+
+ public int hashCode()
+ {
+ return getP().hashCode() ^ getQ().hashCode() ^ getG().hashCode();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAPrivateKeyParameters.java
new file mode 100644
index 000000000..bd1d96813
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAPrivateKeyParameters.java
@@ -0,0 +1,23 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DSAPrivateKeyParameters
+ extends DSAKeyParameters
+{
+ private BigInteger x;
+
+ public DSAPrivateKeyParameters(
+ BigInteger x,
+ DSAParameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAPublicKeyParameters.java
new file mode 100644
index 000000000..cd244b9dc
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAPublicKeyParameters.java
@@ -0,0 +1,23 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DSAPublicKeyParameters
+ extends DSAKeyParameters
+{
+ private BigInteger y;
+
+ public DSAPublicKeyParameters(
+ BigInteger y,
+ DSAParameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAValidationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAValidationParameters.java
new file mode 100644
index 000000000..ca7c6cf67
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/DSAValidationParameters.java
@@ -0,0 +1,65 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.util.Arrays;
+
+public class DSAValidationParameters
+{
+ private int usageIndex;
+ private byte[] seed;
+ private int counter;
+
+ public DSAValidationParameters(
+ byte[] seed,
+ int counter)
+ {
+ this(seed, counter, -1);
+ }
+
+ public DSAValidationParameters(
+ byte[] seed,
+ int counter,
+ int usageIndex)
+ {
+ this.seed = seed;
+ this.counter = counter;
+ this.usageIndex = usageIndex;
+ }
+
+ public int getCounter()
+ {
+ return counter;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+
+ public int getUsageIndex()
+ {
+ return usageIndex;
+ }
+
+ public int hashCode()
+ {
+ return counter ^ Arrays.hashCode(seed);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DSAValidationParameters))
+ {
+ return false;
+ }
+
+ DSAValidationParameters other = (DSAValidationParameters)o;
+
+ if (other.counter != this.counter)
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(this.seed, other.seed);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECDomainParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECDomainParameters.java
new file mode 100644
index 000000000..57daaee3a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECDomainParameters.java
@@ -0,0 +1,74 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECConstants;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.Arrays;
+
+public class ECDomainParameters
+ implements ECConstants
+{
+ private ECCurve curve;
+ private byte[] seed;
+ private ECPoint G;
+ private BigInteger n;
+ private BigInteger h;
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint G,
+ BigInteger n)
+ {
+ this(curve, G, n, ONE, null);
+ }
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint G,
+ BigInteger n,
+ BigInteger h)
+ {
+ this(curve, G, n, h, null);
+ }
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint G,
+ BigInteger n,
+ BigInteger h,
+ byte[] seed)
+ {
+ this.curve = curve;
+ this.G = G.normalize();
+ this.n = n;
+ this.h = h;
+ this.seed = seed;
+ }
+
+ public ECCurve getCurve()
+ {
+ return curve;
+ }
+
+ public ECPoint getG()
+ {
+ return G;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public BigInteger getH()
+ {
+ return h;
+ }
+
+ public byte[] getSeed()
+ {
+ return Arrays.clone(seed);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECKeyGenerationParameters.java
new file mode 100644
index 000000000..4675c65ad
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class ECKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private ECDomainParameters domainParams;
+
+ public ECKeyGenerationParameters(
+ ECDomainParameters domainParams,
+ SecureRandom random)
+ {
+ super(random, domainParams.getN().bitLength());
+
+ this.domainParams = domainParams;
+ }
+
+ public ECDomainParameters getDomainParameters()
+ {
+ return domainParams;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECKeyParameters.java
new file mode 100644
index 000000000..480a969fb
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECKeyParameters.java
@@ -0,0 +1,21 @@
+package org.spongycastle.crypto.params;
+
+public class ECKeyParameters
+ extends AsymmetricKeyParameter
+{
+ ECDomainParameters params;
+
+ protected ECKeyParameters(
+ boolean isPrivate,
+ ECDomainParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public ECDomainParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECPrivateKeyParameters.java
new file mode 100644
index 000000000..484ca232b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECPrivateKeyParameters.java
@@ -0,0 +1,22 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class ECPrivateKeyParameters
+ extends ECKeyParameters
+{
+ BigInteger d;
+
+ public ECPrivateKeyParameters(
+ BigInteger d,
+ ECDomainParameters params)
+ {
+ super(true, params);
+ this.d = d;
+ }
+
+ public BigInteger getD()
+ {
+ return d;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECPublicKeyParameters.java
new file mode 100644
index 000000000..deab005c3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ECPublicKeyParameters.java
@@ -0,0 +1,22 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.math.ec.ECPoint;
+
+public class ECPublicKeyParameters
+ extends ECKeyParameters
+{
+ ECPoint Q;
+
+ public ECPublicKeyParameters(
+ ECPoint Q,
+ ECDomainParameters params)
+ {
+ super(false, params);
+ this.Q = Q.normalize();
+ }
+
+ public ECPoint getQ()
+ {
+ return Q;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalKeyGenerationParameters.java
new file mode 100644
index 000000000..c1741ec18
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalKeyGenerationParameters.java
@@ -0,0 +1,30 @@
+package org.spongycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class ElGamalKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private ElGamalParameters params;
+
+ public ElGamalKeyGenerationParameters(
+ SecureRandom random,
+ ElGamalParameters params)
+ {
+ super(random, getStrength(params));
+
+ this.params = params;
+ }
+
+ public ElGamalParameters getParameters()
+ {
+ return params;
+ }
+
+ static int getStrength(ElGamalParameters params)
+ {
+ return params.getL() != 0 ? params.getL() : params.getP().bitLength();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalKeyParameters.java
new file mode 100644
index 000000000..83c00de64
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalKeyParameters.java
@@ -0,0 +1,47 @@
+package org.spongycastle.crypto.params;
+
+
+public class ElGamalKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private ElGamalParameters params;
+
+ protected ElGamalKeyParameters(
+ boolean isPrivate,
+ ElGamalParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public ElGamalParameters getParameters()
+ {
+ return params;
+ }
+
+ public int hashCode()
+ {
+ return (params != null) ? params.hashCode() : 0;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalKeyParameters))
+ {
+ return false;
+ }
+
+ ElGamalKeyParameters dhKey = (ElGamalKeyParameters)obj;
+
+ if (params == null)
+ {
+ return dhKey.getParameters() == null;
+ }
+ else
+ {
+ return params.equals(dhKey.getParameters());
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalParameters.java
new file mode 100644
index 000000000..60c484b1d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalParameters.java
@@ -0,0 +1,69 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class ElGamalParameters
+ implements CipherParameters
+{
+ private BigInteger g;
+ private BigInteger p;
+ private int l;
+
+ public ElGamalParameters(
+ BigInteger p,
+ BigInteger g)
+ {
+ this(p, g, 0);
+ }
+
+ public ElGamalParameters(
+ BigInteger p,
+ BigInteger g,
+ int l)
+ {
+ this.g = g;
+ this.p = p;
+ this.l = l;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ /**
+ * return the generator - g
+ */
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ /**
+ * return private value limit - l
+ */
+ public int getL()
+ {
+ return l;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalParameters))
+ {
+ return false;
+ }
+
+ ElGamalParameters pm = (ElGamalParameters)obj;
+
+ return pm.getP().equals(p) && pm.getG().equals(g) && pm.getL() == l;
+ }
+
+ public int hashCode()
+ {
+ return (getP().hashCode() ^ getG().hashCode()) + l;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalPrivateKeyParameters.java
new file mode 100644
index 000000000..510958913
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalPrivateKeyParameters.java
@@ -0,0 +1,46 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class ElGamalPrivateKeyParameters
+ extends ElGamalKeyParameters
+{
+ private BigInteger x;
+
+ public ElGamalPrivateKeyParameters(
+ BigInteger x,
+ ElGamalParameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalPrivateKeyParameters))
+ {
+ return false;
+ }
+
+ ElGamalPrivateKeyParameters pKey = (ElGamalPrivateKeyParameters)obj;
+
+ if (!pKey.getX().equals(x))
+ {
+ return false;
+ }
+
+ return super.equals(obj);
+ }
+
+ public int hashCode()
+ {
+ return getX().hashCode();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalPublicKeyParameters.java
new file mode 100644
index 000000000..ae21f8e8b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ElGamalPublicKeyParameters.java
@@ -0,0 +1,41 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class ElGamalPublicKeyParameters
+ extends ElGamalKeyParameters
+{
+ private BigInteger y;
+
+ public ElGamalPublicKeyParameters(
+ BigInteger y,
+ ElGamalParameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ public int hashCode()
+ {
+ return y.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalPublicKeyParameters))
+ {
+ return false;
+ }
+
+ ElGamalPublicKeyParameters other = (ElGamalPublicKeyParameters)obj;
+
+ return other.getY().equals(y) && super.equals(obj);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410KeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410KeyGenerationParameters.java
new file mode 100644
index 000000000..2a32389f9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410KeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+import java.security.SecureRandom;
+
+public class GOST3410KeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private GOST3410Parameters params;
+
+ public GOST3410KeyGenerationParameters(
+ SecureRandom random,
+ GOST3410Parameters params)
+ {
+ super(random, params.getP().bitLength() - 1);
+
+ this.params = params;
+ }
+
+ public GOST3410Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410KeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410KeyParameters.java
new file mode 100644
index 000000000..b9a6ee7e2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410KeyParameters.java
@@ -0,0 +1,21 @@
+package org.spongycastle.crypto.params;
+
+public class GOST3410KeyParameters
+ extends AsymmetricKeyParameter
+{
+ private GOST3410Parameters params;
+
+ public GOST3410KeyParameters(
+ boolean isPrivate,
+ GOST3410Parameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public GOST3410Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410Parameters.java
new file mode 100644
index 000000000..ad29ed657
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410Parameters.java
@@ -0,0 +1,74 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+import java.math.BigInteger;
+
+public class GOST3410Parameters
+ implements CipherParameters
+{
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger a;
+ private GOST3410ValidationParameters validation;
+
+ public GOST3410Parameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger a)
+ {
+ this.p = p;
+ this.q = q;
+ this.a = a;
+ }
+
+ public GOST3410Parameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger a,
+ GOST3410ValidationParameters params)
+ {
+ this.a = a;
+ this.p = p;
+ this.q = q;
+ this.validation = params;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getA()
+ {
+ return a;
+ }
+
+ public GOST3410ValidationParameters getValidationParameters()
+ {
+ return validation;
+ }
+
+ public int hashCode()
+ {
+ return p.hashCode() ^ q.hashCode() ^ a.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof GOST3410Parameters))
+ {
+ return false;
+ }
+
+ GOST3410Parameters pm = (GOST3410Parameters)obj;
+
+ return (pm.getP().equals(p) && pm.getQ().equals(q) && pm.getA().equals(a));
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410PrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410PrivateKeyParameters.java
new file mode 100644
index 000000000..531870e2a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410PrivateKeyParameters.java
@@ -0,0 +1,23 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class GOST3410PrivateKeyParameters
+ extends GOST3410KeyParameters
+{
+ private BigInteger x;
+
+ public GOST3410PrivateKeyParameters(
+ BigInteger x,
+ GOST3410Parameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410PublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410PublicKeyParameters.java
new file mode 100644
index 000000000..b0b058999
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410PublicKeyParameters.java
@@ -0,0 +1,23 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class GOST3410PublicKeyParameters
+ extends GOST3410KeyParameters
+{
+ private BigInteger y;
+
+ public GOST3410PublicKeyParameters(
+ BigInteger y,
+ GOST3410Parameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410ValidationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410ValidationParameters.java
new file mode 100644
index 000000000..46f893ca9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/GOST3410ValidationParameters.java
@@ -0,0 +1,84 @@
+package org.spongycastle.crypto.params;
+
+public class GOST3410ValidationParameters
+{
+ private int x0;
+ private int c;
+ private long x0L;
+ private long cL;
+
+
+ public GOST3410ValidationParameters(
+ int x0,
+ int c)
+ {
+ this.x0 = x0;
+ this.c = c;
+ }
+
+ public GOST3410ValidationParameters(
+ long x0L,
+ long cL)
+ {
+ this.x0L = x0L;
+ this.cL = cL;
+ }
+
+ public int getC()
+ {
+ return c;
+ }
+
+ public int getX0()
+ {
+ return x0;
+ }
+
+ public long getCL()
+ {
+ return cL;
+ }
+
+ public long getX0L()
+ {
+ return x0L;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof GOST3410ValidationParameters))
+ {
+ return false;
+ }
+
+ GOST3410ValidationParameters other = (GOST3410ValidationParameters)o;
+
+ if (other.c != this.c)
+ {
+ return false;
+ }
+
+ if (other.x0 != this.x0)
+ {
+ return false;
+ }
+
+ if (other.cL != this.cL)
+ {
+ return false;
+ }
+
+ if (other.x0L != this.x0L)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ return x0 ^ c ^ (int) x0L ^ (int)(x0L >> 32) ^ (int) cL ^ (int)(cL >> 32);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/HKDFParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/HKDFParameters.java
new file mode 100644
index 000000000..94595a158
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/HKDFParameters.java
@@ -0,0 +1,123 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Parameter class for the HKDFBytesGenerator class.
+ */
+public class HKDFParameters
+ implements DerivationParameters
+{
+ private final byte[] ikm;
+ private final boolean skipExpand;
+ private final byte[] salt;
+ private final byte[] info;
+
+ private HKDFParameters(final byte[] ikm, final boolean skip,
+ final byte[] salt, final byte[] info)
+ {
+ if (ikm == null)
+ {
+ throw new IllegalArgumentException(
+ "IKM (input keying material) should not be null");
+ }
+
+ this.ikm = Arrays.clone(ikm);
+
+ this.skipExpand = skip;
+
+ if (salt == null || salt.length == 0)
+ {
+ this.salt = null;
+ }
+ else
+ {
+ this.salt = Arrays.clone(salt);
+ }
+
+ if (info == null)
+ {
+ this.info = new byte[0];
+ }
+ else
+ {
+ this.info = Arrays.clone(info);
+ }
+ }
+
+ /**
+ * Generates parameters for HKDF, specifying both the optional salt and
+ * optional info. Step 1: Extract won't be skipped.
+ *
+ * @param ikm the input keying material or seed
+ * @param salt the salt to use, may be null for a salt for hashLen zeros
+ * @param info the info to use, may be null for an info field of zero bytes
+ */
+ public HKDFParameters(final byte[] ikm, final byte[] salt, final byte[] info)
+ {
+ this(ikm, false, salt, info);
+ }
+
+ /**
+ * Factory method that makes the HKDF skip the extract part of the key
+ * derivation function.
+ *
+ * @param ikm the input keying material or seed, directly used for step 2:
+ * Expand
+ * @param info the info to use, may be null for an info field of zero bytes
+ * @return HKDFParameters that makes the implementation skip step 1
+ */
+ public static HKDFParameters skipExtractParameters(final byte[] ikm,
+ final byte[] info)
+ {
+
+ return new HKDFParameters(ikm, true, null, info);
+ }
+
+ public static HKDFParameters defaultParameters(final byte[] ikm)
+ {
+ return new HKDFParameters(ikm, false, null, null);
+ }
+
+ /**
+ * Returns the input keying material or seed.
+ *
+ * @return the keying material
+ */
+ public byte[] getIKM()
+ {
+ return Arrays.clone(ikm);
+ }
+
+ /**
+ * Returns if step 1: extract has to be skipped or not
+ *
+ * @return true for skipping, false for no skipping of step 1
+ */
+ public boolean skipExtract()
+ {
+ return skipExpand;
+ }
+
+ /**
+ * Returns the salt, or null if the salt should be generated as a byte array
+ * of HashLen zeros.
+ *
+ * @return the salt, or null
+ */
+ public byte[] getSalt()
+ {
+ return Arrays.clone(salt);
+ }
+
+ /**
+ * Returns the info field, which may be empty (null is converted to empty).
+ *
+ * @return the info field, never null
+ */
+ public byte[] getInfo()
+ {
+ return Arrays.clone(info);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/IESParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/IESParameters.java
new file mode 100644
index 000000000..419d135d3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/IESParameters.java
@@ -0,0 +1,44 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+/**
+ * parameters for using an integrated cipher in stream mode.
+ */
+public class IESParameters
+ implements CipherParameters
+{
+ private byte[] derivation;
+ private byte[] encoding;
+ private int macKeySize;
+
+ /**
+ * @param derivation the derivation parameter for the KDF function.
+ * @param encoding the encoding parameter for the KDF function.
+ * @param macKeySize the size of the MAC key (in bits).
+ */
+ public IESParameters(
+ byte[] derivation,
+ byte[] encoding,
+ int macKeySize)
+ {
+ this.derivation = derivation;
+ this.encoding = encoding;
+ this.macKeySize = macKeySize;
+ }
+
+ public byte[] getDerivationV()
+ {
+ return derivation;
+ }
+
+ public byte[] getEncodingV()
+ {
+ return encoding;
+ }
+
+ public int getMacKeySize()
+ {
+ return macKeySize;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/IESWithCipherParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/IESWithCipherParameters.java
new file mode 100644
index 000000000..68bb2d629
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/IESWithCipherParameters.java
@@ -0,0 +1,30 @@
+package org.spongycastle.crypto.params;
+
+
+public class IESWithCipherParameters
+ extends IESParameters
+{
+ private int cipherKeySize;
+
+ /**
+ * @param derivation the derivation parameter for the KDF function.
+ * @param encoding the encoding parameter for the KDF function.
+ * @param macKeySize the size of the MAC key (in bits).
+ * @param cipherKeySize the size of the associated Cipher key (in bits).
+ */
+ public IESWithCipherParameters(
+ byte[] derivation,
+ byte[] encoding,
+ int macKeySize,
+ int cipherKeySize)
+ {
+ super(derivation, encoding, macKeySize);
+
+ this.cipherKeySize = cipherKeySize;
+ }
+
+ public int getCipherKeySize()
+ {
+ return cipherKeySize;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ISO18033KDFParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ISO18033KDFParameters.java
new file mode 100644
index 000000000..c6e1d9991
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ISO18033KDFParameters.java
@@ -0,0 +1,23 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+
+/**
+ * parameters for Key derivation functions for ISO-18033
+ */
+public class ISO18033KDFParameters
+ implements DerivationParameters
+{
+ byte[] seed;
+
+ public ISO18033KDFParameters(
+ byte[] seed)
+ {
+ this.seed = seed;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFCounterParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFCounterParameters.java
new file mode 100644
index 000000000..b8930ce17
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFCounterParameters.java
@@ -0,0 +1,52 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.util.Arrays;
+
+public final class KDFCounterParameters
+ implements DerivationParameters
+{
+
+ private final byte[] ki;
+ private final byte[] fixedInputData;
+ private final int r;
+
+ public KDFCounterParameters(byte[] ki, byte[] fixedInputData, int r)
+ {
+ if (ki == null)
+ {
+ throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+ }
+ this.ki = Arrays.clone(ki);
+
+ if (fixedInputData == null)
+ {
+ this.fixedInputData = new byte[0];
+ }
+ else
+ {
+ this.fixedInputData = Arrays.clone(fixedInputData);
+ }
+
+ if (r != 8 && r != 16 && r != 24 && r != 32)
+ {
+ throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+ }
+ this.r = r;
+ }
+
+ public byte[] getKI()
+ {
+ return ki;
+ }
+
+ public byte[] getFixedInputData()
+ {
+ return Arrays.clone(fixedInputData);
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFDoublePipelineIterationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFDoublePipelineIterationParameters.java
new file mode 100644
index 000000000..30fe6138c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFDoublePipelineIterationParameters.java
@@ -0,0 +1,80 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Note that counter is only supported at the location presented in the
+ * NIST SP 800-108 specification, not in the additional locations present
+ * in the CAVP test vectors.
+ */
+public final class KDFDoublePipelineIterationParameters
+ implements DerivationParameters
+{
+
+ // could be any valid value, using 32, don't know why
+ private static final int UNUSED_R = 32;
+
+ private final byte[] ki;
+ private final boolean useCounter;
+ private final int r;
+ private final byte[] fixedInputData;
+
+ private KDFDoublePipelineIterationParameters(byte[] ki, byte[] fixedInputData, int r, boolean useCounter)
+ {
+ if (ki == null)
+ {
+ throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+ }
+ this.ki = Arrays.clone(ki);
+
+ if (fixedInputData == null)
+ {
+ this.fixedInputData = new byte[0];
+ }
+ else
+ {
+ this.fixedInputData = Arrays.clone(fixedInputData);
+ }
+
+ if (r != 8 && r != 16 && r != 24 && r != 32)
+ {
+ throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+ }
+ this.r = r;
+
+ this.useCounter = useCounter;
+ }
+
+ public static KDFDoublePipelineIterationParameters createWithCounter(
+ byte[] ki, byte[] fixedInputData, int r)
+ {
+ return new KDFDoublePipelineIterationParameters(ki, fixedInputData, r, true);
+ }
+
+ public static KDFDoublePipelineIterationParameters createWithoutCounter(
+ byte[] ki, byte[] fixedInputData)
+ {
+ return new KDFDoublePipelineIterationParameters(ki, fixedInputData, UNUSED_R, false);
+ }
+
+ public byte[] getKI()
+ {
+ return ki;
+ }
+
+ public boolean useCounter()
+ {
+ return useCounter;
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+
+ public byte[] getFixedInputData()
+ {
+ return Arrays.clone(fixedInputData);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFFeedbackParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFFeedbackParameters.java
new file mode 100644
index 000000000..44d1e9649
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFFeedbackParameters.java
@@ -0,0 +1,96 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Note that counter is only supported at the location presented in the
+ * NIST SP 800-108 specification, not in the additional locations present
+ * in the CAVP test vectors.
+ */
+public final class KDFFeedbackParameters
+ implements DerivationParameters
+{
+
+ // could be any valid value, using 32, don't know why
+ private static final int UNUSED_R = -1;
+
+ private final byte[] ki;
+ private final byte[] iv;
+ private final boolean useCounter;
+ private final int r;
+ private final byte[] fixedInputData;
+
+ private KDFFeedbackParameters(byte[] ki, byte[] iv, byte[] fixedInputData, int r, boolean useCounter)
+ {
+ if (ki == null)
+ {
+ throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+ }
+ this.ki = Arrays.clone(ki);
+
+ if (fixedInputData == null)
+ {
+ this.fixedInputData = new byte[0];
+ }
+ else
+ {
+ this.fixedInputData = Arrays.clone(fixedInputData);
+ }
+
+ this.r = r;
+
+ if (iv == null)
+ {
+ this.iv = new byte[0];
+ }
+ else
+ {
+ this.iv = Arrays.clone(iv);
+ }
+
+ this.useCounter = useCounter;
+ }
+
+ public static KDFFeedbackParameters createWithCounter(
+ byte[] ki, final byte[] iv, byte[] fixedInputData, int r)
+ {
+ if (r != 8 && r != 16 && r != 24 && r != 32)
+ {
+ throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+ }
+
+ return new KDFFeedbackParameters(ki, iv, fixedInputData, r, true);
+ }
+
+ public static KDFFeedbackParameters createWithoutCounter(
+ byte[] ki, final byte[] iv, byte[] fixedInputData)
+ {
+ return new KDFFeedbackParameters(ki, iv, fixedInputData, UNUSED_R, false);
+ }
+
+ public byte[] getKI()
+ {
+ return ki;
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+
+ public boolean useCounter()
+ {
+ return useCounter;
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+
+ public byte[] getFixedInputData()
+ {
+ return Arrays.clone(fixedInputData);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFParameters.java
new file mode 100644
index 000000000..600106153
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KDFParameters.java
@@ -0,0 +1,31 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+
+/**
+ * parameters for Key derivation functions for IEEE P1363a
+ */
+public class KDFParameters
+ implements DerivationParameters
+{
+ byte[] iv;
+ byte[] shared;
+
+ public KDFParameters(
+ byte[] shared,
+ byte[] iv)
+ {
+ this.shared = shared;
+ this.iv = iv;
+ }
+
+ public byte[] getSharedSecret()
+ {
+ return shared;
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KeyParameter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KeyParameter.java
new file mode 100644
index 000000000..079e136be
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/KeyParameter.java
@@ -0,0 +1,30 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class KeyParameter
+ implements CipherParameters
+{
+ private byte[] key;
+
+ public KeyParameter(
+ byte[] key)
+ {
+ this(key, 0, key.length);
+ }
+
+ public KeyParameter(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ {
+ this.key = new byte[keyLen];
+
+ System.arraycopy(key, keyOff, this.key, 0, keyLen);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MGFParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MGFParameters.java
new file mode 100644
index 000000000..082e37cd5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MGFParameters.java
@@ -0,0 +1,32 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.DerivationParameters;
+
+/**
+ * parameters for mask derivation functions.
+ */
+public class MGFParameters
+ implements DerivationParameters
+{
+ byte[] seed;
+
+ public MGFParameters(
+ byte[] seed)
+ {
+ this(seed, 0, seed.length);
+ }
+
+ public MGFParameters(
+ byte[] seed,
+ int off,
+ int len)
+ {
+ this.seed = new byte[len];
+ System.arraycopy(seed, off, this.seed, 0, len);
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MQVPrivateParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MQVPrivateParameters.java
new file mode 100644
index 000000000..ae917ab80
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MQVPrivateParameters.java
@@ -0,0 +1,43 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class MQVPrivateParameters
+ implements CipherParameters
+{
+ private ECPrivateKeyParameters staticPrivateKey;
+ private ECPrivateKeyParameters ephemeralPrivateKey;
+ private ECPublicKeyParameters ephemeralPublicKey;
+
+ public MQVPrivateParameters(
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey)
+ {
+ this(staticPrivateKey, ephemeralPrivateKey, null);
+ }
+
+ public MQVPrivateParameters(
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ this.staticPrivateKey = staticPrivateKey;
+ this.ephemeralPrivateKey = ephemeralPrivateKey;
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public ECPrivateKeyParameters getStaticPrivateKey()
+ {
+ return staticPrivateKey;
+ }
+
+ public ECPrivateKeyParameters getEphemeralPrivateKey()
+ {
+ return ephemeralPrivateKey;
+ }
+
+ public ECPublicKeyParameters getEphemeralPublicKey()
+ {
+ return ephemeralPublicKey;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MQVPublicParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MQVPublicParameters.java
new file mode 100644
index 000000000..9f6f67de5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/MQVPublicParameters.java
@@ -0,0 +1,28 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class MQVPublicParameters
+ implements CipherParameters
+{
+ private ECPublicKeyParameters staticPublicKey;
+ private ECPublicKeyParameters ephemeralPublicKey;
+
+ public MQVPublicParameters(
+ ECPublicKeyParameters staticPublicKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ this.staticPublicKey = staticPublicKey;
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public ECPublicKeyParameters getStaticPublicKey()
+ {
+ return staticPublicKey;
+ }
+
+ public ECPublicKeyParameters getEphemeralPublicKey()
+ {
+ return ephemeralPublicKey;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternKeyGenerationParameters.java
new file mode 100644
index 000000000..c0b5998b6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternKeyGenerationParameters.java
@@ -0,0 +1,97 @@
+package org.spongycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+/**
+ * Parameters for NaccacheStern public private key generation. For details on
+ * this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternKeyGenerationParameters extends KeyGenerationParameters
+{
+
+ // private BigInteger publicExponent;
+ private int certainty;
+
+ private int cntSmallPrimes;
+
+ private boolean debug = false;
+
+ /**
+ * Parameters for generating a NaccacheStern KeyPair.
+ *
+ * @param random
+ * The source of randomness
+ * @param strength
+ * The desired strength of the Key in Bits
+ * @param certainty
+ * the probability that the generated primes are not really prime
+ * as integer: 2^(-certainty) is then the probability
+ * @param cntSmallPrimes
+ * How many small key factors are desired
+ */
+ public NaccacheSternKeyGenerationParameters(SecureRandom random, int strength, int certainty, int cntSmallPrimes)
+ {
+ this(random, strength, certainty, cntSmallPrimes, false);
+ }
+
+ /**
+ * Parameters for a NaccacheStern KeyPair.
+ *
+ * @param random
+ * The source of randomness
+ * @param strength
+ * The desired strength of the Key in Bits
+ * @param certainty
+ * the probability that the generated primes are not really prime
+ * as integer: 2^(-certainty) is then the probability
+ * @param cntSmallPrimes
+ * How many small key factors are desired
+ * @param debug
+ * Turn debugging on or off (reveals secret information, use with
+ * caution)
+ */
+ public NaccacheSternKeyGenerationParameters(SecureRandom random,
+ int strength, int certainty, int cntSmallPrimes, boolean debug)
+ {
+ super(random, strength);
+
+ this.certainty = certainty;
+ if (cntSmallPrimes % 2 == 1)
+ {
+ throw new IllegalArgumentException("cntSmallPrimes must be a multiple of 2");
+ }
+ if (cntSmallPrimes < 30)
+ {
+ throw new IllegalArgumentException("cntSmallPrimes must be >= 30 for security reasons");
+ }
+ this.cntSmallPrimes = cntSmallPrimes;
+
+ this.debug = debug;
+ }
+
+ /**
+ * @return Returns the certainty.
+ */
+ public int getCertainty()
+ {
+ return certainty;
+ }
+
+ /**
+ * @return Returns the cntSmallPrimes.
+ */
+ public int getCntSmallPrimes()
+ {
+ return cntSmallPrimes;
+ }
+
+ public boolean isDebug()
+ {
+ return debug;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternKeyParameters.java
new file mode 100644
index 000000000..4b855b3f3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternKeyParameters.java
@@ -0,0 +1,53 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+/**
+ * Public key parameters for NaccacheStern cipher. For details on this cipher,
+ * please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternKeyParameters extends AsymmetricKeyParameter
+{
+
+ private BigInteger g, n;
+
+ int lowerSigmaBound;
+
+ /**
+ * @param privateKey
+ */
+ public NaccacheSternKeyParameters(boolean privateKey, BigInteger g, BigInteger n, int lowerSigmaBound)
+ {
+ super(privateKey);
+ this.g = g;
+ this.n = n;
+ this.lowerSigmaBound = lowerSigmaBound;
+ }
+
+ /**
+ * @return Returns the g.
+ */
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ /**
+ * @return Returns the lowerSigmaBound.
+ */
+ public int getLowerSigmaBound()
+ {
+ return lowerSigmaBound;
+ }
+
+ /**
+ * @return Returns the n.
+ */
+ public BigInteger getModulus()
+ {
+ return n;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternPrivateKeyParameters.java
new file mode 100644
index 000000000..7fa61c94f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/NaccacheSternPrivateKeyParameters.java
@@ -0,0 +1,50 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+/**
+ * Private key parameters for NaccacheStern cipher. For details on this cipher,
+ * please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternPrivateKeyParameters extends NaccacheSternKeyParameters
+{
+ private BigInteger phi_n;
+ private Vector smallPrimes;
+
+ /**
+ * Constructs a NaccacheSternPrivateKey
+ *
+ * @param g
+ * the public enryption parameter g
+ * @param n
+ * the public modulus n = p*q
+ * @param lowerSigmaBound
+ * the public lower sigma bound up to which data can be encrypted
+ * @param smallPrimes
+ * the small primes, of which sigma is constructed in the right
+ * order
+ * @param phi_n
+ * the private modulus phi(n) = (p-1)(q-1)
+ */
+ public NaccacheSternPrivateKeyParameters(BigInteger g, BigInteger n,
+ int lowerSigmaBound, Vector smallPrimes,
+ BigInteger phi_n)
+ {
+ super(true, g, n, lowerSigmaBound);
+ this.smallPrimes = smallPrimes;
+ this.phi_n = phi_n;
+ }
+
+ public BigInteger getPhi_n()
+ {
+ return phi_n;
+ }
+
+ public Vector getSmallPrimes()
+ {
+ return smallPrimes;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithIV.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithIV.java
new file mode 100644
index 000000000..7fb39b7ea
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithIV.java
@@ -0,0 +1,39 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class ParametersWithIV
+ implements CipherParameters
+{
+ private byte[] iv;
+ private CipherParameters parameters;
+
+ public ParametersWithIV(
+ CipherParameters parameters,
+ byte[] iv)
+ {
+ this(parameters, iv, 0, iv.length);
+ }
+
+ public ParametersWithIV(
+ CipherParameters parameters,
+ byte[] iv,
+ int ivOff,
+ int ivLen)
+ {
+ this.iv = new byte[ivLen];
+ this.parameters = parameters;
+
+ System.arraycopy(iv, ivOff, this.iv, 0, ivLen);
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithRandom.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithRandom.java
new file mode 100644
index 000000000..ca445554e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithRandom.java
@@ -0,0 +1,36 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+import java.security.SecureRandom;
+
+public class ParametersWithRandom
+ implements CipherParameters
+{
+ private SecureRandom random;
+ private CipherParameters parameters;
+
+ public ParametersWithRandom(
+ CipherParameters parameters,
+ SecureRandom random)
+ {
+ this.random = random;
+ this.parameters = parameters;
+ }
+
+ public ParametersWithRandom(
+ CipherParameters parameters)
+ {
+ this(parameters, new SecureRandom());
+ }
+
+ public SecureRandom getRandom()
+ {
+ return random;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithSBox.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithSBox.java
new file mode 100644
index 000000000..a10464763
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithSBox.java
@@ -0,0 +1,28 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class ParametersWithSBox
+ implements CipherParameters
+{
+ private CipherParameters parameters;
+ private byte[] sBox;
+
+ public ParametersWithSBox(
+ CipherParameters parameters,
+ byte[] sBox)
+ {
+ this.parameters = parameters;
+ this.sBox = sBox;
+ }
+
+ public byte[] getSBox()
+ {
+ return sBox;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithSalt.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithSalt.java
new file mode 100644
index 000000000..e6566a362
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/ParametersWithSalt.java
@@ -0,0 +1,42 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+/**
+ * Cipher parameters with a fixed salt value associated with them.
+ */
+public class ParametersWithSalt
+ implements CipherParameters
+{
+ private byte[] salt;
+ private CipherParameters parameters;
+
+ public ParametersWithSalt(
+ CipherParameters parameters,
+ byte[] salt)
+ {
+ this(parameters, salt, 0, salt.length);
+ }
+
+ public ParametersWithSalt(
+ CipherParameters parameters,
+ byte[] salt,
+ int saltOff,
+ int saltLen)
+ {
+ this.salt = new byte[saltLen];
+ this.parameters = parameters;
+
+ System.arraycopy(salt, saltOff, this.salt, 0, saltLen);
+ }
+
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RC2Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RC2Parameters.java
new file mode 100644
index 000000000..ae396f607
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RC2Parameters.java
@@ -0,0 +1,36 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class RC2Parameters
+ implements CipherParameters
+{
+ private byte[] key;
+ private int bits;
+
+ public RC2Parameters(
+ byte[] key)
+ {
+ this(key, (key.length > 128) ? 1024 : (key.length * 8));
+ }
+
+ public RC2Parameters(
+ byte[] key,
+ int bits)
+ {
+ this.key = new byte[key.length];
+ this.bits = bits;
+
+ System.arraycopy(key, 0, this.key, 0, key.length);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public int getEffectiveKeyBits()
+ {
+ return bits;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RC5Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RC5Parameters.java
new file mode 100644
index 000000000..402843e82
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RC5Parameters.java
@@ -0,0 +1,35 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class RC5Parameters
+ implements CipherParameters
+{
+ private byte[] key;
+ private int rounds;
+
+ public RC5Parameters(
+ byte[] key,
+ int rounds)
+ {
+ if (key.length > 255)
+ {
+ throw new IllegalArgumentException("RC5 key length can be no greater than 255");
+ }
+
+ this.key = new byte[key.length];
+ this.rounds = rounds;
+
+ System.arraycopy(key, 0, this.key, 0, key.length);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public int getRounds()
+ {
+ return rounds;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSABlindingParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSABlindingParameters.java
new file mode 100644
index 000000000..fd45d205d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSABlindingParameters.java
@@ -0,0 +1,35 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+
+import java.math.BigInteger;
+
+public class RSABlindingParameters
+ implements CipherParameters
+{
+ private RSAKeyParameters publicKey;
+ private BigInteger blindingFactor;
+
+ public RSABlindingParameters(
+ RSAKeyParameters publicKey,
+ BigInteger blindingFactor)
+ {
+ if (publicKey instanceof RSAPrivateCrtKeyParameters)
+ {
+ throw new IllegalArgumentException("RSA parameters should be for a public key");
+ }
+
+ this.publicKey = publicKey;
+ this.blindingFactor = blindingFactor;
+ }
+
+ public RSAKeyParameters getPublicKey()
+ {
+ return publicKey;
+ }
+
+ public BigInteger getBlindingFactor()
+ {
+ return blindingFactor;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAKeyGenerationParameters.java
new file mode 100644
index 000000000..6c0470591
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAKeyGenerationParameters.java
@@ -0,0 +1,48 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class RSAKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private BigInteger publicExponent;
+ private int certainty;
+
+ public RSAKeyGenerationParameters(
+ BigInteger publicExponent,
+ SecureRandom random,
+ int strength,
+ int certainty)
+ {
+ super(random, strength);
+
+ if (strength < 12)
+ {
+ throw new IllegalArgumentException("key strength too small");
+ }
+
+ //
+ // public exponent cannot be even
+ //
+ if (!publicExponent.testBit(0))
+ {
+ throw new IllegalArgumentException("public exponent cannot be even");
+ }
+
+ this.publicExponent = publicExponent;
+ this.certainty = certainty;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ public int getCertainty()
+ {
+ return certainty;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAKeyParameters.java
new file mode 100644
index 000000000..d2eceeba6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAKeyParameters.java
@@ -0,0 +1,31 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class RSAKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private BigInteger modulus;
+ private BigInteger exponent;
+
+ public RSAKeyParameters(
+ boolean isPrivate,
+ BigInteger modulus,
+ BigInteger exponent)
+ {
+ super(isPrivate);
+
+ this.modulus = modulus;
+ this.exponent = exponent;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getExponent()
+ {
+ return exponent;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAPrivateCrtKeyParameters.java
new file mode 100644
index 000000000..4dc79bdfd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/RSAPrivateCrtKeyParameters.java
@@ -0,0 +1,67 @@
+package org.spongycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class RSAPrivateCrtKeyParameters
+ extends RSAKeyParameters
+{
+ private BigInteger e;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger dP;
+ private BigInteger dQ;
+ private BigInteger qInv;
+
+ /**
+ *
+ */
+ public RSAPrivateCrtKeyParameters(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger p,
+ BigInteger q,
+ BigInteger dP,
+ BigInteger dQ,
+ BigInteger qInv)
+ {
+ super(true, modulus, privateExponent);
+
+ this.e = publicExponent;
+ this.p = p;
+ this.q = q;
+ this.dP = dP;
+ this.dQ = dQ;
+ this.qInv = qInv;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return e;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getDP()
+ {
+ return dP;
+ }
+
+ public BigInteger getDQ()
+ {
+ return dQ;
+ }
+
+ public BigInteger getQInv()
+ {
+ return qInv;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/SkeinParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/SkeinParameters.java
new file mode 100644
index 000000000..f706409e5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/SkeinParameters.java
@@ -0,0 +1,293 @@
+package org.spongycastle.crypto.params;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.digests.SkeinDigest;
+import org.spongycastle.crypto.digests.SkeinEngine;
+import org.spongycastle.crypto.macs.SkeinMac;
+import org.spongycastle.util.Integers;
+
+/**
+ * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+ *
+ * Internal access to the digest is synchronized so a single one of these can be shared.
+ *
+ * Access to internals is synchronized so a single one of these can be shared.
+ *
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the default SecureRandom does for its generateSeed() call.
+ *
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the passed in SecureRandom does for its generateSeed() call.
+ *
+ * Note: If this constructor is used any calls to setSeed() in the resulting SecureRandom will be ignored.
+ *
+ * Based on an idea from Marcus Lippert.
+ *
+ * If fast is set to true, the code should be round about 8 times faster when
+ * generating a long sequence of random bytes. 20 bytes of random values using
+ * the fast mode take less than half a second on a Nokia e70. If fast is set to false,
+ * it takes round about 2500 ms.
+ *
+ * Minimum entropy requirement is the security strength requested.
+ *
+ * The cofactor is used to calculate the output block length (maxOutlen) according to
+ * =0)
+ {
+ break; //step 14
+ }
+ else
+ {
+ pq[0] = p[0];
+ pq[1] = p[1];
+ return y[0].intValue(); //return for procedure B step 2
+ }
+ }
+ }
+ return y[0].intValue();
+ }
+
+ //Procedure A'
+ private long procedure_Aa(long x0, long c, BigInteger[] pq, int size)
+ {
+ //Verify and perform condition: 0=0)
+ {
+ break; //step 14
+ }
+ else
+ {
+ pq[0] = p[0];
+ pq[1] = p[1];
+ return y[0].longValue(); //return for procedure B' step 2
+ }
+ }
+ }
+ return y[0].longValue();
+ }
+
+ //Procedure B
+ private void procedure_B(int x0, int c, BigInteger[] pq)
+ {
+ //Verify and perform condition: 0
+ * This implementation is based on ISO 18033/IEEE P1363a.
+ */
+public class KDF1BytesGenerator
+ extends BaseKDFBytesGenerator
+{
+ /**
+ * Construct a KDF1 byte generator.
+ *
+ * This implementation is based on IEEE P1363/ISO 18033.
+ */
+public class KDF2BytesGenerator
+ extends BaseKDFBytesGenerator
+{
+ /**
+ * Construct a KDF2 bytes generator. Generates key material
+ * according to IEEE P1363 or ISO 18033 depending on the initialisation.
+ *
+ * The {@code r} value has a specific format with some bits required to be cleared, resulting in an
+ * effective 106 bit key.
+ * A separately generated 256 bit key can be modified to fit the Poly1305 key format by using the
+ * {@link #clamp(byte[])} method to clear the required bits.
+ *
+ * @see Poly1305
+ */
+public class Poly1305KeyGenerator
+ extends CipherKeyGenerator
+{
+ private static final byte R_MASK_LOW_2 = (byte)0xFC;
+ private static final byte R_MASK_HIGH_4 = (byte)0x0F;
+
+ /**
+ * Initialises the key generator.
+ * Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored.
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ // Poly1305 keys are always 256 bits
+ super.init(new KeyGenerationParameters(param.getRandom(), 256));
+ }
+
+ /**
+ * Generates a 256 bit key in the format required for Poly1305 - e.g.
+ * k[0] ... k[15], r[0] ... r[15]
with the required bits in r
cleared
+ * as per {@link #clamp(byte[])}.
+ */
+ public byte[] generateKey()
+ {
+ final byte[] key = super.generateKey();
+ clamp(key);
+ return key;
+ }
+
+ /**
+ * Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
+ * clearing required bits in the r
(second 16 bytes) portion of the key.
+ * Specifically:
+ *
+ *
+ *
+ * @param a 32 byte key value k[0] ... k[15], r[0] ... r[15]
+ */
+ public static void clamp(byte[] key)
+ {
+ /*
+ * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
+ */
+ if (key.length != 32)
+ {
+ throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
+ }
+
+ /*
+ * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
+ */
+ key[19] &= R_MASK_HIGH_4;
+ key[23] &= R_MASK_HIGH_4;
+ key[27] &= R_MASK_HIGH_4;
+ key[31] &= R_MASK_HIGH_4;
+
+ /*
+ * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
+ */
+ key[20] &= R_MASK_LOW_2;
+ key[24] &= R_MASK_LOW_2;
+ key[28] &= R_MASK_LOW_2;
+ }
+
+ /**
+ * Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g.
+ * k[0] ... k[15], r[0] ... r[15]
with the required bits in r
cleared
+ * as per {@link #clamp(byte[])}.
+ *
+ * @throws IllegalArgumentException if the key is of the wrong length, or has invalid bits set
+ * in the r
portion of the key.
+ */
+ public static void checkKey(byte[] key)
+ {
+ if (key.length != 32)
+ {
+ throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
+ }
+
+ checkMask(key[19], R_MASK_HIGH_4);
+ checkMask(key[23], R_MASK_HIGH_4);
+ checkMask(key[27], R_MASK_HIGH_4);
+ checkMask(key[31], R_MASK_HIGH_4);
+
+ checkMask(key[20], R_MASK_LOW_2);
+ checkMask(key[24], R_MASK_LOW_2);
+ checkMask(key[28], R_MASK_LOW_2);
+ }
+
+ private static void checkMask(byte b, byte mask)
+ {
+ if ((b & (~mask)) != 0)
+ {
+ throw new IllegalArgumentException("Invalid format for r portion of Poly1305 key.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/RSABlindingFactorGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/RSABlindingFactorGenerator.java
new file mode 100644
index 000000000..50b7db5e3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/RSABlindingFactorGenerator.java
@@ -0,0 +1,77 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Generate a random factor suitable for use with RSA blind signatures
+ * as outlined in Chaum's blinding and unblinding as outlined in
+ * "Handbook of Applied Cryptography", page 475.
+ */
+public class RSABlindingFactorGenerator
+{
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ private RSAKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * Initialise the factor generator
+ *
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RSAKeyParameters)rParam.getParameters();
+ random = rParam.getRandom();
+ }
+ else
+ {
+ key = (RSAKeyParameters)param;
+ random = new SecureRandom();
+ }
+
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ throw new IllegalArgumentException("generator requires RSA public key");
+ }
+ }
+
+ /**
+ * Generate a suitable blind factor for the public key the generator was initialised with.
+ *
+ * @return a random blind factor
+ */
+ public BigInteger generateBlindingFactor()
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("generator not initialised");
+ }
+
+ BigInteger m = key.getModulus();
+ int length = m.bitLength() - 1; // must be less than m.bitLength()
+ BigInteger factor;
+ BigInteger gcd;
+
+ do
+ {
+ factor = new BigInteger(length, random);
+ gcd = factor.gcd(m);
+ }
+ while (factor.equals(ZERO) || factor.equals(ONE) || !gcd.equals(ONE));
+
+ return factor;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/RSAKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/RSAKeyPairGenerator.java
new file mode 100644
index 000000000..0708ec729
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -0,0 +1,147 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * an RSA key pair generator.
+ */
+public class RSAKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private RSAKeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (RSAKeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ BigInteger p, q, n, d, e, pSub1, qSub1, phi;
+
+ //
+ // p and q values should have a length of half the strength in bits
+ //
+ int strength = param.getStrength();
+ int pbitlength = (strength + 1) / 2;
+ int qbitlength = strength - pbitlength;
+ int mindiffbits = strength / 3;
+
+ e = param.getPublicExponent();
+
+ // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
+ // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
+
+ //
+ // generate p, prime and (p-1) relatively prime to e
+ //
+ for (;;)
+ {
+ p = new BigInteger(pbitlength, 1, param.getRandom());
+
+ if (p.mod(e).equals(ONE))
+ {
+ continue;
+ }
+
+ if (!p.isProbablePrime(param.getCertainty()))
+ {
+ continue;
+ }
+
+ if (e.gcd(p.subtract(ONE)).equals(ONE))
+ {
+ break;
+ }
+ }
+
+ //
+ // generate a modulus of the required length
+ //
+ for (;;)
+ {
+ // generate q, prime and (q-1) relatively prime to e,
+ // and not equal to p
+ //
+ for (;;)
+ {
+ q = new BigInteger(qbitlength, 1, param.getRandom());
+
+ if (q.subtract(p).abs().bitLength() < mindiffbits)
+ {
+ continue;
+ }
+
+ if (q.mod(e).equals(ONE))
+ {
+ continue;
+ }
+
+ if (!q.isProbablePrime(param.getCertainty()))
+ {
+ continue;
+ }
+
+ if (e.gcd(q.subtract(ONE)).equals(ONE))
+ {
+ break;
+ }
+ }
+
+ //
+ // calculate the modulus
+ //
+ n = p.multiply(q);
+
+ if (n.bitLength() == param.getStrength())
+ {
+ break;
+ }
+
+ //
+ // if we get here our primes aren't big enough, make the largest
+ // of the two p and try again
+ //
+ p = p.max(q);
+ }
+
+ if (p.compareTo(q) < 0)
+ {
+ phi = p;
+ p = q;
+ q = phi;
+ }
+
+ pSub1 = p.subtract(ONE);
+ qSub1 = q.subtract(ONE);
+ phi = pSub1.multiply(qSub1);
+
+ //
+ // calculate the private exponent
+ //
+ d = e.modInverse(phi);
+
+ //
+ // calculate the CRT factors
+ //
+ BigInteger dP, dQ, qInv;
+
+ dP = d.remainder(pSub1);
+ dQ = d.remainder(qSub1);
+ qInv = q.modInverse(p);
+
+ return new AsymmetricCipherKeyPair(
+ new RSAKeyParameters(false, n, e),
+ new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/SCrypt.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/SCrypt.java
new file mode 100644
index 000000000..c199adbf2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/generators/SCrypt.java
@@ -0,0 +1,147 @@
+package org.spongycastle.crypto.generators;
+
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.engines.Salsa20Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+
+public class SCrypt
+{
+ // TODO Validate arguments
+ public static byte[] generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
+ {
+ return MFcrypt(P, S, N, r, p, dkLen);
+ }
+
+ private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen)
+ {
+ int MFLenBytes = r * 128;
+ byte[] bytes = SingleIterationPBKDF2(P, S, p * MFLenBytes);
+
+ int[] B = null;
+
+ try
+ {
+ int BLen = bytes.length >>> 2;
+ B = new int[BLen];
+
+ Pack.littleEndianToInt(bytes, 0, B);
+
+ int MFLenWords = MFLenBytes >>> 2;
+ for (int BOff = 0; BOff < BLen; BOff += MFLenWords)
+ {
+ // TODO These can be done in parallel threads
+ SMix(B, BOff, N, r);
+ }
+
+ Pack.intToLittleEndian(B, bytes, 0);
+
+ return SingleIterationPBKDF2(P, bytes, dkLen);
+ }
+ finally
+ {
+ Clear(bytes);
+ Clear(B);
+ }
+ }
+
+ private static byte[] SingleIterationPBKDF2(byte[] P, byte[] S, int dkLen)
+ {
+ PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest());
+ pGen.init(P, S, 1);
+ KeyParameter key = (KeyParameter) pGen.generateDerivedMacParameters(dkLen * 8);
+ return key.getKey();
+ }
+
+ private static void SMix(int[] B, int BOff, int N, int r)
+ {
+ int BCount = r * 32;
+
+ int[] blockX1 = new int[16];
+ int[] blockX2 = new int[16];
+ int[] blockY = new int[BCount];
+
+ int[] X = new int[BCount];
+ int[][] V = new int[N][];
+
+ try
+ {
+ System.arraycopy(B, BOff, X, 0, BCount);
+
+ for (int i = 0; i < N; ++i)
+ {
+ V[i] = Arrays.clone(X);
+ BlockMix(X, blockX1, blockX2, blockY, r);
+ }
+
+ int mask = N - 1;
+ for (int i = 0; i < N; ++i)
+ {
+ int j = X[BCount - 16] & mask;
+ Xor(X, V[j], 0, X);
+ BlockMix(X, blockX1, blockX2, blockY, r);
+ }
+
+ System.arraycopy(X, 0, B, BOff, BCount);
+ }
+ finally
+ {
+ ClearAll(V);
+ ClearAll(new int[][]{ X, blockX1, blockX2, blockY });
+ }
+ }
+
+ private static void BlockMix(int[] B, int[] X1, int[] X2, int[] Y, int r)
+ {
+ System.arraycopy(B, B.length - 16, X1, 0, 16);
+
+ int BOff = 0, YOff = 0, halfLen = B.length >>> 1;
+
+ for (int i = 2 * r; i > 0; --i)
+ {
+ Xor(X1, B, BOff, X2);
+
+ Salsa20Engine.salsaCore(8, X2, X1);
+ System.arraycopy(X1, 0, Y, YOff, 16);
+
+ YOff = halfLen + BOff - YOff;
+ BOff += 16;
+ }
+
+ System.arraycopy(Y, 0, B, 0, Y.length);
+ }
+
+ private static void Xor(int[] a, int[] b, int bOff, int[] output)
+ {
+ for (int i = output.length - 1; i >= 0; --i)
+ {
+ output[i] = a[i] ^ b[bOff + i];
+ }
+ }
+
+ private static void Clear(byte[] array)
+ {
+ if (array != null)
+ {
+ Arrays.fill(array, (byte)0);
+ }
+ }
+
+ private static void Clear(int[] array)
+ {
+ if (array != null)
+ {
+ Arrays.fill(array, 0);
+ }
+ }
+
+ private static void ClearAll(int[][] arrays)
+ {
+ for (int i = 0; i < arrays.length; ++i)
+ {
+ Clear(arrays[i]);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/CipherInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/CipherInputStream.java
new file mode 100644
index 000000000..4b5b677ea
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/CipherInputStream.java
@@ -0,0 +1,301 @@
+package org.spongycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+
+/**
+ * A CipherInputStream is composed of an InputStream and a cipher so that read() methods return data
+ * that are read in from the underlying InputStream but have been additionally processed by the
+ * Cipher. The cipher must be fully initialized before being used by a CipherInputStream.
+ *
+ * For example, if the Cipher is initialized for decryption, the
+ * CipherInputStream will attempt to read in data and decrypt them,
+ * before returning the decrypted data.
+ */
+public class CipherInputStream
+ extends FilterInputStream
+{
+ private BufferedBlockCipher bufferedBlockCipher;
+ private StreamCipher streamCipher;
+ private AEADBlockCipher aeadBlockCipher;
+
+ private final byte[] buf;
+ private final byte[] inBuf;
+
+ private int bufOff;
+ private int maxBuf;
+ private boolean finalized;
+
+ private static final int INPUT_BUF_SIZE = 2048;
+
+ /**
+ * Constructs a CipherInputStream from an InputStream and a
+ * BufferedBlockCipher.
+ */
+ public CipherInputStream(
+ InputStream is,
+ BufferedBlockCipher cipher)
+ {
+ super(is);
+
+ this.bufferedBlockCipher = cipher;
+
+ buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
+ inBuf = new byte[INPUT_BUF_SIZE];
+ }
+
+ public CipherInputStream(
+ InputStream is,
+ StreamCipher cipher)
+ {
+ super(is);
+
+ this.streamCipher = cipher;
+
+ buf = new byte[INPUT_BUF_SIZE];
+ inBuf = new byte[INPUT_BUF_SIZE];
+ }
+
+ /**
+ * Constructs a CipherInputStream from an InputStream and an AEADBlockCipher.
+ */
+ public CipherInputStream(InputStream is, AEADBlockCipher cipher)
+ {
+ super(is);
+
+ this.aeadBlockCipher = cipher;
+
+ buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
+ inBuf = new byte[INPUT_BUF_SIZE];
+ }
+
+ /**
+ * Read data from underlying stream and process with cipher until end of stream or some data is
+ * available after cipher processing.
+ *
+ * @return -1 to indicate end of stream, or the number of bytes (> 0) available.
+ */
+ private int nextChunk()
+ throws IOException
+ {
+ if (finalized)
+ {
+ return -1;
+ }
+
+ bufOff = 0;
+ maxBuf = 0;
+
+ // Keep reading until EOF or cipher processing produces data
+ while (maxBuf == 0)
+ {
+ int read = in.read(inBuf);
+ if (read == -1)
+ {
+ finaliseCipher();
+ if (maxBuf == 0)
+ {
+ return -1;
+ }
+ return maxBuf;
+ }
+
+ try
+ {
+ if (bufferedBlockCipher != null)
+ {
+ maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, read, buf, 0);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ maxBuf = aeadBlockCipher.processBytes(inBuf, 0, read, buf, 0);
+ }
+ else
+ {
+ streamCipher.processBytes(inBuf, 0, read, buf, 0);
+ maxBuf = read;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error processing stream " + e);
+ }
+ }
+ return maxBuf;
+ }
+
+ private void finaliseCipher()
+ throws IOException
+ {
+ try
+ {
+ finalized = true;
+ if (bufferedBlockCipher != null)
+ {
+ maxBuf = bufferedBlockCipher.doFinal(buf, 0);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ maxBuf = aeadBlockCipher.doFinal(buf, 0);
+ }
+ else
+ {
+ maxBuf = 0; // a stream cipher
+ }
+ }
+ catch (final InvalidCipherTextException e)
+ {
+ throw new InvalidCipherTextIOException("Error finalising cipher", e);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error finalising cipher " + e);
+ }
+ }
+
+ /**
+ * Reads data from the underlying stream and processes it with the cipher until the cipher
+ * outputs data, and returns the next available byte.
+ *
+ * If the underlying stream is exhausted by this call, the cipher will be finalised.
+ *
+ * @throws IOException if there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
+ public int read()
+ throws IOException
+ {
+ if (bufOff >= maxBuf)
+ {
+ if (nextChunk() < 0)
+ {
+ return -1;
+ }
+ }
+
+ return buf[bufOff++] & 0xff;
+ }
+
+ /**
+ * Reads data from the underlying stream and processes it with the cipher until the cipher
+ * outputs data, and then returns up to b.length
bytes in the provided array.
+ *
+ * If the underlying stream is exhausted by this call, the cipher will be finalised.
+ *
+ * @param b the buffer into which the data is read.
+ * @return the total number of bytes read into the buffer, or -1
if there is no
+ * more data because the end of the stream has been reached.
+ * @throws IOException if there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
+ public int read(
+ byte[] b)
+ throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Reads data from the underlying stream and processes it with the cipher until the cipher
+ * outputs data, and then returns up to len
bytes in the provided array.
+ *
+ * If the underlying stream is exhausted by this call, the cipher will be finalised.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset in the destination array b
+ * @param len the maximum number of bytes read.
+ * @return the total number of bytes read into the buffer, or -1
if there is no
+ * more data because the end of the stream has been reached.
+ * @throws IOException if there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ if (bufOff >= maxBuf)
+ {
+ if (nextChunk() < 0)
+ {
+ return -1;
+ }
+ }
+
+ int toSupply = Math.min(len, available());
+ System.arraycopy(buf, bufOff, b, off, toSupply);
+ bufOff += toSupply;
+ return toSupply;
+ }
+
+ public long skip(
+ long n)
+ throws IOException
+ {
+ if (n <= 0)
+ {
+ return 0;
+ }
+
+ int skip = (int)Math.min(n, available());
+ bufOff += skip;
+ return skip;
+ }
+
+ public int available()
+ throws IOException
+ {
+ return maxBuf - bufOff;
+ }
+
+ /**
+ * Closes the underlying input stream and finalises the processing of the data by the cipher.
+ *
+ * @throws IOException if there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
+ public void close()
+ throws IOException
+ {
+ try
+ {
+ in.close();
+ }
+ finally
+ {
+ if (!finalized)
+ {
+ // Reset the cipher, discarding any data buffered in it
+ // Errors in cipher finalisation trump I/O error closing input
+ finaliseCipher();
+ }
+ }
+ maxBuf = bufOff = 0;
+ }
+
+ public void mark(int readlimit)
+ {
+ }
+
+ public void reset()
+ throws IOException
+ {
+ }
+
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/CipherOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/CipherOutputStream.java
new file mode 100644
index 000000000..797e838be
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/CipherOutputStream.java
@@ -0,0 +1,266 @@
+package org.spongycastle.crypto.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+
+/**
+ * A CipherOutputStream is composed of an OutputStream and a cipher so that write() methods process
+ * the written data with the cipher, and the output of the cipher is in turn written to the
+ * underlying OutputStream. The cipher must be fully initialized before being used by a
+ * CipherInputStream.
+ *
+ * For example, if the cipher is initialized for encryption, the CipherOutputStream will encrypt the
+ * data before writing the encrypted data to the underlying stream.
+ */
+public class CipherOutputStream
+ extends FilterOutputStream
+{
+ private BufferedBlockCipher bufferedBlockCipher;
+ private StreamCipher streamCipher;
+ private AEADBlockCipher aeadBlockCipher;
+
+ private final byte[] oneByte = new byte[1];
+ private byte[] buf;
+
+ /**
+ * Constructs a CipherOutputStream from an OutputStream and a
+ * BufferedBlockCipher.
+ */
+ public CipherOutputStream(
+ OutputStream os,
+ BufferedBlockCipher cipher)
+ {
+ super(os);
+ this.bufferedBlockCipher = cipher;
+ }
+
+ /**
+ * Constructs a CipherOutputStream from an OutputStream and a
+ * BufferedBlockCipher.
+ */
+ public CipherOutputStream(
+ OutputStream os,
+ StreamCipher cipher)
+ {
+ super(os);
+ this.streamCipher = cipher;
+ }
+
+ /**
+ * Constructs a CipherOutputStream from an OutputStream and a AEADBlockCipher.
+ */
+ public CipherOutputStream(OutputStream os, AEADBlockCipher cipher)
+ {
+ super(os);
+ this.aeadBlockCipher = cipher;
+ }
+
+ /**
+ * Writes the specified byte to this output stream.
+ *
+ * @param b the byte
.
+ * @throws java.io.IOException if an I/O error occurs.
+ */
+ public void write(
+ int b)
+ throws IOException
+ {
+ oneByte[0] = (byte)b;
+
+ if (streamCipher != null)
+ {
+ out.write(streamCipher.returnByte((byte)b));
+ }
+ else
+ {
+ write(oneByte, 0, 1);
+ }
+ }
+
+ /**
+ * Writes b.length
bytes from the specified byte array
+ * to this output stream.
+ *
+ * The write
method of
+ * CipherOutputStream
calls the write
+ * method of three arguments with the three arguments
+ * b
, 0
, and b.length
.
+ *
+ * @param b the data.
+ * @throws java.io.IOException if an I/O error occurs.
+ * @see #write(byte[], int, int)
+ */
+ public void write(
+ byte[] b)
+ throws IOException
+ {
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Writes len
bytes from the specified byte array
+ * starting at offset off
to this output stream.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @throws java.io.IOException if an I/O error occurs.
+ */
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ ensureCapacity(len);
+
+ if (bufferedBlockCipher != null)
+ {
+ int outLen = bufferedBlockCipher.processBytes(b, off, len, buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ else if (aeadBlockCipher != null)
+ {
+ int outLen = aeadBlockCipher.processBytes(b, off, len, buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ else
+ {
+ streamCipher.processBytes(b, off, len, buf, 0);
+
+ out.write(buf, 0, len);
+ }
+ }
+
+ /**
+ * Ensure the ciphertext buffer has space sufficient to accept an upcoming output.
+ *
+ * @param outputSize the size of the pending update.
+ */
+ private void ensureCapacity(int outputSize)
+ {
+ // This overestimates buffer on updates for AEAD/padded, but keeps it simple.
+ int bufLen;
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getOutputSize(outputSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getOutputSize(outputSize);
+ }
+ else
+ {
+ bufLen = outputSize;
+ }
+ if ((buf == null) || (buf.length < bufLen))
+ {
+ buf = new byte[bufLen];
+ }
+ }
+
+ /**
+ * Flushes this output stream by forcing any buffered output bytes
+ * that have already been processed by the encapsulated cipher object
+ * to be written out.
+ *
+ *
+ * Any bytes buffered by the encapsulated cipher
+ * and waiting to be processed by it will not be written out. For example,
+ * if the encapsulated cipher is a block cipher, and the total number of
+ * bytes written using one of the write
methods is less than
+ * the cipher's block size, no bytes will be written out.
+ *
+ * @throws java.io.IOException if an I/O error occurs.
+ */
+ public void flush()
+ throws IOException
+ {
+ out.flush();
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ *
+ * This method invokes the doFinal
method of the encapsulated
+ * cipher object, which causes any bytes buffered by the encapsulated
+ * cipher to be processed. The result is written out by calling the
+ * flush
method of this output stream.
+ *
+ * This method resets the encapsulated cipher object to its initial state
+ * and calls the close
method of the underlying output
+ * stream.
+ *
+ * @throws java.io.IOException if an I/O error occurs.
+ * @throws InvalidCipherTextIOException if the data written to this stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
+ public void close()
+ throws IOException
+ {
+ ensureCapacity(0);
+ IOException error = null;
+ try
+ {
+ if (bufferedBlockCipher != null)
+ {
+ int outLen = bufferedBlockCipher.doFinal(buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ else if (aeadBlockCipher != null)
+ {
+ int outLen = aeadBlockCipher.doFinal(buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ }
+ catch (final InvalidCipherTextException e)
+ {
+ error = new InvalidCipherTextIOException("Error finalising cipher data", e);
+ }
+ catch (Exception e)
+ {
+ error = new IOException("Error closing stream: " + e);
+ }
+
+ try
+ {
+ flush();
+ out.close();
+ }
+ catch (IOException e)
+ {
+ // Invalid ciphertext takes precedence over close error
+ if (error == null)
+ {
+ error = e;
+ }
+ }
+ if (error != null)
+ {
+ throw error;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/DigestInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/DigestInputStream.java
new file mode 100644
index 000000000..23a719640
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/DigestInputStream.java
@@ -0,0 +1,52 @@
+package org.spongycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.crypto.Digest;
+
+public class DigestInputStream
+ extends FilterInputStream
+{
+ protected Digest digest;
+
+ public DigestInputStream(
+ InputStream stream,
+ Digest digest)
+ {
+ super(stream);
+ this.digest = digest;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ digest.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n > 0)
+ {
+ digest.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Digest getDigest()
+ {
+ return digest;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/DigestOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/DigestOutputStream.java
new file mode 100644
index 000000000..d280b514f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/DigestOutputStream.java
@@ -0,0 +1,42 @@
+package org.spongycastle.crypto.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.Digest;
+
+public class DigestOutputStream
+ extends OutputStream
+{
+ protected Digest digest;
+
+ public DigestOutputStream(
+ Digest Digest)
+ {
+ this.digest = Digest;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ digest.update((byte)b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ digest.update(b, off, len);
+ }
+
+ public byte[] getDigest()
+ {
+ byte[] res = new byte[digest.getDigestSize()];
+
+ digest.doFinal(res, 0);
+
+ return res;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/InvalidCipherTextIOException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/InvalidCipherTextIOException.java
new file mode 100644
index 000000000..60f218347
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/InvalidCipherTextIOException.java
@@ -0,0 +1,28 @@
+package org.spongycastle.crypto.io;
+
+import java.io.IOException;
+
+/**
+ * {@link IOException} wrapper around an exception indicating an invalid ciphertext, such as in
+ * authentication failure during finalisation of an AEAD cipher. For use in streams that need to
+ * expose invalid ciphertext errors.
+ */
+public class InvalidCipherTextIOException
+ extends IOException
+{
+ private static final long serialVersionUID = 1L;
+
+ private final Throwable cause;
+
+ public InvalidCipherTextIOException(String message, Throwable cause)
+ {
+ super(message);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/MacInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/MacInputStream.java
new file mode 100644
index 000000000..35e0e9c20
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/MacInputStream.java
@@ -0,0 +1,52 @@
+package org.spongycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.crypto.Mac;
+
+public class MacInputStream
+ extends FilterInputStream
+{
+ protected Mac mac;
+
+ public MacInputStream(
+ InputStream stream,
+ Mac mac)
+ {
+ super(stream);
+ this.mac = mac;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ mac.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n >= 0)
+ {
+ mac.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Mac getMac()
+ {
+ return mac;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/MacOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/MacOutputStream.java
new file mode 100644
index 000000000..340b994eb
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/MacOutputStream.java
@@ -0,0 +1,42 @@
+package org.spongycastle.crypto.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.Mac;
+
+public class MacOutputStream
+ extends OutputStream
+{
+ protected Mac mac;
+
+ public MacOutputStream(
+ Mac mac)
+ {
+ this.mac = mac;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ mac.update((byte)b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ mac.update(b, off, len);
+ }
+
+ public byte[] getMac()
+ {
+ byte[] res = new byte[mac.getMacSize()];
+
+ mac.doFinal(res, 0);
+
+ return res;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/SignerInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/SignerInputStream.java
new file mode 100644
index 000000000..f7f6935fa
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/SignerInputStream.java
@@ -0,0 +1,52 @@
+package org.spongycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.crypto.Signer;
+
+public class SignerInputStream
+ extends FilterInputStream
+{
+ protected Signer signer;
+
+ public SignerInputStream(
+ InputStream stream,
+ Signer signer)
+ {
+ super(stream);
+ this.signer = signer;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ signer.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n > 0)
+ {
+ signer.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Signer getSigner()
+ {
+ return signer;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/SignerOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/SignerOutputStream.java
new file mode 100644
index 000000000..6e6c55b16
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/io/SignerOutputStream.java
@@ -0,0 +1,38 @@
+package org.spongycastle.crypto.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.Signer;
+
+public class SignerOutputStream
+ extends OutputStream
+{
+ protected Signer signer;
+
+ public SignerOutputStream(
+ Signer Signer)
+ {
+ this.signer = Signer;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ signer.update((byte)b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ signer.update(b, off, len);
+ }
+
+ public Signer getSigner()
+ {
+ return signer;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/kems/ECIESKeyEncapsulation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/kems/ECIESKeyEncapsulation.java
new file mode 100755
index 000000000..bfe8c1077
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/kems/ECIESKeyEncapsulation.java
@@ -0,0 +1,255 @@
+package org.spongycastle.crypto.kems;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.KeyEncapsulation;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.KDFParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.BigIntegers;
+
+/**
+ * The ECIES Key Encapsulation Mechanism (ECIES-KEM) from ISO 18033-2.
+ */
+public class ECIESKeyEncapsulation
+ implements KeyEncapsulation
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private DerivationFunction kdf;
+ private SecureRandom rnd;
+ private ECKeyParameters key;
+ private boolean CofactorMode;
+ private boolean OldCofactorMode;
+ private boolean SingleHashMode;
+
+ /**
+ * Set up the ECIES-KEM.
+ *
+ * @param kdf the key derivation function to be used.
+ * @param rnd the random source for the session key.
+ */
+ public ECIESKeyEncapsulation(
+ DerivationFunction kdf,
+ SecureRandom rnd)
+ {
+ this.kdf = kdf;
+ this.rnd = rnd;
+ this.CofactorMode = false;
+ this.OldCofactorMode = false;
+ this.SingleHashMode = false;
+ }
+
+ /**
+ * Set up the ECIES-KEM.
+ *
+ * @param kdf the key derivation function to be used.
+ * @param rnd the random source for the session key.
+ * @param cofactorMode true to use the new cofactor ECDH.
+ * @param oldCofactorMode true to use the old cofactor ECDH.
+ * @param singleHashMode true to use single hash mode.
+ */
+ public ECIESKeyEncapsulation(
+ DerivationFunction kdf,
+ SecureRandom rnd,
+ boolean cofactorMode,
+ boolean oldCofactorMode,
+ boolean singleHashMode)
+ {
+ this.kdf = kdf;
+ this.rnd = rnd;
+
+ // If both cofactorMode and oldCofactorMode are set to true
+ // then the implementation will use the new cofactor ECDH
+ this.CofactorMode = cofactorMode;
+ this.OldCofactorMode = oldCofactorMode;
+ this.SingleHashMode = singleHashMode;
+ }
+
+ /**
+ * Initialise the ECIES-KEM.
+ *
+ * @param key the recipient's public (for encryption) or private (for decryption) key.
+ */
+ public void init(CipherParameters key)
+ throws IllegalArgumentException
+ {
+ if (!(key instanceof ECKeyParameters))
+ {
+ throw new IllegalArgumentException("EC key required");
+ }
+ else
+ {
+ this.key = (ECKeyParameters)key;
+ }
+ }
+
+ /**
+ * Generate and encapsulate a random session key.
+ *
+ * @param out the output buffer for the encapsulated key.
+ * @param outOff the offset for the output buffer.
+ * @param keyLen the length of the session key.
+ * @return the random session key.
+ */
+ public CipherParameters encrypt(byte[] out, int outOff, int keyLen)
+ throws IllegalArgumentException
+ {
+ if (!(key instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("Public key required for encryption");
+ }
+
+ ECPublicKeyParameters ecPubKey = (ECPublicKeyParameters)key;
+ ECDomainParameters ecParams = ecPubKey.getParameters();
+ ECCurve curve = ecParams.getCurve();
+ BigInteger n = ecParams.getN();
+ BigInteger h = ecParams.getH();
+
+ // Generate the ephemeral key pair
+ BigInteger r = BigIntegers.createRandomInRange(ONE, n, rnd);
+
+ // Compute the static-ephemeral key agreement
+ BigInteger rPrime = CofactorMode ? r.multiply(h).mod(n) : r;
+
+ ECPoint[] ghTilde = new ECPoint[]{
+ ecParams.getG().multiply(r),
+ ecPubKey.getQ().multiply(rPrime)
+ };
+
+ // NOTE: More efficient than normalizing each individually
+ curve.normalizeAll(ghTilde);
+
+ ECPoint gTilde = ghTilde[0], hTilde = ghTilde[1];
+
+ // Encode the ephemeral public key
+ byte[] C = gTilde.getEncoded();
+ System.arraycopy(C, 0, out, outOff, C.length);
+
+ // Encode the shared secret value
+ byte[] PEH = hTilde.getAffineXCoord().getEncoded();
+
+ // Initialise the KDF
+ byte[] kdfInput;
+ if (SingleHashMode)
+ {
+ kdfInput = new byte[C.length + PEH.length];
+ System.arraycopy(C, 0, kdfInput, 0, C.length);
+ System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length);
+ }
+ else
+ {
+ kdfInput = PEH;
+ }
+
+ kdf.init(new KDFParameters(kdfInput, null));
+
+ // Generate the secret key
+ byte[] K = new byte[keyLen];
+ kdf.generateBytes(K, 0, K.length);
+
+ // Return the ciphertext
+ return new KeyParameter(K);
+ }
+
+ /**
+ * Generate and encapsulate a random session key.
+ *
+ * @param out the output buffer for the encapsulated key.
+ * @param keyLen the length of the session key.
+ * @return the random session key.
+ */
+ public CipherParameters encrypt(byte[] out, int keyLen)
+ {
+ return encrypt(out, 0, keyLen);
+ }
+
+ /**
+ * Decrypt an encapsulated session key.
+ *
+ * @param in the input buffer for the encapsulated key.
+ * @param inOff the offset for the input buffer.
+ * @param inLen the length of the encapsulated key.
+ * @param keyLen the length of the session key.
+ * @return the session key.
+ */
+ public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen)
+ throws IllegalArgumentException
+ {
+ if (!(key instanceof ECPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("Private key required for encryption");
+ }
+
+ ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters)key;
+ ECDomainParameters ecParams = ecPrivKey.getParameters();
+ ECCurve curve = ecParams.getCurve();
+ BigInteger n = ecParams.getN();
+ BigInteger h = ecParams.getH();
+
+ // Decode the ephemeral public key
+ byte[] C = new byte[inLen];
+ System.arraycopy(in, inOff, C, 0, inLen);
+
+ // NOTE: Decoded points are already normalized (i.e in affine form)
+ ECPoint gTilde = curve.decodePoint(C);
+
+ // Compute the static-ephemeral key agreement
+ ECPoint gHat = gTilde;
+ if ((CofactorMode) || (OldCofactorMode))
+ {
+ gHat = gHat.multiply(h);
+ }
+
+ BigInteger xHat = ecPrivKey.getD();
+ if (CofactorMode)
+ {
+ xHat = xHat.multiply(h.modInverse(n)).mod(n);
+ }
+
+ ECPoint hTilde = gHat.multiply(xHat).normalize();
+
+ // Encode the shared secret value
+ byte[] PEH = hTilde.getAffineXCoord().getEncoded();
+
+ // Initialise the KDF
+ byte[] kdfInput;
+ if (SingleHashMode)
+ {
+ kdfInput = new byte[C.length + PEH.length];
+ System.arraycopy(C, 0, kdfInput, 0, C.length);
+ System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length);
+ }
+ else
+ {
+ kdfInput = PEH;
+ }
+ kdf.init(new KDFParameters(kdfInput, null));
+
+ // Generate the secret key
+ byte[] K = new byte[keyLen];
+ kdf.generateBytes(K, 0, K.length);
+
+ return new KeyParameter(K);
+ }
+
+ /**
+ * Decrypt an encapsulated session key.
+ *
+ * @param in the input buffer for the encapsulated key.
+ * @param keyLen the length of the session key.
+ * @return the session key.
+ */
+ public CipherParameters decrypt(byte[] in, int keyLen)
+ {
+ return decrypt(in, 0, in.length, keyLen);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/kems/RSAKeyEncapsulation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/kems/RSAKeyEncapsulation.java
new file mode 100755
index 000000000..5f9a7f519
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/kems/RSAKeyEncapsulation.java
@@ -0,0 +1,164 @@
+package org.spongycastle.crypto.kems;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.KeyEncapsulation;
+import org.spongycastle.crypto.params.KDFParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.util.BigIntegers;
+
+/**
+ * The RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
+ */
+public class RSAKeyEncapsulation
+ implements KeyEncapsulation
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private DerivationFunction kdf;
+ private SecureRandom rnd;
+ private RSAKeyParameters key;
+
+ /**
+ * Set up the RSA-KEM.
+ *
+ * @param kdf the key derivation function to be used.
+ * @param rnd the random source for the session key.
+ */
+ public RSAKeyEncapsulation(
+ DerivationFunction kdf,
+ SecureRandom rnd)
+ {
+ this.kdf = kdf;
+ this.rnd = rnd;
+ }
+
+
+ /**
+ * Initialise the RSA-KEM.
+ *
+ * @param key the recipient's public (for encryption) or private (for decryption) key.
+ */
+ public void init(CipherParameters key)
+ throws IllegalArgumentException
+ {
+ if (!(key instanceof RSAKeyParameters))
+ {
+ throw new IllegalArgumentException("RSA key required");
+ }
+ else
+ {
+ this.key = (RSAKeyParameters)key;
+ }
+ }
+
+
+ /**
+ * Generate and encapsulate a random session key.
+ *
+ * @param out the output buffer for the encapsulated key.
+ * @param outOff the offset for the output buffer.
+ * @param keyLen the length of the random session key.
+ * @return the random session key.
+ */
+ public CipherParameters encrypt(byte[] out, int outOff, int keyLen)
+ throws IllegalArgumentException
+ {
+ if (key.isPrivate())
+ {
+ throw new IllegalArgumentException("Public key required for encryption");
+ }
+
+ BigInteger n = key.getModulus();
+ BigInteger e = key.getExponent();
+
+ // Generate the ephemeral random and encode it
+ BigInteger r = BigIntegers.createRandomInRange(ZERO, n.subtract(ONE), rnd);
+ byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, r);
+
+ // Encrypt the random and encode it
+ BigInteger c = r.modPow(e, n);
+ byte[] C = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, c);
+ System.arraycopy(C, 0, out, outOff, C.length);
+
+
+ // Initialise the KDF
+ kdf.init(new KDFParameters(R, null));
+
+ // Generate the secret key
+ byte[] K = new byte[keyLen];
+ kdf.generateBytes(K, 0, K.length);
+
+ return new KeyParameter(K);
+ }
+
+
+ /**
+ * Generate and encapsulate a random session key.
+ *
+ * @param out the output buffer for the encapsulated key.
+ * @param keyLen the length of the random session key.
+ * @return the random session key.
+ */
+ public CipherParameters encrypt(byte[] out, int keyLen)
+ {
+ return encrypt(out, 0, keyLen);
+ }
+
+
+ /**
+ * Decrypt an encapsulated session key.
+ *
+ * @param in the input buffer for the encapsulated key.
+ * @param inOff the offset for the input buffer.
+ * @param inLen the length of the encapsulated key.
+ * @param keyLen the length of the session key.
+ * @return the session key.
+ */
+ public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen)
+ throws IllegalArgumentException
+ {
+ if (!key.isPrivate())
+ {
+ throw new IllegalArgumentException("Private key required for decryption");
+ }
+
+ BigInteger n = key.getModulus();
+ BigInteger d = key.getExponent();
+
+ // Decode the input
+ byte[] C = new byte[inLen];
+ System.arraycopy(in, inOff, C, 0, C.length);
+ BigInteger c = new BigInteger(1, C);
+
+ // Decrypt the ephemeral random and encode it
+ BigInteger r = c.modPow(d, n);
+ byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, r);
+
+ // Initialise the KDF
+ kdf.init(new KDFParameters(R, null));
+
+ // Generate the secret key
+ byte[] K = new byte[keyLen];
+ kdf.generateBytes(K, 0, K.length);
+
+ return new KeyParameter(K);
+ }
+
+ /**
+ * Decrypt an encapsulated session key.
+ *
+ * @param in the input buffer for the encapsulated key.
+ * @param keyLen the length of the session key.
+ * @return the session key.
+ */
+ public CipherParameters decrypt(byte[] in, int keyLen)
+ {
+ return decrypt(in, 0, in.length, keyLen);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/BlockCipherMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/BlockCipherMac.java
new file mode 100644
index 000000000..2446812cf
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/BlockCipherMac.java
@@ -0,0 +1,174 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+
+public class BlockCipherMac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private BlockCipher cipher;
+
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @deprecated use CBCBlockCipherMac
+ */
+ public BlockCipherMac(
+ BlockCipher cipher)
+ {
+ this(cipher, (cipher.getBlockSize() * 8) / 2);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits.
+ *
+ * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+ *
+ * @param params an instance of {@link SkeinParameters} or {@link KeyParameter}.
+ */
+ public void init(CipherParameters params)
+ throws IllegalArgumentException
+ {
+ SkeinParameters skeinParameters;
+ if (params instanceof SkeinParameters)
+ {
+ skeinParameters = (SkeinParameters)params;
+ }
+ else if (params instanceof KeyParameter)
+ {
+ skeinParameters = new SkeinParameters.Builder().setKey(((KeyParameter)params).getKey()).build();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Skein MAC init - "
+ + params.getClass().getName());
+ }
+ if (skeinParameters.getKey() == null)
+ {
+ throw new IllegalArgumentException("Skein MAC requires a key parameter.");
+ }
+ engine.init(skeinParameters);
+ }
+
+ public int getMacSize()
+ {
+ return engine.getOutputSize();
+ }
+
+ public void reset()
+ {
+ engine.reset();
+ }
+
+ public void update(byte in)
+ {
+ engine.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ engine.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return engine.doFinal(out, outOff);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/VMPCMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/VMPCMac.java
new file mode 100644
index 000000000..56a38ba91
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/VMPCMac.java
@@ -0,0 +1,186 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+public class VMPCMac implements Mac
+{
+ private byte g;
+
+ private byte n = 0;
+ private byte[] P = null;
+ private byte s = 0;
+
+ private byte[] T;
+ private byte[] workingIV;
+
+ private byte[] workingKey;
+
+ private byte x1, x2, x3, x4;
+
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ // Execute the Post-Processing Phase
+ for (int r = 1; r < 25; r++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+
+ x4 = P[(x4 + x3 + r) & 0xff];
+ x3 = P[(x3 + x2 + r) & 0xff];
+ x2 = P[(x2 + x1 + r) & 0xff];
+ x1 = P[(x1 + s + r) & 0xff];
+ T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1);
+ T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2);
+ T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3);
+ T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4);
+ g = (byte) ((g + 4) & 0x1f);
+
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+
+ // Input T to the IV-phase of the VMPC KSA
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ // Store 20 new outputs of the VMPC Stream Cipher in table M
+ byte[] M = new byte[20];
+ for (int i = 0; i < 20; i++)
+ {
+ s = P[(s + P[i & 0xff]) & 0xff];
+ M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+
+ byte temp = P[i & 0xff];
+ P[i & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ System.arraycopy(M, 0, out, outOff, M.length);
+ reset();
+
+ return M.length;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "VMPC-MAC";
+ }
+
+ public int getMacSize()
+ {
+ return 20;
+ }
+
+ public void init(CipherParameters params) throws IllegalArgumentException
+ {
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "VMPC-MAC Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV) params;
+ KeyParameter key = (KeyParameter) ivParams.getParameters();
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "VMPC-MAC Init parameters must include a key");
+ }
+
+ this.workingIV = ivParams.getIV();
+
+ if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
+ {
+ throw new IllegalArgumentException(
+ "VMPC-MAC requires 1 to 768 bytes of IV");
+ }
+
+ this.workingKey = key.getKey();
+
+ reset();
+
+ }
+
+ private void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ n = 0;
+ }
+
+ public void reset()
+ {
+ initKey(this.workingKey, this.workingIV);
+ g = x1 = x2 = x3 = x4 = n = 0;
+ T = new byte[32];
+ for (int i = 0; i < 32; i++)
+ {
+ T[i] = 0;
+ }
+ }
+
+ public void update(byte in) throws IllegalStateException
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte c = (byte) (in ^ P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]);
+
+ x4 = P[(x4 + x3) & 0xff];
+ x3 = P[(x3 + x2) & 0xff];
+ x2 = P[(x2 + x1) & 0xff];
+ x1 = P[(x1 + s + c) & 0xff];
+ T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1);
+ T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2);
+ T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3);
+ T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4);
+ g = (byte) ((g + 4) & 0x1f);
+
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ update(in[i]);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/AEADBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/AEADBlockCipher.java
new file mode 100644
index 000000000..ab8233824
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/AEADBlockCipher.java
@@ -0,0 +1,126 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A block cipher mode that includes authenticated encryption with a streaming mode and optional associated data.
+ * @see org.spongycastle.crypto.params.AEADParameters
+ */
+public interface AEADBlockCipher
+{
+ /**
+ * initialise the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object.
+ *
+ * @param forEncryption true if we are setting up for encryption, false otherwise.
+ * @param params the necessary parameters for the underlying cipher to be initialised.
+ * @exception IllegalArgumentException if the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm.
+ *
+ * @return the algorithm name.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * return the cipher this object wraps.
+ *
+ * @return the cipher this object wraps.
+ */
+ public BlockCipher getUnderlyingCipher();
+
+ /**
+ * Add a single byte to the associated data check.
+ *
If the implementation supports it, this will be an online operation and will not retain the associated data.
+ *
+ * @param in the byte to be processed.
+ */
+ public void processAADByte(byte in);
+
+ /**
+ * Add a sequence of bytes to the associated data check.
+ *
If the implementation supports it, this will be an online operation and will not retain the associated data.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ */
+ public void processAADBytes(byte[] in, int inOff, int len);
+
+ /**
+ * encrypt/decrypt a single byte.
+ *
+ * @param in the byte to be processed.
+ * @param out the output buffer the processed byte goes into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes written to out.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException;
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes written to out.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException;
+
+ /**
+ * Finish the operation either appending or verifying the MAC at the end of the data.
+ *
+ * @param out space for any resulting output data.
+ * @param outOff offset into out to start copying the data at.
+ * @return number of bytes written into out.
+ * @throws IllegalStateException if the cipher is in an inappropriate state.
+ * @throws org.spongycastle.crypto.InvalidCipherTextException if the MAC fails to match.
+ */
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException;
+
+ /**
+ * Return the value of the MAC associated with the last stream processed.
+ *
+ * @return MAC for plaintext data.
+ */
+ public byte[] getMac();
+
+ /**
+ * return the size of the output buffer required for a processBytes
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to processBytes
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(int len);
+
+ /**
+ * return the size of the output buffer required for a processBytes plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to processBytes and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(int len);
+
+ /**
+ * Reset the cipher. After resetting the cipher is in the same state
+ * as it was after the last init (if there was one).
+ */
+ public void reset();
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CBCBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CBCBlockCipher.java
new file mode 100644
index 000000000..381eab8a0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CBCBlockCipher.java
@@ -0,0 +1,253 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+
+/**
+ * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
+ */
+public class CBCBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] cbcV;
+ private byte[] cbcNextV;
+
+ private int blockSize;
+ private BlockCipher cipher = null;
+ private boolean encrypting;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of chaining.
+ */
+ public CBCBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.getBlockSize();
+
+ this.IV = new byte[blockSize];
+ this.cbcV = new byte[blockSize];
+ this.cbcNextV = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ boolean oldEncrypting = this.encrypting;
+
+ this.encrypting = encrypting;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length != blockSize)
+ {
+ throw new IllegalArgumentException("initialisation vector must be the same length as block size");
+ }
+
+ System.arraycopy(iv, 0, IV, 0, iv.length);
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(encrypting, ivParam.getParameters());
+ }
+ else if (oldEncrypting != encrypting)
+ {
+ throw new IllegalArgumentException("cannot change encrypting state without providing key.");
+ }
+ }
+ else
+ {
+ reset();
+
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
+ cipher.init(encrypting, params);
+ }
+ else if (oldEncrypting != encrypting)
+ {
+ throw new IllegalArgumentException("cannot change encrypting state without providing key.");
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CBC".
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CBC";
+ }
+
+ /**
+ * return the block size of the underlying cipher.
+ *
+ * @return the block size of the underlying cipher.
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, cbcV, 0, IV.length);
+ Arrays.fill(cbcNextV, (byte)0);
+
+ cipher.reset();
+ }
+
+ /**
+ * Do the appropriate chaining step for CBC mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ /*
+ * XOR the cbcV and the input,
+ * then encrypt the cbcV
+ */
+ for (int i = 0; i < blockSize; i++)
+ {
+ cbcV[i] ^= in[inOff + i];
+ }
+
+ int length = cipher.processBlock(cbcV, 0, out, outOff);
+
+ /*
+ * copy ciphertext to cbcV
+ */
+ System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
+
+ return length;
+ }
+
+ /**
+ * Do the appropriate chaining step for CBC mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the decrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
+
+ int length = cipher.processBlock(in, inOff, out, outOff);
+
+ /*
+ * XOR the cbcV and the output
+ */
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] ^= cbcV[i];
+ }
+
+ /*
+ * swap the back up buffer into next position
+ */
+ byte[] tmp;
+
+ tmp = cbcV;
+ cbcV = cbcNextV;
+ cbcNextV = tmp;
+
+ return length;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CCMBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CCMBlockCipher.java
new file mode 100644
index 000000000..289d611b5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/CCMBlockCipher.java
@@ -0,0 +1,452 @@
+package org.spongycastle.crypto.modes;
+
+import java.io.ByteArrayOutputStream;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.macs.CBCBlockCipherMac;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
+ * NIST Special Publication 800-38C.
+ * License for
+ * Open-Source Software Implementations of OCB (Jan 9, 2013) — “License 1”
+ */
+public class OCBBlockCipher
+ implements AEADBlockCipher
+{
+
+ private static final int BLOCK_SIZE = 16;
+
+ private BlockCipher hashCipher;
+ private BlockCipher mainCipher;
+
+ /*
+ * CONFIGURATION
+ */
+ private boolean forEncryption;
+ private int macSize;
+ private byte[] initialAssociatedText;
+
+ /*
+ * KEY-DEPENDENT
+ */
+ // NOTE: elements are lazily calculated
+ private Vector L;
+ private byte[] L_Asterisk, L_Dollar;
+
+ /*
+ * NONCE-DEPENDENT
+ */
+ private byte[] OffsetMAIN_0;
+
+ /*
+ * PER-ENCRYPTION/DECRYPTION
+ */
+ private byte[] hashBlock, mainBlock;
+ private int hashBlockPos, mainBlockPos;
+ private long hashBlockCount, mainBlockCount;
+ private byte[] OffsetHASH;
+ private byte[] Sum;
+ private byte[] OffsetMAIN;
+ private byte[] Checksum;
+
+ // NOTE: The MAC value is preserved after doFinal
+ private byte[] macBlock;
+
+ public OCBBlockCipher(BlockCipher hashCipher, BlockCipher mainCipher)
+ {
+ if (hashCipher == null)
+ {
+ throw new IllegalArgumentException("'hashCipher' cannot be null");
+ }
+ if (hashCipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("'hashCipher' must have a block size of "
+ + BLOCK_SIZE);
+ }
+ if (mainCipher == null)
+ {
+ throw new IllegalArgumentException("'mainCipher' cannot be null");
+ }
+ if (mainCipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("'mainCipher' must have a block size of "
+ + BLOCK_SIZE);
+ }
+
+ if (!hashCipher.getAlgorithmName().equals(mainCipher.getAlgorithmName()))
+ {
+ throw new IllegalArgumentException(
+ "'hashCipher' and 'mainCipher' must be the same algorithm");
+ }
+
+ this.hashCipher = hashCipher;
+ this.mainCipher = mainCipher;
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return mainCipher;
+ }
+
+ public String getAlgorithmName()
+ {
+ return mainCipher.getAlgorithmName() + "/OCB";
+ }
+
+ public void init(boolean forEncryption, CipherParameters parameters)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+ this.macBlock = null;
+
+ KeyParameter keyParameter;
+
+ byte[] N;
+ if (parameters instanceof AEADParameters)
+ {
+ AEADParameters aeadParameters = (AEADParameters)parameters;
+
+ N = aeadParameters.getNonce();
+ initialAssociatedText = aeadParameters.getAssociatedText();
+
+ int macSizeBits = aeadParameters.getMacSize();
+ if (macSizeBits < 64 || macSizeBits > 128 || macSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+ }
+
+ macSize = macSizeBits / 8;
+ keyParameter = aeadParameters.getKey();
+ }
+ else if (parameters instanceof ParametersWithIV)
+ {
+ ParametersWithIV parametersWithIV = (ParametersWithIV)parameters;
+
+ N = parametersWithIV.getIV();
+ initialAssociatedText = null;
+ macSize = 16;
+ keyParameter = (KeyParameter)parametersWithIV.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to OCB");
+ }
+
+ this.hashBlock = new byte[16];
+ this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)];
+
+ if (N == null)
+ {
+ N = new byte[0];
+ }
+
+ if (N.length > 15)
+ {
+ throw new IllegalArgumentException("IV must be no more than 15 bytes");
+ }
+
+ /*
+ * KEY-DEPENDENT INITIALISATION
+ */
+
+ if (keyParameter == null)
+ {
+ // TODO If 'keyParameter' is null we're re-using the last key.
+ }
+
+ // hashCipher always used in forward mode
+ hashCipher.init(true, keyParameter);
+ mainCipher.init(forEncryption, keyParameter);
+
+ this.L_Asterisk = new byte[16];
+ hashCipher.processBlock(L_Asterisk, 0, L_Asterisk, 0);
+
+ this.L_Dollar = OCB_double(L_Asterisk);
+
+ this.L = new Vector();
+ this.L.addElement(OCB_double(L_Dollar));
+
+ /*
+ * NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALISATION
+ */
+
+ byte[] nonce = new byte[16];
+ System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length);
+ nonce[0] = (byte)(macSize << 4);
+ nonce[15 - N.length] |= 1;
+
+ int bottom = nonce[15] & 0x3F;
+
+ byte[] Ktop = new byte[16];
+ nonce[15] &= 0xC0;
+ hashCipher.processBlock(nonce, 0, Ktop, 0);
+
+ byte[] Stretch = new byte[24];
+ System.arraycopy(Ktop, 0, Stretch, 0, 16);
+ for (int i = 0; i < 8; ++i)
+ {
+ Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
+ }
+
+ this.OffsetMAIN_0 = new byte[16];
+ int bits = bottom % 8, bytes = bottom / 8;
+ if (bits == 0)
+ {
+ System.arraycopy(Stretch, bytes, OffsetMAIN_0, 0, 16);
+ }
+ else
+ {
+ for (int i = 0; i < 16; ++i)
+ {
+ int b1 = Stretch[bytes] & 0xff;
+ int b2 = Stretch[++bytes] & 0xff;
+ this.OffsetMAIN_0[i] = (byte)((b1 << bits) | (b2 >>> (8 - bits)));
+ }
+ }
+
+ this.hashBlockPos = 0;
+ this.mainBlockPos = 0;
+
+ this.hashBlockCount = 0;
+ this.mainBlockCount = 0;
+
+ this.OffsetHASH = new byte[16];
+ this.Sum = new byte[16];
+ this.OffsetMAIN = Arrays.clone(this.OffsetMAIN_0);
+ this.Checksum = new byte[16];
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ public byte[] getMac()
+ {
+ return Arrays.clone(macBlock);
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + mainBlockPos;
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ int totalData = len + mainBlockPos;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % BLOCK_SIZE;
+ }
+
+ public void processAADByte(byte input)
+ {
+ hashBlock[hashBlockPos] = input;
+ if (++hashBlockPos == hashBlock.length)
+ {
+ processHashBlock();
+ }
+ }
+
+ public void processAADBytes(byte[] input, int off, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ hashBlock[hashBlockPos] = input[off + i];
+ if (++hashBlockPos == hashBlock.length)
+ {
+ processHashBlock();
+ }
+ }
+ }
+
+ public int processByte(byte input, byte[] output, int outOff)
+ throws DataLengthException
+ {
+ mainBlock[mainBlockPos] = input;
+ if (++mainBlockPos == mainBlock.length)
+ {
+ processMainBlock(output, outOff);
+ return BLOCK_SIZE;
+ }
+ return 0;
+ }
+
+ public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+ throws DataLengthException
+ {
+ int resultLen = 0;
+
+ for (int i = 0; i < len; ++i)
+ {
+ mainBlock[mainBlockPos] = input[inOff + i];
+ if (++mainBlockPos == mainBlock.length)
+ {
+ processMainBlock(output, outOff + resultLen);
+ resultLen += BLOCK_SIZE;
+ }
+ }
+
+ return resultLen;
+ }
+
+ public int doFinal(byte[] output, int outOff)
+ throws IllegalStateException,
+ InvalidCipherTextException
+ {
+ /*
+ * For decryption, get the tag from the end of the message
+ */
+ byte[] tag = null;
+ if (!forEncryption)
+ {
+ if (mainBlockPos < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ mainBlockPos -= macSize;
+ tag = new byte[macSize];
+ System.arraycopy(mainBlock, mainBlockPos, tag, 0, macSize);
+ }
+
+ /*
+ * HASH: Process any final partial block; compute final hash value
+ */
+ if (hashBlockPos > 0)
+ {
+ OCB_extend(hashBlock, hashBlockPos);
+ updateHASH(L_Asterisk);
+ }
+
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Process any final partial block
+ */
+ if (mainBlockPos > 0)
+ {
+ if (forEncryption)
+ {
+ OCB_extend(mainBlock, mainBlockPos);
+ xor(Checksum, mainBlock);
+ }
+
+ xor(OffsetMAIN, L_Asterisk);
+
+ byte[] Pad = new byte[16];
+ hashCipher.processBlock(OffsetMAIN, 0, Pad, 0);
+
+ xor(mainBlock, Pad);
+
+ System.arraycopy(mainBlock, 0, output, outOff, mainBlockPos);
+
+ if (!forEncryption)
+ {
+ OCB_extend(mainBlock, mainBlockPos);
+ xor(Checksum, mainBlock);
+ }
+ }
+
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Compute raw tag
+ */
+ xor(Checksum, OffsetMAIN);
+ xor(Checksum, L_Dollar);
+ hashCipher.processBlock(Checksum, 0, Checksum, 0);
+ xor(Checksum, Sum);
+
+ this.macBlock = new byte[macSize];
+ System.arraycopy(Checksum, 0, macBlock, 0, macSize);
+
+ /*
+ * Validate or append tag and reset this cipher for the next run
+ */
+ int resultLen = mainBlockPos;
+
+ if (forEncryption)
+ {
+ // Append tag to the message
+ System.arraycopy(macBlock, 0, output, outOff + resultLen, macSize);
+ resultLen += macSize;
+ }
+ else
+ {
+ // Compare the tag from the message with the calculated one
+ if (!Arrays.constantTimeAreEqual(macBlock, tag))
+ {
+ throw new InvalidCipherTextException("mac check in OCB failed");
+ }
+ }
+
+ reset(false);
+
+ return resultLen;
+ }
+
+ public void reset()
+ {
+ reset(true);
+ }
+
+ protected void clear(byte[] bs)
+ {
+ if (bs != null)
+ {
+ Arrays.fill(bs, (byte)0);
+ }
+ }
+
+ protected byte[] getLSub(int n)
+ {
+ while (n >= L.size())
+ {
+ L.addElement(OCB_double((byte[])L.lastElement()));
+ }
+ return (byte[])L.elementAt(n);
+ }
+
+ protected void processHashBlock()
+ {
+ /*
+ * HASH: Process any whole blocks
+ */
+ updateHASH(getLSub(OCB_ntz(++hashBlockCount)));
+ hashBlockPos = 0;
+ }
+
+ protected void processMainBlock(byte[] output, int outOff)
+ {
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
+ */
+
+ if (forEncryption)
+ {
+ xor(Checksum, mainBlock);
+ mainBlockPos = 0;
+ }
+
+ xor(OffsetMAIN, getLSub(OCB_ntz(++mainBlockCount)));
+
+ xor(mainBlock, OffsetMAIN);
+ mainCipher.processBlock(mainBlock, 0, mainBlock, 0);
+ xor(mainBlock, OffsetMAIN);
+
+ System.arraycopy(mainBlock, 0, output, outOff, 16);
+
+ if (!forEncryption)
+ {
+ xor(Checksum, mainBlock);
+ System.arraycopy(mainBlock, BLOCK_SIZE, mainBlock, 0, macSize);
+ mainBlockPos = macSize;
+ }
+ }
+
+ protected void reset(boolean clearMac)
+ {
+ hashCipher.reset();
+ mainCipher.reset();
+
+ clear(hashBlock);
+ clear(mainBlock);
+
+ hashBlockPos = 0;
+ mainBlockPos = 0;
+
+ hashBlockCount = 0;
+ mainBlockCount = 0;
+
+ clear(OffsetHASH);
+ clear(Sum);
+ System.arraycopy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
+ clear(Checksum);
+
+ if (clearMac)
+ {
+ macBlock = null;
+ }
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ protected void updateHASH(byte[] LSub)
+ {
+ xor(OffsetHASH, LSub);
+ xor(hashBlock, OffsetHASH);
+ hashCipher.processBlock(hashBlock, 0, hashBlock, 0);
+ xor(Sum, hashBlock);
+ }
+
+ protected static byte[] OCB_double(byte[] block)
+ {
+ byte[] result = new byte[16];
+ int carry = shiftLeft(block, result);
+
+ /*
+ * NOTE: This construction is an attempt at a constant-time implementation.
+ */
+ result[15] ^= (0x87 >>> ((1 - carry) << 3));
+
+ return result;
+ }
+
+ protected static void OCB_extend(byte[] block, int pos)
+ {
+ block[pos] = (byte)0x80;
+ while (++pos < 16)
+ {
+ block[pos] = 0;
+ }
+ }
+
+ protected static int OCB_ntz(long x)
+ {
+ if (x == 0)
+ {
+ return 64;
+ }
+
+ int n = 0;
+ while ((x & 1L) == 0L)
+ {
+ ++n;
+ x >>= 1;
+ }
+ return n;
+ }
+
+ protected static int shiftLeft(byte[] block, byte[] output)
+ {
+ int i = 16;
+ int bit = 0;
+ while (--i >= 0)
+ {
+ int b = block[i] & 0xff;
+ output[i] = (byte)((b << 1) | bit);
+ bit = (b >>> 7) & 1;
+ }
+ return bit;
+ }
+
+ protected static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OFBBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OFBBlockCipher.java
new file mode 100644
index 000000000..3756c7f13
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OFBBlockCipher.java
@@ -0,0 +1,187 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements a Output-FeedBack (OFB) mode on top of a simple cipher.
+ */
+public class OFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] ofbV;
+ private byte[] ofbOutV;
+
+ private final int blockSize;
+ private final BlockCipher cipher;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public OFBBlockCipher(
+ BlockCipher cipher,
+ int blockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = blockSize / 8;
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.ofbV = new byte[cipher.getBlockSize()];
+ this.ofbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting, //ignored by this OFB mode
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ reset();
+
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
+ cipher.init(true, params);
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/OFB"
+ * and the block size in bits
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8);
+ }
+
+
+ /**
+ * return the block size we are operating at (in bytes).
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * 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.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+
+ //
+ // XOR the ofbV with the plaintext producing the cipher text (and
+ // the next input block).
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
+ System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the feedback vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, ofbV, 0, IV.length);
+
+ cipher.reset();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OldCTSBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OldCTSBlockCipher.java
new file mode 100644
index 000000000..30402b55f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/modes/OldCTSBlockCipher.java
@@ -0,0 +1,269 @@
+package org.spongycastle.crypto.modes;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+ * be used to produce cipher text which is the same length as the plain text.
+ *
+ * Under this license, you are authorized to make, use, and distribute open-source software
+ * implementations of OCB. This license terminates for you if you sue someone over their open-source
+ * software implementation of OCB claiming that you have a patent covering their implementation.
+ *
+ * This is a non-binding summary of a legal document (the link above). The parameters of the license
+ * are specified in the license document and that document is controlling.
+ *
+ *
+ * @see SkeinEngine
+ * @see SkeinDigest
+ * @see SkeinMac
+ */
+public class SkeinParameters
+ implements CipherParameters
+{
+ /**
+ * The parameter type for a secret key, supporting MAC or KDF functions: {@value
+ * #PARAM_TYPE_KEY}.
+ */
+ public static final int PARAM_TYPE_KEY = 0;
+
+ /**
+ * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}.
+ */
+ public static final int PARAM_TYPE_CONFIG = 4;
+
+ /**
+ * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}.
+ */
+ public static final int PARAM_TYPE_PERSONALISATION = 8;
+
+ /**
+ * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}.
+ */
+ public static final int PARAM_TYPE_PUBLIC_KEY = 12;
+
+ /**
+ * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}.
+ */
+ public static final int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+ /**
+ * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}.
+ */
+ public static final int PARAM_TYPE_NONCE = 20;
+
+ /**
+ * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}.
+ */
+ public static final int PARAM_TYPE_MESSAGE = 48;
+
+ /**
+ * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}.
+ */
+ public static final int PARAM_TYPE_OUTPUT = 63;
+
+ private Hashtable parameters;
+
+ public SkeinParameters()
+ {
+ this(new Hashtable());
+ }
+
+ private SkeinParameters(final Hashtable parameters)
+ {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object.
+ */
+ public Hashtable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or null
if not
+ * set.
+ */
+ public byte[] getKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+ * null
if not set.
+ */
+ public byte[] getPersonalisation()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+ * null
if not set.
+ */
+ public byte[] getPublicKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+ * null
if not set.
+ */
+ public byte[] getKeyIdentifier()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or null
if
+ * not set.
+ */
+ public byte[] getNonce()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE));
+ }
+
+ /**
+ * A builder for {@link SkeinParameters}.
+ */
+ public static class Builder
+ {
+ private Hashtable parameters = new Hashtable();
+
+ public Builder()
+ {
+ }
+
+ public Builder(Hashtable paramsMap)
+ {
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, paramsMap.get(key));
+ }
+ }
+
+ public Builder(SkeinParameters params)
+ {
+ Enumeration keys = params.parameters.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, params.parameters.get(key));
+ }
+ }
+
+ /**
+ * Sets a parameters to apply to the Skein hash function.
+ * Parameter types must be in the range 0,5..62, and cannot use the value {@value
+ * SkeinParameters#PARAM_TYPE_MESSAGE} (reserved for message body).
+ *
+ * Parameters with type < {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before
+ * the message content, parameters with type > {@value SkeinParameters#PARAM_TYPE_MESSAGE}
+ * are processed after the message and prior to output.
+ *
+ * @param type the type of the parameter, in the range 5..62.
+ * @param value the byte sequence of the parameter.
+ * @return
+ */
+ public Builder set(int type, byte[] value)
+ {
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter value must not be null.");
+ }
+ if ((type != PARAM_TYPE_KEY)
+ && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+ {
+ throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+ }
+ if (type == PARAM_TYPE_CONFIG)
+ {
+ throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+ + " is reserved for internal use.");
+ }
+ this.parameters.put(Integers.valueOf(type), value);
+ return this;
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY} parameter.
+ */
+ public Builder setKey(byte[] key)
+ {
+ return set(PARAM_TYPE_KEY, key);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_PERSONALISATION} parameter.
+ */
+ public Builder setPersonalisation(byte[] personalisation)
+ {
+ return set(PARAM_TYPE_PERSONALISATION, personalisation);
+ }
+
+ /**
+ * Implements the recommended personalisation format for Skein defined in Section 4.11 of
+ * the Skein 1.3 specification.
+ *
+ * The format is YYYYMMDD email@address distinguisher
, encoded to a byte
+ * sequence using UTF-8 encoding.
+ *
+ * @param date the date the personalised application of the Skein was defined.
+ * @param emailAddress the email address of the creation of the personalised application.
+ * @param distinguisher an arbitrary personalisation string distinguishing the application.
+ * @return
+ */
+ public Builder setPersonalisation(Date date, String emailAddress, String distinguisher)
+ {
+ try
+ {
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+ final DateFormat format = new SimpleDateFormat("YYYYMMDD");
+ out.write(format.format(date));
+ out.write(" ");
+ out.write(emailAddress);
+ out.write(" ");
+ out.write(distinguisher);
+ out.close();
+ return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Byte I/O failed: " + e);
+ }
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setPublicKey(byte[] publicKey)
+ {
+ return set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setKeyIdentifier(byte[] keyIdentifier)
+ {
+ return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_NONCE} parameter.
+ */
+ public Builder setNonce(byte[] nonce)
+ {
+ return set(PARAM_TYPE_NONCE, nonce);
+ }
+
+ /**
+ * Constructs a new {@link SkeinParameters} instance with the parameters provided to this
+ * builder.
+ */
+ public SkeinParameters build()
+ {
+ return new SkeinParameters(parameters);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/TweakableBlockCipherParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/TweakableBlockCipherParameters.java
new file mode 100644
index 000000000..2a12186ea
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/params/TweakableBlockCipherParameters.java
@@ -0,0 +1,40 @@
+package org.spongycastle.crypto.params;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Parameters for tweakable block ciphers.
+ */
+public class TweakableBlockCipherParameters
+ implements CipherParameters
+{
+ private final byte[] tweak;
+ private final KeyParameter key;
+
+ public TweakableBlockCipherParameters(final KeyParameter key, final byte[] tweak)
+ {
+ this.key = key;
+ this.tweak = Arrays.clone(tweak);
+ }
+
+ /**
+ * Gets the key.
+ *
+ * @return the key to use, or null
to use the current key.
+ */
+ public KeyParameter getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Gets the tweak value.
+ *
+ * @return the tweak to use, or null
to use the current tweak.
+ */
+ public byte[] getTweak()
+ {
+ return tweak;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/parsers/DHIESPublicKeyParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/parsers/DHIESPublicKeyParser.java
new file mode 100644
index 000000000..26ba1193a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/parsers/DHIESPublicKeyParser.java
@@ -0,0 +1,31 @@
+package org.spongycastle.crypto.parsers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.KeyParser;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.crypto.params.DHPublicKeyParameters;
+
+public class DHIESPublicKeyParser
+ implements KeyParser
+{
+ private DHParameters dhParams;
+
+ public DHIESPublicKeyParser(DHParameters dhParams)
+ {
+ this.dhParams = dhParams;
+ }
+
+ public AsymmetricKeyParameter readKey(InputStream stream)
+ throws IOException
+ {
+ byte[] V = new byte[(dhParams.getP().bitLength() + 7) / 8];
+
+ stream.read(V, 0, V.length);
+
+ return new DHPublicKeyParameters(new BigInteger(1, V), dhParams);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/parsers/ECIESPublicKeyParser.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/parsers/ECIESPublicKeyParser.java
new file mode 100644
index 000000000..aed4a6253
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/parsers/ECIESPublicKeyParser.java
@@ -0,0 +1,53 @@
+package org.spongycastle.crypto.parsers;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.crypto.KeyParser;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+
+public class ECIESPublicKeyParser
+ implements KeyParser
+{
+ private ECDomainParameters ecParams;
+
+ public ECIESPublicKeyParser(ECDomainParameters ecParams)
+ {
+ this.ecParams = ecParams;
+ }
+
+ public AsymmetricKeyParameter readKey(InputStream stream)
+ throws IOException
+ {
+ byte[] V;
+ int first = stream.read();
+
+ // Decode the public ephemeral key
+ switch (first)
+ {
+ case 0x00: // infinity
+ throw new IOException("Sender's public key invalid.");
+
+ case 0x02: // compressed
+ case 0x03: // Byte length calculated as in ECPoint.getEncoded();
+ V = new byte[1 + (ecParams.getCurve().getFieldSize()+7)/8];
+ break;
+
+ case 0x04: // uncompressed or
+ case 0x06: // hybrid
+ case 0x07: // Byte length calculated as in ECPoint.getEncoded();
+ V = new byte[1 + 2*((ecParams.getCurve().getFieldSize()+7)/8)];
+ break;
+
+ default:
+ throw new IOException("Sender's public key has invalid point encoding 0x" + Integer.toString(first, 16));
+ }
+
+ V[0] = (byte)first;
+ stream.read(V, 1, V.length - 1);
+
+ return new ECPublicKeyParameters(ecParams.getCurve().decodePoint(V), ecParams);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java
new file mode 100644
index 000000000..176b5fb0f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java
@@ -0,0 +1,53 @@
+package org.spongycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+/**
+ * An EntropySourceProvider where entropy generation is based on a SecureRandom output using SecureRandom.generateSeed().
+ */
+public class BasicEntropySourceProvider
+ implements EntropySourceProvider
+{
+ private final SecureRandom _sr;
+ private final boolean _predictionResistant;
+
+ /**
+ * Create a entropy source provider based on the passed in SecureRandom.
+ *
+ * @param random the SecureRandom to base EntropySource construction on.
+ * @param isPredictionResistant boolean indicating if the SecureRandom is based on prediction resistant entropy or not (true if it is).
+ */
+ public BasicEntropySourceProvider(SecureRandom random, boolean isPredictionResistant)
+ {
+ _sr = random;
+ _predictionResistant = isPredictionResistant;
+ }
+
+ /**
+ * Return an entropy source that will create bitsRequired bits of entropy on
+ * each invocation of getEntropy().
+ *
+ * @param bitsRequired size (in bits) of entropy to be created by the provided source.
+ * @return an EntropySource that generates bitsRequired bits of entropy on each call to its getEntropy() method.
+ */
+ public EntropySource get(final int bitsRequired)
+ {
+ return new EntropySource()
+ {
+ public boolean isPredictionResistant()
+ {
+ return _predictionResistant;
+ }
+
+ public byte[] getEntropy()
+ {
+ return _sr.generateSeed((bitsRequired + 7) / 8);
+ }
+
+ public int entropySize()
+ {
+ return bitsRequired;
+ }
+ };
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java
new file mode 100644
index 000000000..f90673234
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java
@@ -0,0 +1,8 @@
+package org.spongycastle.crypto.prng;
+
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+
+interface DRBGProvider
+{
+ SP80090DRBG get(EntropySource entropySource);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java
new file mode 100644
index 000000000..5505338ad
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java
@@ -0,0 +1,123 @@
+package org.spongycastle.crypto.prng;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * Random generation based on the digest with counter. Calling addSeedMaterial will
+ * always increase the entropy of the hash.
+ *
+ * // First 1850 fractional digit of Pi number.
+ * byte[] key = new BigInteger("14159265358979323846...5068006422512520511").toByteArray();
+ * s = 0;
+ * P = new byte[256];
+ * for (int i = 0; i < 256; i++) {
+ * P[i] = (byte) i;
+ * }
+ * for (int m = 0; m < 768; m++) {
+ * s = P[(s + P[m & 0xff] + key[m % key.length]) & 0xff];
+ * byte temp = P[m & 0xff];
+ * P[m & 0xff] = P[s & 0xff];
+ * P[s & 0xff] = temp;
+ * }
+ */
+ private byte[] P =
+ {
+ (byte) 0xbb, (byte) 0x2c, (byte) 0x62, (byte) 0x7f,
+ (byte) 0xb5, (byte) 0xaa, (byte) 0xd4, (byte) 0x0d, (byte) 0x81,
+ (byte) 0xfe, (byte) 0xb2, (byte) 0x82, (byte) 0xcb, (byte) 0xa0,
+ (byte) 0xa1, (byte) 0x08, (byte) 0x18, (byte) 0x71, (byte) 0x56,
+ (byte) 0xe8, (byte) 0x49, (byte) 0x02, (byte) 0x10, (byte) 0xc4,
+ (byte) 0xde, (byte) 0x35, (byte) 0xa5, (byte) 0xec, (byte) 0x80,
+ (byte) 0x12, (byte) 0xb8, (byte) 0x69, (byte) 0xda, (byte) 0x2f,
+ (byte) 0x75, (byte) 0xcc, (byte) 0xa2, (byte) 0x09, (byte) 0x36,
+ (byte) 0x03, (byte) 0x61, (byte) 0x2d, (byte) 0xfd, (byte) 0xe0,
+ (byte) 0xdd, (byte) 0x05, (byte) 0x43, (byte) 0x90, (byte) 0xad,
+ (byte) 0xc8, (byte) 0xe1, (byte) 0xaf, (byte) 0x57, (byte) 0x9b,
+ (byte) 0x4c, (byte) 0xd8, (byte) 0x51, (byte) 0xae, (byte) 0x50,
+ (byte) 0x85, (byte) 0x3c, (byte) 0x0a, (byte) 0xe4, (byte) 0xf3,
+ (byte) 0x9c, (byte) 0x26, (byte) 0x23, (byte) 0x53, (byte) 0xc9,
+ (byte) 0x83, (byte) 0x97, (byte) 0x46, (byte) 0xb1, (byte) 0x99,
+ (byte) 0x64, (byte) 0x31, (byte) 0x77, (byte) 0xd5, (byte) 0x1d,
+ (byte) 0xd6, (byte) 0x78, (byte) 0xbd, (byte) 0x5e, (byte) 0xb0,
+ (byte) 0x8a, (byte) 0x22, (byte) 0x38, (byte) 0xf8, (byte) 0x68,
+ (byte) 0x2b, (byte) 0x2a, (byte) 0xc5, (byte) 0xd3, (byte) 0xf7,
+ (byte) 0xbc, (byte) 0x6f, (byte) 0xdf, (byte) 0x04, (byte) 0xe5,
+ (byte) 0x95, (byte) 0x3e, (byte) 0x25, (byte) 0x86, (byte) 0xa6,
+ (byte) 0x0b, (byte) 0x8f, (byte) 0xf1, (byte) 0x24, (byte) 0x0e,
+ (byte) 0xd7, (byte) 0x40, (byte) 0xb3, (byte) 0xcf, (byte) 0x7e,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9a, (byte) 0x4d, (byte) 0x1c,
+ (byte) 0xa3, (byte) 0xdb, (byte) 0x32, (byte) 0x92, (byte) 0x58,
+ (byte) 0x11, (byte) 0x27, (byte) 0xf4, (byte) 0x59, (byte) 0xd0,
+ (byte) 0x4e, (byte) 0x6a, (byte) 0x17, (byte) 0x5b, (byte) 0xac,
+ (byte) 0xff, (byte) 0x07, (byte) 0xc0, (byte) 0x65, (byte) 0x79,
+ (byte) 0xfc, (byte) 0xc7, (byte) 0xcd, (byte) 0x76, (byte) 0x42,
+ (byte) 0x5d, (byte) 0xe7, (byte) 0x3a, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x30, (byte) 0x28, (byte) 0x0f, (byte) 0x73, (byte) 0x01,
+ (byte) 0xf9, (byte) 0xd1, (byte) 0xd2, (byte) 0x19, (byte) 0xe9,
+ (byte) 0x91, (byte) 0xb9, (byte) 0x5a, (byte) 0xed, (byte) 0x41,
+ (byte) 0x6d, (byte) 0xb4, (byte) 0xc3, (byte) 0x9e, (byte) 0xbf,
+ (byte) 0x63, (byte) 0xfa, (byte) 0x1f, (byte) 0x33, (byte) 0x60,
+ (byte) 0x47, (byte) 0x89, (byte) 0xf0, (byte) 0x96, (byte) 0x1a,
+ (byte) 0x5f, (byte) 0x93, (byte) 0x3d, (byte) 0x37, (byte) 0x4b,
+ (byte) 0xd9, (byte) 0xa8, (byte) 0xc1, (byte) 0x1b, (byte) 0xf6,
+ (byte) 0x39, (byte) 0x8b, (byte) 0xb7, (byte) 0x0c, (byte) 0x20,
+ (byte) 0xce, (byte) 0x88, (byte) 0x6e, (byte) 0xb6, (byte) 0x74,
+ (byte) 0x8e, (byte) 0x8d, (byte) 0x16, (byte) 0x29, (byte) 0xf2,
+ (byte) 0x87, (byte) 0xf5, (byte) 0xeb, (byte) 0x70, (byte) 0xe3,
+ (byte) 0xfb, (byte) 0x55, (byte) 0x9f, (byte) 0xc6, (byte) 0x44,
+ (byte) 0x4a, (byte) 0x45, (byte) 0x7d, (byte) 0xe2, (byte) 0x6b,
+ (byte) 0x5c, (byte) 0x6c, (byte) 0x66, (byte) 0xa9, (byte) 0x8c,
+ (byte) 0xee, (byte) 0x84, (byte) 0x13, (byte) 0xa7, (byte) 0x1e,
+ (byte) 0x9d, (byte) 0xdc, (byte) 0x67, (byte) 0x48, (byte) 0xba,
+ (byte) 0x2e, (byte) 0xe6, (byte) 0xa4, (byte) 0xab, (byte) 0x7c,
+ (byte) 0x94, (byte) 0x00, (byte) 0x21, (byte) 0xef, (byte) 0xea,
+ (byte) 0xbe, (byte) 0xca, (byte) 0x72, (byte) 0x4f, (byte) 0x52,
+ (byte) 0x98, (byte) 0x3f, (byte) 0xc2, (byte) 0x14, (byte) 0x7b,
+ (byte) 0x3b, (byte) 0x54 };
+
+ /**
+ * Value generated in the same way as {@link VMPCRandomGenerator#P};
+ */
+ private byte s = (byte) 0xbe;
+
+ public VMPCRandomGenerator()
+ {
+ }
+
+ public void addSeedMaterial(byte[] seed)
+ {
+ for (int m = 0; m < seed.length; m++)
+ {
+ s = P[(s + P[n & 0xff] + seed[m]) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+
+ public void addSeedMaterial(long seed)
+ {
+ addSeedMaterial(Pack.longToBigEndian(seed));
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ nextBytes(bytes, 0, bytes.length);
+ }
+
+ public void nextBytes(byte[] bytes, int start, int len)
+ {
+ synchronized (P)
+ {
+ int end = start + len;
+ for (int i = start; i != end; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ bytes[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java
new file mode 100644
index 000000000..5fbdf37e1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java
@@ -0,0 +1,468 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+
+/**
+ * A SP800-90A CTR DRBG.
+ */
+public class CTRSP800DRBG
+ implements SP80090DRBG
+{
+ private static final long TDEA_RESEED_MAX = 1L << (32 - 1);
+ private static final long AES_RESEED_MAX = 1L << (48 - 1);
+ private static final int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1);
+ private static final int AES_MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private EntropySource _entropySource;
+ private BlockCipher _engine;
+ private int _keySizeInBits;
+ private int _seedLength;
+
+ // internal state
+ private byte[] _Key;
+ private byte[] _V;
+ private long _reseedCounter = 0;
+ private boolean _isTDEA = false;
+
+ /**
+ * Construct a SP800-90A CTR DRBG.
+ *
+ * max_outlen = largest multiple of 8 less than ((field size in bits) - (13 + log2(cofactor))
+ *
+ *
+ * Minimum entropy requirement is the security strength requested. + *
+ * @param digest source digest to use with the DRB stream. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public DualECSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) + { + this(nistPoints, digest, securityStrength, entropySource, personalizationString, nonce); + } + + /** + * Construct a SP800-90A Dual EC DRBG. + *+ * Minimum entropy requirement is the security strength requested. + *
+ * @param pointSet an array of points to choose from, in order of increasing security strength + * @param digest source digest to use with the DRB stream. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public DualECSP800DRBG(DualECPoints[] pointSet, Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) + { + _digest = digest; + _entropySource = entropySource; + _securityStrength = securityStrength; + + if (Utils.isTooLarge(personalizationString, MAX_PERSONALIZATION_STRING / 8)) + { + throw new IllegalArgumentException("Personalization string too large"); + } + + if (entropySource.entropySize() < securityStrength || entropySource.entropySize() > MAX_ENTROPY_LENGTH) + { + throw new IllegalArgumentException("EntropySource must provide between " + securityStrength + " and " + MAX_ENTROPY_LENGTH + " bits"); + } + + byte[] entropy = entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); + + for (int i = 0; i != pointSet.length; i++) + { + if (securityStrength <= pointSet[i].getSecurityStrength()) + { + if (Utils.getMaxSecurityStrength(digest) < pointSet[i].getSecurityStrength()) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } + _seedlen = pointSet[i].getSeedLen(); + _outlen = pointSet[i].getMaxOutlen() / 8; + _P = pointSet[i].getP(); + _Q = pointSet[i].getQ(); + break; + } + } + + if (_P == null) + { + throw new IllegalArgumentException("security strength cannot be greater than 256 bits"); + } + + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); + _sLength = _s.length; + + _reseedCounter = 0; + } + + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) + { + int numberOfBits = output.length*8; + int m = output.length / _outlen; + + if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8)) + { + throw new IllegalArgumentException("Additional input too large"); + } + + if (_reseedCounter + m > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) + { + reseed(additionalInput); + additionalInput = null; + } + + BigInteger s; + + if (additionalInput != null) + { + // Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it. + additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen); + s = new BigInteger(1, xor(_s, additionalInput)); + } + else + { + s = new BigInteger(1, _s); + } + + // make sure we start with a clean output array. + Arrays.fill(output, (byte)0); + + int outOffset = 0; + + for (int i = 0; i < m; i++) + { + s = getScalarMultipleXCoord(_P, s); + + //System.err.println("S: " + new String(Hex.encode(_s))); + + byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray(); + + if (r.length > _outlen) + { + System.arraycopy(r, r.length - _outlen, output, outOffset, _outlen); + } + else + { + System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), r.length); + } + + //System.err.println("R: " + new String(Hex.encode(r))); + outOffset += _outlen; + + _reseedCounter++; + } + + if (outOffset < output.length) + { + s = getScalarMultipleXCoord(_P, s); + + byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray(); + + int required = output.length - outOffset; + + if (r.length > _outlen) + { + System.arraycopy(r, r.length - _outlen, output, outOffset, required); + } + else + { + System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), required); + } + + _reseedCounter++; + } + + // Need to preserve length of S as unsigned int. + _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(s).normalize().getAffineXCoord().toBigInteger()); + + return numberOfBits; + } + + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ + public void reseed(byte[] additionalInput) + { + if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8)) + { + throw new IllegalArgumentException("Additional input string too large"); + } + + byte[] entropy = _entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(pad8(_s, _seedlen), entropy, additionalInput); + + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); + + _reseedCounter = 0; + } + + private byte[] xor(byte[] a, byte[] b) + { + if (b == null) + { + return a; + } + + byte[] rv = new byte[a.length]; + + for (int i = 0; i != rv.length; i++) + { + rv[i] = (byte)(a[i] ^ b[i]); + } + + return rv; + } + + // Note: works in place + private byte[] pad8(byte[] s, int seedlen) + { + if (seedlen % 8 == 0) + { + return s; + } + + int shift = 8 - (seedlen % 8); + int carry = 0; + + for (int i = s.length - 1; i >= 0; i--) + { + int b = s[i] & 0xff; + s[i] = (byte)((b << shift) | (carry >> (8 - shift))); + carry = b; + } + + return s; + } + + private BigInteger getScalarMultipleXCoord(ECPoint p, BigInteger s) + { + return p.multiply(s).normalize().getAffineXCoord().toBigInteger(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java new file mode 100644 index 000000000..ad93896cc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java @@ -0,0 +1,171 @@ +package org.spongycastle.crypto.prng.drbg; + +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.prng.EntropySource; +import org.spongycastle.util.Arrays; + +/** + * A SP800-90A HMAC DRBG. + */ +public class HMacSP800DRBG + implements SP80090DRBG +{ + private final static long RESEED_MAX = 1L << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + + private byte[] _K; + private byte[] _V; + private long _reseedCounter; + private EntropySource _entropySource; + private Mac _hMac; + + /** + * Construct a SP800-90A Hash DRBG. + *+ * Minimum entropy requirement is the security strength requested. + *
+ * @param hMac Hash MAC to base the DRBG on. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public HMacSP800DRBG(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) + { + if (securityStrength > Utils.getMaxSecurityStrength(hMac)) + { + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } + + _entropySource = entropySource; + _hMac = hMac; + + byte[] entropy = entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); + + _K = new byte[hMac.getMacSize()]; + _V = new byte[_K.length]; + Arrays.fill(_V, (byte)1); + + hmac_DRBG_Update(seedMaterial); + + _reseedCounter = 1; + } + + private void hmac_DRBG_Update(byte[] seedMaterial) + { + hmac_DRBG_Update_Func(seedMaterial, (byte)0x00); + if (seedMaterial != null) + { + hmac_DRBG_Update_Func(seedMaterial, (byte)0x01); + } + } + + private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue) + { + _hMac.init(new KeyParameter(_K)); + + _hMac.update(_V, 0, _V.length); + _hMac.update(vValue); + + if (seedMaterial != null) + { + _hMac.update(seedMaterial, 0, seedMaterial.length); + } + + _hMac.doFinal(_K, 0); + + _hMac.init(new KeyParameter(_K)); + _hMac.update(_V, 0, _V.length); + + _hMac.doFinal(_V, 0); + } + + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) + { + int numberOfBits = output.length * 8; + + if (numberOfBits > MAX_BITS_REQUEST) + { + throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); + } + + if (_reseedCounter > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) + { + reseed(additionalInput); + additionalInput = null; + } + + // 2. + if (additionalInput != null) + { + hmac_DRBG_Update(additionalInput); + } + + // 3. + byte[] rv = new byte[output.length]; + + int m = output.length / _V.length; + + _hMac.init(new KeyParameter(_K)); + + for (int i = 0; i < m; i++) + { + _hMac.update(_V, 0, _V.length); + _hMac.doFinal(_V, 0); + + System.arraycopy(_V, 0, rv, i * _V.length, _V.length); + } + + if (m * _V.length < rv.length) + { + _hMac.update(_V, 0, _V.length); + _hMac.doFinal(_V, 0); + + System.arraycopy(_V, 0, rv, m * _V.length, rv.length - (m * _V.length)); + } + + hmac_DRBG_Update(additionalInput); + + _reseedCounter++; + + System.arraycopy(rv, 0, output, 0, output.length); + + return numberOfBits; + } + + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ + public void reseed(byte[] additionalInput) + { + byte[] entropy = _entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(entropy, additionalInput); + + hmac_DRBG_Update(seedMaterial); + + _reseedCounter = 1; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java new file mode 100644 index 000000000..822724942 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java @@ -0,0 +1,269 @@ +package org.spongycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.prng.EntropySource; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Integers; + +/** + * A SP800-90A Hash DRBG. + */ +public class HashSP800DRBG + implements SP80090DRBG +{ + private final static byte[] ONE = { 0x01 }; + + private final static long RESEED_MAX = 1L << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + + private final static Hashtable seedlens = new Hashtable(); + + static + { + seedlens.put("SHA-1", Integers.valueOf(440)); + seedlens.put("SHA-224", Integers.valueOf(440)); + seedlens.put("SHA-256", Integers.valueOf(440)); + seedlens.put("SHA-512/256", Integers.valueOf(440)); + seedlens.put("SHA-512/224", Integers.valueOf(440)); + seedlens.put("SHA-384", Integers.valueOf(888)); + seedlens.put("SHA-512", Integers.valueOf(888)); + } + + private Digest _digest; + private byte[] _V; + private byte[] _C; + private long _reseedCounter; + private EntropySource _entropySource; + private int _securityStrength; + private int _seedLength; + + /** + * Construct a SP800-90A Hash DRBG. + *+ * Minimum entropy requirement is the security strength requested. + *
+ * @param digest source digest to use for DRB stream. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public HashSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) + { + if (securityStrength > Utils.getMaxSecurityStrength(digest)) + { + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } + + _digest = digest; + _entropySource = entropySource; + _securityStrength = securityStrength; + _seedLength = ((Integer)seedlens.get(digest.getAlgorithmName())).intValue(); + + // 1. seed_material = entropy_input || nonce || personalization_string. + // 2. seed = Hash_df (seed_material, seedlen). + // 3. V = seed. + // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte + // of zeros. + // 5. reseed_counter = 1. + // 6. Return V, C, and reseed_counter as the initial_working_state + + byte[] entropy = entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); + byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); + + _V = seed; + byte[] subV = new byte[_V.length + 1]; + System.arraycopy(_V, 0, subV, 1, _V.length); + _C = Utils.hash_df(_digest, subV, _seedLength); + + _reseedCounter = 1; + } + + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) + { + // 1. If reseed_counter > reseed_interval, then return an indication that a + // reseed is required. + // 2. If (additional_input != Null), then do + // 2.1 w = Hash (0x02 || V || additional_input). + // 2.2 V = (V + w) mod 2^seedlen + // . + // 3. (returned_bits) = Hashgen (requested_number_of_bits, V). + // 4. H = Hash (0x03 || V). + // 5. V = (V + H + C + reseed_counter) mod 2^seedlen + // . + // 6. reseed_counter = reseed_counter + 1. + // 7. Return SUCCESS, returned_bits, and the new values of V, C, and + // reseed_counter for the new_working_state. + int numberOfBits = output.length*8; + + if (numberOfBits > MAX_BITS_REQUEST) + { + throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); + } + + if (_reseedCounter > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) + { + reseed(additionalInput); + additionalInput = null; + } + + // 2. + if (additionalInput != null) + { + byte[] newInput = new byte[1 + _V.length + additionalInput.length]; + newInput[0] = 0x02; + System.arraycopy(_V, 0, newInput, 1, _V.length); + // TODO: inOff / inLength + System.arraycopy(additionalInput, 0, newInput, 1 + _V.length, additionalInput.length); + byte[] w = hash(newInput); + + addTo(_V, w); + } + + // 3. + byte[] rv = hashgen(_V, numberOfBits); + + // 4. + byte[] subH = new byte[_V.length + 1]; + System.arraycopy(_V, 0, subH, 1, _V.length); + subH[0] = 0x03; + + byte[] H = hash(subH); + + // 5. + addTo(_V, H); + addTo(_V, _C); + byte[] c = new byte[4]; + c[0] = (byte)(_reseedCounter >> 24); + c[1] = (byte)(_reseedCounter >> 16); + c[2] = (byte)(_reseedCounter >> 8); + c[3] = (byte)_reseedCounter; + + addTo(_V, c); + + _reseedCounter++; + + System.arraycopy(rv, 0, output, 0, output.length); + + return numberOfBits; + } + + // this will always add the shorter length byte array mathematically to the + // longer length byte array. + // be careful.... + private void addTo(byte[] longer, byte[] shorter) + { + int carry = 0; + for (int i=1;i <= shorter.length; i++) // warning + { + int res = (longer[longer.length-i] & 0xff) + (shorter[shorter.length-i] & 0xff) + carry; + carry = (res > 0xff) ? 1 : 0; + longer[longer.length-i] = (byte)res; + } + + for (int i=shorter.length+1;i <= longer.length; i++) // warning + { + int res = (longer[longer.length-i] & 0xff) + carry; + carry = (res > 0xff) ? 1 : 0; + longer[longer.length-i] = (byte)res; + } + } + + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ + public void reseed(byte[] additionalInput) + { + // 1. seed_material = 0x01 || V || entropy_input || additional_input. + // + // 2. seed = Hash_df (seed_material, seedlen). + // + // 3. V = seed. + // + // 4. C = Hash_df ((0x00 || V), seedlen). + // + // 5. reseed_counter = 1. + // + // 6. Return V, C, and reseed_counter for the new_working_state. + // + // Comment: Precede with a byte of all zeros. + byte[] entropy = _entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(ONE, _V, entropy, additionalInput); + byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); + + _V = seed; + byte[] subV = new byte[_V.length + 1]; + subV[0] = 0x00; + System.arraycopy(_V, 0, subV, 1, _V.length); + _C = Utils.hash_df(_digest, subV, _seedLength); + + _reseedCounter = 1; + } + + private byte[] hash(byte[] input) + { + _digest.update(input, 0, input.length); + byte[] hash = new byte[_digest.getDigestSize()]; + _digest.doFinal(hash, 0); + return hash; + } + + // 1. m = [requested_number_of_bits / outlen] + // 2. data = V. + // 3. W = the Null string. + // 4. For i = 1 to m + // 4.1 wi = Hash (data). + // 4.2 W = W || wi. + // 4.3 data = (data + 1) mod 2^seedlen + // . + // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W. + private byte[] hashgen(byte[] input, int lengthInBits) + { + int digestSize = _digest.getDigestSize(); + int m = (lengthInBits / 8) / digestSize; + + byte[] data = new byte[input.length]; + System.arraycopy(input, 0, data, 0, input.length); + + byte[] W = new byte[lengthInBits / 8]; + + byte[] dig; + for (int i = 0; i <= m; i++) + { + dig = hash(data); + + int bytesToCopy = ((W.length - i * dig.length) > dig.length) + ? dig.length + : (W.length - i * dig.length); + System.arraycopy(dig, 0, W, i * dig.length, bytesToCopy); + + addTo(data, ONE); + } + + return W; + } +} \ No newline at end of file diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java new file mode 100644 index 000000000..d7c58d33d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java @@ -0,0 +1,25 @@ +package org.spongycastle.crypto.prng.drbg; + +/** + * Interface to SP800-90A deterministic random bit generators. + */ +public interface SP80090DRBG +{ + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + int generate(byte[] output, byte[] additionalInput, boolean predictionResistant); + + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ + void reseed(byte[] additionalInput); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java new file mode 100644 index 000000000..bf2626d02 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java @@ -0,0 +1,103 @@ +package org.spongycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Mac; +import org.spongycastle.util.Integers; + +class Utils +{ + static final Hashtable maxSecurityStrengths = new Hashtable(); + + static + { + maxSecurityStrengths.put("SHA-1", Integers.valueOf(128)); + + maxSecurityStrengths.put("SHA-224", Integers.valueOf(192)); + maxSecurityStrengths.put("SHA-256", Integers.valueOf(256)); + maxSecurityStrengths.put("SHA-384", Integers.valueOf(256)); + maxSecurityStrengths.put("SHA-512", Integers.valueOf(256)); + + maxSecurityStrengths.put("SHA-512/224", Integers.valueOf(192)); + maxSecurityStrengths.put("SHA-512/256", Integers.valueOf(256)); + } + + static int getMaxSecurityStrength(Digest d) + { + return ((Integer)maxSecurityStrengths.get(d.getAlgorithmName())).intValue(); + } + + static int getMaxSecurityStrength(Mac m) + { + String name = m.getAlgorithmName(); + + return ((Integer)maxSecurityStrengths.get(name.substring(0, name.indexOf("/")))).intValue(); + } + + /** + * Used by both Dual EC and Hash. + */ + static byte[] hash_df(Digest digest, byte[] seedMaterial, int seedLength) + { + // 1. temp = the Null string. + // 2. . + // 3. counter = an 8-bit binary value representing the integer "1". + // 4. For i = 1 to len do + // Comment : In step 4.1, no_of_bits_to_return + // is used as a 32-bit string. + // 4.1 temp = temp || Hash (counter || no_of_bits_to_return || + // input_string). + // 4.2 counter = counter + 1. + // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp. + // 6. Return SUCCESS and requested_bits. + byte[] temp = new byte[(seedLength + 7) / 8]; + + int len = temp.length / digest.getDigestSize(); + int counter = 1; + + byte[] dig = new byte[digest.getDigestSize()]; + + for (int i = 0; i <= len; i++) + { + digest.update((byte)counter); + + digest.update((byte)(seedLength >> 24)); + digest.update((byte)(seedLength >> 16)); + digest.update((byte)(seedLength >> 8)); + digest.update((byte)seedLength); + + digest.update(seedMaterial, 0, seedMaterial.length); + + digest.doFinal(dig, 0); + + int bytesToCopy = ((temp.length - i * dig.length) > dig.length) + ? dig.length + : (temp.length - i * dig.length); + System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy); + + counter++; + } + + // do a left shift to get rid of excess bits. + if (seedLength % 8 != 0) + { + int shift = 8 - (seedLength % 8); + int carry = 0; + + for (int i = 0; i != temp.length; i++) + { + int b = temp[i] & 0xff; + temp[i] = (byte)((b >>> shift) | (carry << (8 - shift))); + carry = b; + } + } + + return temp; + } + + static boolean isTooLarge(byte[] bytes, int maxBytes) + { + return bytes != null && bytes.length > maxBytes; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSADigestSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSADigestSigner.java new file mode 100644 index 000000000..c15a81feb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSADigestSigner.java @@ -0,0 +1,163 @@ +package org.spongycastle.crypto.signers; + +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERInteger; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; + +public class DSADigestSigner + implements Signer +{ + private final Digest digest; + private final DSA dsaSigner; + private boolean forSigning; + + public DSADigestSigner( + DSA signer, + Digest digest) + { + this.digest = digest; + this.dsaSigner = signer; + } + + public void init( + boolean forSigning, + CipherParameters parameters) + { + this.forSigning = forSigning; + + AsymmetricKeyParameter k; + + if (parameters instanceof ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).getParameters(); + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.isPrivate()) + { + throw new IllegalArgumentException("Signing Requires Private Key."); + } + + if (!forSigning && k.isPrivate()) + { + throw new IllegalArgumentException("Verification Requires Public Key."); + } + + reset(); + + dsaSigner.init(forSigning, parameters); + } + + /** + * update the internal digest with the byte b + */ + public void update( + byte input) + { + digest.update(input); + } + + /** + * update the internal digest with the byte array in + */ + public void update( + byte[] input, + int inOff, + int length) + { + digest.update(input, inOff, length); + } + + /** + * Generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + public byte[] generateSignature() + { + if (!forSigning) + { + throw new IllegalStateException("DSADigestSigner not initialised for signature generation."); + } + + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + + BigInteger[] sig = dsaSigner.generateSignature(hash); + + try + { + return derEncode(sig[0], sig[1]); + } + catch (IOException e) + { + throw new IllegalStateException("unable to encode signature"); + } + } + + public boolean verifySignature( + byte[] signature) + { + if (forSigning) + { + throw new IllegalStateException("DSADigestSigner not initialised for verification"); + } + + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + + try + { + BigInteger[] sig = derDecode(signature); + return dsaSigner.verifySignature(hash, sig[0], sig[1]); + } + catch (IOException e) + { + return false; + } + } + + public void reset() + { + digest.reset(); + } + + private byte[] derEncode( + BigInteger r, + BigInteger s) + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new DERInteger(r)); + v.add(new DERInteger(s)); + + return new DERSequence(v).getEncoded(ASN1Encoding.DER); + } + + private BigInteger[] derDecode( + byte[] encoding) + throws IOException + { + ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding); + + return new BigInteger[] + { + ((DERInteger)s.getObjectAt(0)).getValue(), + ((DERInteger)s.getObjectAt(1)).getValue() + }; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSAKCalculator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSAKCalculator.java new file mode 100644 index 000000000..6c8e1dc8a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSAKCalculator.java @@ -0,0 +1,41 @@ +package org.spongycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * Interface define calculators of K values for DSA/ECDSA. + */ +public interface DSAKCalculator +{ + /** + * Return true if this calculator is deterministic, false otherwise. + * + * @return true if deterministic, otherwise false. + */ + boolean isDeterministic(); + + /** + * Non-deterministic initialiser. + * + * @param n the order of the DSA group. + * @param random a source of randomness. + */ + void init(BigInteger n, SecureRandom random); + + /** + * Deterministic initialiser. + * + * @param n the order of the DSA group. + * @param d the DSA private value. + * @param message the message being signed. + */ + void init(BigInteger n, BigInteger d, byte[] message); + + /** + * Return the next valid value of K. + * + * @return a K value. + */ + BigInteger nextK(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSASigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSASigner.java new file mode 100644 index 000000000..4d9b87842 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSASigner.java @@ -0,0 +1,160 @@ +package org.spongycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.params.DSAKeyParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.crypto.params.ParametersWithRandom; + +/** + * The Digital Signature Algorithm - as described in "Handbook of Applied + * Cryptography", pages 452 - 453. + */ +public class DSASigner + implements DSA +{ + private final DSAKCalculator kCalculator; + + private DSAKeyParameters key; + private SecureRandom random; + + /** + * Default configuration, random K values. + */ + public DSASigner() + { + this.kCalculator = new RandomDSAKCalculator(); + } + + /** + * Configuration with an alternate, possibly deterministic calculator of K. + * + * @param kCalculator a K value calculator. + */ + public DSASigner(DSAKCalculator kCalculator) + { + this.kCalculator = kCalculator; + } + + public void init( + boolean forSigning, + CipherParameters param) + { + if (forSigning) + { + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + this.random = rParam.getRandom(); + this.key = (DSAPrivateKeyParameters)rParam.getParameters(); + } + else + { + this.random = new SecureRandom(); + this.key = (DSAPrivateKeyParameters)param; + } + } + else + { + this.key = (DSAPublicKeyParameters)param; + } + } + + /** + * generate a signature for the given message using the key we were + * initialised with. For conventional DSA the message should be a SHA-1 + * hash of the message of interest. + * + * @param message the message that will be verified later. + */ + public BigInteger[] generateSignature( + byte[] message) + { + DSAParameters params = key.getParameters(); + BigInteger m = calculateE(params.getQ(), message); + + if (kCalculator.isDeterministic()) + { + kCalculator.init(params.getQ(), ((DSAPrivateKeyParameters)key).getX(), message); + } + else + { + kCalculator.init(params.getQ(), random); + } + + BigInteger k = kCalculator.nextK(); + + BigInteger r = params.getG().modPow(k, params.getP()).mod(params.getQ()); + + k = k.modInverse(params.getQ()).multiply( + m.add(((DSAPrivateKeyParameters)key).getX().multiply(r))); + + BigInteger s = k.mod(params.getQ()); + + BigInteger[] res = new BigInteger[2]; + + res[0] = r; + res[1] = s; + + return res; + } + + /** + * return true if the value r and s represent a DSA signature for + * the passed in message for standard DSA the message should be a + * SHA-1 hash of the real message to be verified. + */ + public boolean verifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + DSAParameters params = key.getParameters(); + BigInteger m = calculateE(params.getQ(), message); + BigInteger zero = BigInteger.valueOf(0); + + if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0) + { + return false; + } + + if (zero.compareTo(s) >= 0 || params.getQ().compareTo(s) <= 0) + { + return false; + } + + BigInteger w = s.modInverse(params.getQ()); + + BigInteger u1 = m.multiply(w).mod(params.getQ()); + BigInteger u2 = r.multiply(w).mod(params.getQ()); + + u1 = params.getG().modPow(u1, params.getP()); + u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, params.getP()); + + BigInteger v = u1.multiply(u2).mod(params.getP()).mod(params.getQ()); + + return v.equals(r); + } + + private BigInteger calculateE(BigInteger n, byte[] message) + { + if (n.bitLength() >= message.length * 8) + { + return new BigInteger(1, message); + } + else + { + byte[] trunc = new byte[n.bitLength() / 8]; + + System.arraycopy(message, 0, trunc, 0, trunc.length); + + return new BigInteger(1, trunc); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSTU4145Signer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSTU4145Signer.java new file mode 100644 index 000000000..4aba32e4f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/DSTU4145Signer.java @@ -0,0 +1,175 @@ +package org.spongycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.math.ec.ECAlgorithms; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECFieldElement; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.util.Arrays; + +/** + * DSTU 4145-2002 + *+ * National Ukrainian standard of digital signature based on elliptic curves (DSTU 4145-2002). + *
+ */ +public class DSTU4145Signer + implements DSA +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + + private ECKeyParameters key; + private SecureRandom random; + + public void init(boolean forSigning, CipherParameters param) + { + if (forSigning) + { + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + this.random = rParam.getRandom(); + param = rParam.getParameters(); + } + else + { + this.random = new SecureRandom(); + } + + this.key = (ECPrivateKeyParameters)param; + } + else + { + this.key = (ECPublicKeyParameters)param; + } + + } + + public BigInteger[] generateSignature(byte[] message) + { + ECDomainParameters parameters = key.getParameters(); + + ECCurve curve = parameters.getCurve(); + + ECFieldElement h = hash2FieldElement(curve, message); + if (h.isZero()) + { + h = curve.fromBigInteger(ONE); + } + + BigInteger n = parameters.getN(); + BigInteger e, r, s; + ECFieldElement Fe, y; + + do + { + do + { + do + { + e = generateRandomInteger(n, random); + Fe = parameters.getG().multiply(e).normalize().getAffineXCoord(); + } + while (Fe.isZero()); + + y = h.multiply(Fe); + r = fieldElement2Integer(n, y); + } + while (r.signum() == 0); + + s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(n); + } + while (s.signum() == 0); + + return new BigInteger[]{r, s}; + } + + public boolean verifySignature(byte[] message, BigInteger r, BigInteger s) + { + if (r.signum() <= 0 || s.signum() <= 0) + { + return false; + } + + ECDomainParameters parameters = key.getParameters(); + + BigInteger n = parameters.getN(); + if (r.compareTo(n) >= 0 || s.compareTo(n) >= 0) + { + return false; + } + + ECCurve curve = parameters.getCurve(); + + ECFieldElement h = hash2FieldElement(curve, message); + if (h.isZero()) + { + h = curve.fromBigInteger(ONE); + } + + ECPoint R = ECAlgorithms.sumOfTwoMultiplies(parameters.getG(), s, ((ECPublicKeyParameters)key).getQ(), r).normalize(); + + // components must be bogus. + if (R.isInfinity()) + { + return false; + } + + ECFieldElement y = h.multiply(R.getAffineXCoord()); + return fieldElement2Integer(n, y).compareTo(r) == 0; + } + + /** + * Generates random integer such, than its bit length is less than that of n + */ + private static BigInteger generateRandomInteger(BigInteger n, SecureRandom random) + { + return new BigInteger(n.bitLength() - 1, random); + } + + private static void reverseBytes(byte[] bytes) + { + byte tmp; + + for (int i=0; i+ * Note: the usual value for the salt length is the number of + * bytes in the hash function. + */ +public class PSSSigner + implements Signer +{ + static final public byte TRAILER_IMPLICIT = (byte)0xBC; + + private Digest contentDigest; + private Digest mgfDigest; + private AsymmetricBlockCipher cipher; + private SecureRandom random; + + private int hLen; + private int mgfhLen; + private int sLen; + private int emBits; + private byte[] salt; + private byte[] mDash; + private byte[] block; + private byte trailer; + + /** + * basic constructor + * + * @param cipher the asymmetric cipher to use. + * @param digest the digest to use. + * @param sLen the length of the salt to use (in bytes). + */ + public PSSSigner( + AsymmetricBlockCipher cipher, + Digest digest, + int sLen) + { + this(cipher, digest, sLen, TRAILER_IMPLICIT); + } + + public PSSSigner( + AsymmetricBlockCipher cipher, + Digest contentDigest, + Digest mgfDigest, + int sLen) + { + this(cipher, contentDigest, mgfDigest, sLen, TRAILER_IMPLICIT); + } + + public PSSSigner( + AsymmetricBlockCipher cipher, + Digest digest, + int sLen, + byte trailer) + { + this(cipher, digest, digest, sLen, trailer); + } + + public PSSSigner( + AsymmetricBlockCipher cipher, + Digest contentDigest, + Digest mgfDigest, + int sLen, + byte trailer) + { + this.cipher = cipher; + this.contentDigest = contentDigest; + this.mgfDigest = mgfDigest; + this.hLen = contentDigest.getDigestSize(); + this.mgfhLen = mgfDigest.getDigestSize(); + this.sLen = sLen; + this.salt = new byte[sLen]; + this.mDash = new byte[8 + sLen + hLen]; + this.trailer = trailer; + } + + public void init( + boolean forSigning, + CipherParameters param) + { + CipherParameters params; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom)param; + + params = p.getParameters(); + random = p.getRandom(); + } + else + { + params = param; + if (forSigning) + { + random = new SecureRandom(); + } + } + + cipher.init(forSigning, params); + + RSAKeyParameters kParam; + + if (params instanceof RSABlindingParameters) + { + kParam = ((RSABlindingParameters)params).getPublicKey(); + } + else + { + kParam = (RSAKeyParameters)params; + } + + emBits = kParam.getModulus().bitLength() - 1; + + if (emBits < (8 * hLen + 8 * sLen + 9)) + { + throw new IllegalArgumentException("key too small for specified hash and salt lengths"); + } + + block = new byte[(emBits + 7) / 8]; + + reset(); + } + + /** + * clear possible sensitive data + */ + private void clearBlock( + byte[] block) + { + for (int i = 0; i != block.length; i++) + { + block[i] = 0; + } + } + + /** + * update the internal digest with the byte b + */ + public void update( + byte b) + { + contentDigest.update(b); + } + + /** + * update the internal digest with the byte array in + */ + public void update( + byte[] in, + int off, + int len) + { + contentDigest.update(in, off, len); + } + + /** + * reset the internal state + */ + public void reset() + { + contentDigest.reset(); + } + + /** + * generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + public byte[] generateSignature() + throws CryptoException, DataLengthException + { + contentDigest.doFinal(mDash, mDash.length - hLen - sLen); + + if (sLen != 0) + { + random.nextBytes(salt); + + System.arraycopy(salt, 0, mDash, mDash.length - sLen, sLen); + } + + byte[] h = new byte[hLen]; + + contentDigest.update(mDash, 0, mDash.length); + + contentDigest.doFinal(h, 0); + + block[block.length - sLen - 1 - hLen - 1] = 0x01; + System.arraycopy(salt, 0, block, block.length - sLen - hLen - 1, sLen); + + byte[] dbMask = maskGeneratorFunction1(h, 0, h.length, block.length - hLen - 1); + for (int i = 0; i != dbMask.length; i++) + { + block[i] ^= dbMask[i]; + } + + block[0] &= (0xff >> ((block.length * 8) - emBits)); + + System.arraycopy(h, 0, block, block.length - hLen - 1, hLen); + + block[block.length - 1] = trailer; + + byte[] b = cipher.processBlock(block, 0, block.length); + + clearBlock(block); + + return b; + } + + /** + * return true if the internal state represents the signature described + * in the passed in array. + */ + public boolean verifySignature( + byte[] signature) + { + contentDigest.doFinal(mDash, mDash.length - hLen - sLen); + + try + { + byte[] b = cipher.processBlock(signature, 0, signature.length); + System.arraycopy(b, 0, block, block.length - b.length, b.length); + } + catch (Exception e) + { + return false; + } + + if (block[block.length - 1] != trailer) + { + clearBlock(block); + return false; + } + + byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - 1, hLen, block.length - hLen - 1); + + for (int i = 0; i != dbMask.length; i++) + { + block[i] ^= dbMask[i]; + } + + block[0] &= (0xff >> ((block.length * 8) - emBits)); + + for (int i = 0; i != block.length - hLen - sLen - 2; i++) + { + if (block[i] != 0) + { + clearBlock(block); + return false; + } + } + + if (block[block.length - hLen - sLen - 2] != 0x01) + { + clearBlock(block); + return false; + } + + System.arraycopy(block, block.length - sLen - hLen - 1, mDash, mDash.length - sLen, sLen); + + contentDigest.update(mDash, 0, mDash.length); + contentDigest.doFinal(mDash, mDash.length - hLen); + + for (int i = block.length - hLen - 1, j = mDash.length - hLen; + j != mDash.length; i++, j++) + { + if ((block[i] ^ mDash[j]) != 0) + { + clearBlock(mDash); + clearBlock(block); + return false; + } + } + + clearBlock(mDash); + clearBlock(block); + + return true; + } + + /** + * 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); + } + + /** + * mask generator function, as described in PKCS1v2. + */ + private byte[] maskGeneratorFunction1( + byte[] Z, + int zOff, + int zLen, + int length) + { + byte[] mask = new byte[length]; + byte[] hashBuf = new byte[mgfhLen]; + byte[] C = new byte[4]; + int counter = 0; + + mgfDigest.reset(); + + while (counter < (length / mgfhLen)) + { + ItoOSP(counter, C); + + mgfDigest.update(Z, zOff, zLen); + mgfDigest.update(C, 0, C.length); + mgfDigest.doFinal(hashBuf, 0); + + System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mgfhLen); + + counter++; + } + + if ((counter * mgfhLen) < length) + { + ItoOSP(counter, C); + + mgfDigest.update(Z, zOff, zLen); + mgfDigest.update(C, 0, C.length); + mgfDigest.doFinal(hashBuf, 0); + + System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mask.length - (counter * mgfhLen)); + } + + return mask; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/RSADigestSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/RSADigestSigner.java new file mode 100644 index 000000000..494b72f7a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/RSADigestSigner.java @@ -0,0 +1,238 @@ +package org.spongycastle.crypto.signers; + +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DigestInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.util.Arrays; + +public class RSADigestSigner + implements Signer +{ + private final AsymmetricBlockCipher rsaEngine = new PKCS1Encoding(new RSABlindedEngine()); + private final AlgorithmIdentifier algId; + private final Digest digest; + private boolean forSigning; + + private static final Hashtable oidMap = new Hashtable(); + + /* + * Load OID table. + */ + static + { + oidMap.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128); + oidMap.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160); + oidMap.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256); + + oidMap.put("SHA-1", X509ObjectIdentifiers.id_SHA1); + oidMap.put("SHA-224", NISTObjectIdentifiers.id_sha224); + oidMap.put("SHA-256", NISTObjectIdentifiers.id_sha256); + oidMap.put("SHA-384", NISTObjectIdentifiers.id_sha384); + oidMap.put("SHA-512", NISTObjectIdentifiers.id_sha512); + + oidMap.put("MD2", PKCSObjectIdentifiers.md2); + oidMap.put("MD4", PKCSObjectIdentifiers.md4); + oidMap.put("MD5", PKCSObjectIdentifiers.md5); + } + + public RSADigestSigner( + Digest digest) + { + this(digest, (ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName())); + } + + public RSADigestSigner( + Digest digest, + ASN1ObjectIdentifier digestOid) + { + this.digest = digest; + this.algId = new AlgorithmIdentifier(digestOid, DERNull.INSTANCE); + } + + /** + * @deprecated + */ + public String getAlgorithmName() + { + return digest.getAlgorithmName() + "withRSA"; + } + + /** + * initialise the signer for signing or verification. + * + * @param forSigning + * true if for signing, false otherwise + * @param parameters + * necessary parameters. + */ + public void init( + boolean forSigning, + CipherParameters parameters) + { + this.forSigning = forSigning; + AsymmetricKeyParameter k; + + if (parameters instanceof ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).getParameters(); + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.isPrivate()) + { + throw new IllegalArgumentException("signing requires private key"); + } + + if (!forSigning && k.isPrivate()) + { + throw new IllegalArgumentException("verification requires public key"); + } + + reset(); + + rsaEngine.init(forSigning, parameters); + } + + /** + * update the internal digest with the byte b + */ + public void update( + byte input) + { + digest.update(input); + } + + /** + * update the internal digest with the byte array in + */ + public void update( + byte[] input, + int inOff, + int length) + { + digest.update(input, inOff, length); + } + + /** + * Generate a signature for the message we've been loaded with using the key + * we were initialised with. + */ + public byte[] generateSignature() + throws CryptoException, DataLengthException + { + if (!forSigning) + { + throw new IllegalStateException("RSADigestSigner not initialised for signature generation."); + } + + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + + try + { + byte[] data = derEncode(hash); + return rsaEngine.processBlock(data, 0, data.length); + } + catch (IOException e) + { + throw new CryptoException("unable to encode signature: " + e.getMessage(), e); + } + } + + /** + * return true if the internal state represents the signature described in + * the passed in array. + */ + public boolean verifySignature( + byte[] signature) + { + if (forSigning) + { + throw new IllegalStateException("RSADigestSigner not initialised for verification"); + } + + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + byte[] sig; + byte[] expected; + + try + { + sig = rsaEngine.processBlock(signature, 0, signature.length); + expected = derEncode(hash); + } + catch (Exception e) + { + return false; + } + + if (sig.length == expected.length) + { + return Arrays.constantTimeAreEqual(sig, expected); + } + else if (sig.length == expected.length - 2) // NULL left out + { + int sigOffset = sig.length - hash.length - 2; + int expectedOffset = expected.length - hash.length - 2; + + expected[1] -= 2; // adjust lengths + expected[3] -= 2; + + int nonEqual = 0; + + for (int i = 0; i < hash.length; i++) + { + nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]); + } + + for (int i = 0; i < sigOffset; i++) + { + nonEqual |= (sig[i] ^ expected[i]); // check header less NULL + } + + return nonEqual == 0; + } + else + { + return false; + } + } + + public void reset() + { + digest.reset(); + } + + private byte[] derEncode( + byte[] hash) + throws IOException + { + DigestInfo dInfo = new DigestInfo(algId, hash); + + return dInfo.getEncoded(ASN1Encoding.DER); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/RandomDSAKCalculator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/RandomDSAKCalculator.java new file mode 100644 index 000000000..fb8e3ae71 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/signers/RandomDSAKCalculator.java @@ -0,0 +1,43 @@ +package org.spongycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +class RandomDSAKCalculator + implements DSAKCalculator +{ + private static final BigInteger ZERO = BigInteger.valueOf(0); + + private BigInteger q; + private SecureRandom random; + + public boolean isDeterministic() + { + return false; + } + + public void init(BigInteger n, SecureRandom random) + { + this.q = n; + this.random = random; + } + + public void init(BigInteger n, BigInteger d, byte[] message) + { + throw new IllegalStateException("Operation not supported"); + } + + public BigInteger nextK() + { + int qBitLength = q.bitLength(); + + BigInteger k; + do + { + k = new BigInteger(qBitLength, random); + } + while (k.equals(ZERO) || k.compareTo(q) >= 0); + + return k; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsAgreementCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsAgreementCredentials.java new file mode 100644 index 000000000..188dfafe4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsAgreementCredentials.java @@ -0,0 +1,7 @@ +package org.spongycastle.crypto.tls; + +public abstract class AbstractTlsAgreementCredentials + extends AbstractTlsCredentials + implements TlsAgreementCredentials +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsCipherFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsCipherFactory.java new file mode 100644 index 000000000..755c7b7eb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsCipherFactory.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public class AbstractTlsCipherFactory + implements TlsCipherFactory +{ + public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) + throws IOException + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsClient.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsClient.java new file mode 100644 index 000000000..81c399ffe --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsClient.java @@ -0,0 +1,235 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; + +public abstract class AbstractTlsClient + extends AbstractTlsPeer + implements TlsClient +{ + protected TlsCipherFactory cipherFactory; + + protected TlsClientContext context; + + protected Vector supportedSignatureAlgorithms; + protected int[] namedCurves; + protected short[] clientECPointFormats, serverECPointFormats; + + protected int selectedCipherSuite; + protected short selectedCompressionMethod; + + public AbstractTlsClient() + { + this(new DefaultTlsCipherFactory()); + } + + public AbstractTlsClient(TlsCipherFactory cipherFactory) + { + this.cipherFactory = cipherFactory; + } + + public void init(TlsClientContext context) + { + this.context = context; + } + + public TlsSession getSessionToResume() + { + return null; + } + + /** + * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value + * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest + * version number supported by the client, and the value of ClientHello.client_version. No + * single value will guarantee interoperability with all old servers, but this is a complex + * topic beyond the scope of this document." + */ + public ProtocolVersion getClientHelloRecordLayerVersion() + { + // "{03,00}" + // return ProtocolVersion.SSLv3; + + // "the lowest version number supported by the client" + // return getMinimumVersion(); + + // "the value of ClientHello.client_version" + return getClientVersion(); + } + + public ProtocolVersion getClientVersion() + { + return ProtocolVersion.TLSv12; + } + + public Hashtable getClientExtensions() + throws IOException + { + Hashtable clientExtensions = null; + + ProtocolVersion clientVersion = context.getClientVersion(); + + /* + * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2. + * Clients MUST NOT offer it if they are offering prior versions. + */ + if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + // TODO Provide a way for the user to specify the acceptable hash/signature algorithms. + + short[] hashAlgorithms = new short[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256, + HashAlgorithm.sha224, HashAlgorithm.sha1 }; + + // TODO Sort out ECDSA signatures and add them as the preferred option here + short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa }; + + this.supportedSignatureAlgorithms = new Vector(); + for (int i = 0; i < hashAlgorithms.length; ++i) + { + for (int j = 0; j < signatureAlgorithms.length; ++j) + { + this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[i], + signatureAlgorithms[j])); + } + } + + /* + * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1. + */ + this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, + SignatureAlgorithm.dsa)); + + clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions); + + TlsUtils.addSignatureAlgorithmsExtension(clientExtensions, supportedSignatureAlgorithms); + } + + if (TlsECCUtils.containsECCCipherSuites(getCipherSuites())) + { + /* + * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message + * appends these extensions (along with any others), enumerating the curves it supports + * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic + * Curves Extension and the Supported Point Formats Extension. + */ + /* + * TODO Could just add all the curves since we support them all, but users may not want + * to use unnecessarily large fields. Need configuration options. + */ + this.namedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 }; + this.clientECPointFormats = new short[]{ ECPointFormat.uncompressed, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; + + clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions); + + TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves); + TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats); + } + + return clientExtensions; + } + + public ProtocolVersion getMinimumVersion() + { + return ProtocolVersion.TLSv10; + } + + public void notifyServerVersion(ProtocolVersion serverVersion) + throws IOException + { + if (!getMinimumVersion().isEqualOrEarlierVersionOf(serverVersion)) + { + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + } + + public short[] getCompressionMethods() + { + return new short[]{CompressionMethod._null}; + } + + public void notifySessionID(byte[] sessionID) + { + // Currently ignored + } + + public void notifySelectedCipherSuite(int selectedCipherSuite) + { + this.selectedCipherSuite = selectedCipherSuite; + } + + public void notifySelectedCompressionMethod(short selectedCompressionMethod) + { + this.selectedCompressionMethod = selectedCompressionMethod; + } + + public void processServerExtensions(Hashtable serverExtensions) + throws IOException + { + /* + * TlsProtocol implementation validates that any server extensions received correspond to + * client extensions sent. By default, we don't send any, and this method is not called. + */ + if (serverExtensions != null) + { + /* + * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension. + */ + if (serverExtensions.containsKey(TlsUtils.EXT_signature_algorithms)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions); + if (namedCurves != null) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions); + if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public void processServerSupplementalData(Vector serverSupplementalData) + throws IOException + { + if (serverSupplementalData != null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public Vector getClientSupplementalData() + throws IOException + { + return null; + } + + public TlsCompression getCompression() + throws IOException + { + switch (selectedCompressionMethod) + { + case CompressionMethod._null: + return new TlsNullCompression(); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected compression method was in the list of client-offered compression + * methods, so if we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public void notifyNewSessionTicket(NewSessionTicket newSessionTicket) + throws IOException + { + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsContext.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsContext.java new file mode 100644 index 000000000..ecf7fa984 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsContext.java @@ -0,0 +1,110 @@ +package org.spongycastle.crypto.tls; + +import java.security.SecureRandom; + +abstract class AbstractTlsContext + implements TlsContext +{ + private SecureRandom secureRandom; + private SecurityParameters securityParameters; + + private ProtocolVersion clientVersion = null; + private ProtocolVersion serverVersion = null; + private TlsSession session = null; + private Object userObject = null; + + AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters) + { + this.secureRandom = secureRandom; + this.securityParameters = securityParameters; + } + + public SecureRandom getSecureRandom() + { + return secureRandom; + } + + public SecurityParameters getSecurityParameters() + { + return securityParameters; + } + + public ProtocolVersion getClientVersion() + { + return clientVersion; + } + + void setClientVersion(ProtocolVersion clientVersion) + { + this.clientVersion = clientVersion; + } + + public ProtocolVersion getServerVersion() + { + return serverVersion; + } + + void setServerVersion(ProtocolVersion serverVersion) + { + this.serverVersion = serverVersion; + } + + public TlsSession getResumableSession() + { + return session; + } + + void setResumableSession(TlsSession session) + { + this.session = session; + } + + public Object getUserObject() + { + return userObject; + } + + public void setUserObject(Object userObject) + { + this.userObject = userObject; + } + + public byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length) + { + if (context_value != null && !TlsUtils.isValidUint16(context_value.length)) + { + throw new IllegalArgumentException("'context_value' must have length less than 2^16 (or be null)"); + } + + SecurityParameters sp = getSecurityParameters(); + byte[] cr = sp.getClientRandom(), sr = sp.getServerRandom(); + + int seedLength = cr.length + sr.length; + if (context_value != null) + { + seedLength += (2 + context_value.length); + } + + byte[] seed = new byte[seedLength]; + int seedPos = 0; + + System.arraycopy(cr, 0, seed, seedPos, cr.length); + seedPos += cr.length; + System.arraycopy(sr, 0, seed, seedPos, sr.length); + seedPos += sr.length; + if (context_value != null) + { + TlsUtils.writeUint16(context_value.length, seed, seedPos); + seedPos += 2; + System.arraycopy(context_value, 0, seed, seedPos, context_value.length); + seedPos += context_value.length; + } + + if (seedPos != seedLength) + { + throw new IllegalStateException("error in calculation of seed for export"); + } + + return TlsUtils.PRF(this, sp.getMasterSecret(), asciiLabel, seed, length); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsCredentials.java new file mode 100644 index 000000000..ab6cda7ea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsCredentials.java @@ -0,0 +1,6 @@ +package org.spongycastle.crypto.tls; + +public abstract class AbstractTlsCredentials + implements TlsCredentials +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsEncryptionCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsEncryptionCredentials.java new file mode 100644 index 000000000..8eb0147e0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsEncryptionCredentials.java @@ -0,0 +1,7 @@ +package org.spongycastle.crypto.tls; + +public abstract class AbstractTlsEncryptionCredentials + extends AbstractTlsCredentials + implements TlsEncryptionCredentials +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsKeyExchange.java new file mode 100644 index 000000000..4920007d8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsKeyExchange.java @@ -0,0 +1,166 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +public abstract class AbstractTlsKeyExchange + implements TlsKeyExchange +{ + protected int keyExchange; + protected Vector supportedSignatureAlgorithms; + + protected TlsContext context; + + protected AbstractTlsKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms) + { + this.keyExchange = keyExchange; + this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; + } + + public void init(TlsContext context) + { + this.context = context; + + ProtocolVersion clientVersion = context.getClientVersion(); + + if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + /* + * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension, + * the server MUST do the following: + * + * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK, + * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}. + * + * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if + * the client had sent the value {sha1,dsa}. + * + * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA), + * behave as if the client had sent value {sha1,ecdsa}. + */ + if (this.supportedSignatureAlgorithms == null) + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.DH_DSS: + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.SRP_DSS: + { + this.supportedSignatureAlgorithms = TlsUtils.getDefaultDSSSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.ECDH_ECDSA: + case KeyExchangeAlgorithm.ECDHE_ECDSA: + { + this.supportedSignatureAlgorithms = TlsUtils.getDefaultECDSASignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.DH_RSA: + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.ECDH_RSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + case KeyExchangeAlgorithm.RSA: + case KeyExchangeAlgorithm.RSA_PSK: + case KeyExchangeAlgorithm.SRP_RSA: + { + this.supportedSignatureAlgorithms = TlsUtils.getDefaultRSASignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.SRP: + break; + + default: + throw new IllegalStateException("unsupported key exchange algorithm"); + } + } + + } + else if (this.supportedSignatureAlgorithms != null) + { + throw new IllegalStateException("supported_signature_algorithms not allowed for " + clientVersion); + } + } + + public void processServerCertificate(Certificate serverCertificate) + throws IOException + { + if (supportedSignatureAlgorithms == null) + { + /* + * TODO RFC 2264 7.4.2. Unless otherwise specified, the signing algorithm for the + * certificate must be the same as the algorithm for the certificate key. + */ + } + else + { + /* + * TODO RFC 5264 7.4.2. If the client provided a "signature_algorithms" extension, then + * all certificates provided by the server MUST be signed by a hash/signature algorithm + * pair that appears in that extension. + */ + } + } + + public void processServerCredentials(TlsCredentials serverCredentials) + throws IOException + { + processServerCertificate(serverCredentials.getCertificate()); + } + + public boolean requiresServerKeyExchange() + { + return false; + } + + public byte[] generateServerKeyExchange() + throws IOException + { + if (requiresServerKeyExchange()) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + return null; + } + + public void skipServerKeyExchange() + throws IOException + { + if (requiresServerKeyExchange()) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public void processServerKeyExchange(InputStream input) + throws IOException + { + if (!requiresServerKeyExchange()) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public void skipClientCredentials() + throws IOException + { + } + + public void processClientCertificate(Certificate clientCertificate) + throws IOException + { + } + + public void processClientKeyExchange(InputStream input) + throws IOException + { + // Key exchange implementation MUST support client key exchange + throw new TlsFatalAlert(AlertDescription.internal_error); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsPeer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsPeer.java new file mode 100644 index 000000000..238e49336 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsPeer.java @@ -0,0 +1,31 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public abstract class AbstractTlsPeer + implements TlsPeer +{ + public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException + { + if (!secureRenegotiation) + { + /* + * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead + * of continuing; see Section 4.1/4.3 for discussion. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause) + { + } + + public void notifyAlertReceived(short alertLevel, short alertDescription) + { + } + + public void notifyHandshakeComplete() throws IOException + { + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsServer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsServer.java new file mode 100644 index 000000000..30152dd0c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsServer.java @@ -0,0 +1,314 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.util.Arrays; + +public abstract class AbstractTlsServer + extends AbstractTlsPeer + implements TlsServer +{ + protected TlsCipherFactory cipherFactory; + + protected TlsServerContext context; + + protected ProtocolVersion clientVersion; + protected int[] offeredCipherSuites; + protected short[] offeredCompressionMethods; + protected Hashtable clientExtensions; + + protected short maxFragmentLengthOffered; + protected boolean truncatedHMacOffered; + protected Vector supportedSignatureAlgorithms; + protected boolean eccCipherSuitesOffered; + protected int[] namedCurves; + protected short[] clientECPointFormats, serverECPointFormats; + + protected ProtocolVersion serverVersion; + protected int selectedCipherSuite; + protected short selectedCompressionMethod; + protected Hashtable serverExtensions; + + public AbstractTlsServer() + { + this(new DefaultTlsCipherFactory()); + } + + public AbstractTlsServer(TlsCipherFactory cipherFactory) + { + this.cipherFactory = cipherFactory; + } + + protected boolean allowTruncatedHMac() + { + return false; + } + + protected Hashtable checkServerExtensions() + { + return this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.serverExtensions); + } + + protected abstract int[] getCipherSuites(); + + protected short[] getCompressionMethods() + { + return new short[]{CompressionMethod._null}; + } + + protected ProtocolVersion getMaximumVersion() + { + return ProtocolVersion.TLSv11; + } + + protected ProtocolVersion getMinimumVersion() + { + return ProtocolVersion.TLSv10; + } + + protected boolean supportsClientECCCapabilities(int[] namedCurves, short[] ecPointFormats) + { + // NOTE: BC supports all the current set of point formats so we don't check them here + + if (namedCurves == null) + { + /* + * RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these + * extensions. In this case, the server is free to choose any one of the elliptic curves + * or point formats [...]. + */ + return TlsECCUtils.hasAnySupportedNamedCurves(); + } + + for (int i = 0; i < namedCurves.length; ++i) + { + int namedCurve = namedCurves[i]; + if (!NamedCurve.refersToASpecificNamedCurve(namedCurve) || TlsECCUtils.isSupportedNamedCurve(namedCurve)) + { + return true; + } + } + + return false; + } + + public void init(TlsServerContext context) + { + this.context = context; + } + + public void notifyClientVersion(ProtocolVersion clientVersion) + throws IOException + { + this.clientVersion = clientVersion; + } + + public void notifyOfferedCipherSuites(int[] offeredCipherSuites) + throws IOException + { + this.offeredCipherSuites = offeredCipherSuites; + this.eccCipherSuitesOffered = TlsECCUtils.containsECCCipherSuites(this.offeredCipherSuites); + } + + public void notifyOfferedCompressionMethods(short[] offeredCompressionMethods) + throws IOException + { + this.offeredCompressionMethods = offeredCompressionMethods; + } + + public void processClientExtensions(Hashtable clientExtensions) + throws IOException + { + this.clientExtensions = clientExtensions; + + if (clientExtensions != null) + { + this.maxFragmentLengthOffered = TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions); + this.truncatedHMacOffered = TlsExtensionsUtils.hasTruncatedHMacExtension(clientExtensions); + + this.supportedSignatureAlgorithms = TlsUtils.getSignatureAlgorithmsExtension(clientExtensions); + if (this.supportedSignatureAlgorithms != null) + { + /* + * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior + * to 1.2. Clients MUST NOT offer it if they are offering prior versions. + */ + if (!TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + this.namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(clientExtensions); + this.clientECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(clientExtensions); + } + + /* + * RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it + * does not propose any ECC cipher suites. + */ + if (!this.eccCipherSuitesOffered && (this.namedCurves != null || this.clientECPointFormats != null)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public ProtocolVersion getServerVersion() + throws IOException + { + if (getMinimumVersion().isEqualOrEarlierVersionOf(clientVersion)) + { + ProtocolVersion maximumVersion = getMaximumVersion(); + if (clientVersion.isEqualOrEarlierVersionOf(maximumVersion)) + { + return serverVersion = clientVersion; + } + if (clientVersion.isLaterVersionOf(maximumVersion)) + { + return serverVersion = maximumVersion; + } + } + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + + public int getSelectedCipherSuite() + throws IOException + { + /* + * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate + * cipher suites against the "signature_algorithms" extension before selecting them. This is + * somewhat inelegant but is a compromise designed to minimize changes to the original + * cipher suite design. + */ + + /* + * RFC 4429 5.1. A server that receives a ClientHello containing one or both of these + * extensions MUST use the client's enumerated capabilities to guide its selection of an + * appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only + * if the server can successfully complete the handshake while using the curves and point + * formats supported by the client [...]. + */ + boolean eccCipherSuitesEnabled = supportsClientECCCapabilities(this.namedCurves, this.clientECPointFormats); + + int[] cipherSuites = getCipherSuites(); + for (int i = 0; i < cipherSuites.length; ++i) + { + int cipherSuite = cipherSuites[i]; + + // TODO Certain cipher suites may only be available starting at a particular version + if (Arrays.contains(this.offeredCipherSuites, cipherSuite) + && (eccCipherSuitesEnabled || !TlsECCUtils.isECCCipherSuite(cipherSuite))) + { + return this.selectedCipherSuite = cipherSuite; + } + } + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + public short getSelectedCompressionMethod() + throws IOException + { + short[] compressionMethods = getCompressionMethods(); + for (int i = 0; i < compressionMethods.length; ++i) + { + if (Arrays.contains(offeredCompressionMethods, compressionMethods[i])) + { + return this.selectedCompressionMethod = compressionMethods[i]; + } + } + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + // Hashtable is (Integer -> byte[]) + public Hashtable getServerExtensions() + throws IOException + { + if (this.maxFragmentLengthOffered >= 0) + { + TlsExtensionsUtils.addMaxFragmentLengthExtension(checkServerExtensions(), this.maxFragmentLengthOffered); + } + + if (this.truncatedHMacOffered && allowTruncatedHMac()) + { + TlsExtensionsUtils.addTruncatedHMacExtension(checkServerExtensions()); + } + + if (this.clientECPointFormats != null && TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite)) + { + /* + * RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello + * message including a Supported Point Formats Extension appends this extension (along + * with others) to its ServerHello message, enumerating the point formats it can parse. + */ + this.serverECPointFormats = new short[]{ ECPointFormat.ansiX962_compressed_char2, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed }; + + TlsECCUtils.addSupportedPointFormatsExtension(checkServerExtensions(), serverECPointFormats); + } + + return serverExtensions; + } + + public Vector getServerSupplementalData() + throws IOException + { + return null; + } + + public CertificateStatus getCertificateStatus() + throws IOException + { + return null; + } + + public CertificateRequest getCertificateRequest() + throws IOException + { + return null; + } + + public void processClientSupplementalData(Vector clientSupplementalData) + throws IOException + { + if (clientSupplementalData != null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public void notifyClientCertificate(Certificate clientCertificate) + throws IOException + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public TlsCompression getCompression() + throws IOException + { + switch (selectedCompressionMethod) + { + case CompressionMethod._null: + return new TlsNullCompression(); + + default: + /* + * Note: internal error here; we selected the compression method, so if we now can't + * produce an implementation, we shouldn't have chosen it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public NewSessionTicket getNewSessionTicket() + throws IOException + { + /* + * RFC 5077 3.3. If the server determines that it does not want to include a ticket after it + * has included the SessionTicket extension in the ServerHello, then it sends a zero-length + * ticket in the NewSessionTicket handshake message. + */ + return new NewSessionTicket(0L, TlsUtils.EMPTY_BYTES); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsSigner.java new file mode 100644 index 000000000..cc88f5a21 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsSigner.java @@ -0,0 +1,38 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +public abstract class AbstractTlsSigner + implements TlsSigner +{ + protected TlsContext context; + + public void init(TlsContext context) + { + this.context = context; + } + + public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + throws CryptoException + { + return generateRawSignature(null, privateKey, md5AndSha1); + } + + public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + throws CryptoException + { + return verifyRawSignature(null, sigBytes, publicKey, md5AndSha1); + } + + public Signer createSigner(AsymmetricKeyParameter privateKey) + { + return createSigner(null, privateKey); + } + + public Signer createVerifyer(AsymmetricKeyParameter publicKey) + { + return createVerifyer(null, publicKey); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsSignerCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsSignerCredentials.java new file mode 100644 index 000000000..d10e64998 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AbstractTlsSignerCredentials.java @@ -0,0 +1,11 @@ +package org.spongycastle.crypto.tls; + +public abstract class AbstractTlsSignerCredentials + extends AbstractTlsCredentials + implements TlsSignerCredentials +{ + public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm() + { + throw new IllegalStateException("TlsSignerCredentials implementation does not support (D)TLS 1.2+"); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlertDescription.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlertDescription.java new file mode 100644 index 000000000..275cb16ef --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlertDescription.java @@ -0,0 +1,216 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 5246 7.2. + */ +public class AlertDescription +{ + /** + * This message notifies the recipient that the sender will not send any more messages on this + * connection. Note that as of TLS 1.1, failure to properly close a connection no longer + * requires that a session not be resumed. This is a change from TLS 1.0 ("The session becomes + * unresumable if any connection is terminated without proper close_notify messages with level + * equal to warning.") to conform with widespread implementation practice. + */ + public static final short close_notify = 0; + + /** + * An inappropriate message was received. This alert is always fatal and should never be + * observed in communication between proper implementations. + */ + public static final short unexpected_message = 10; + + /** + * This alert is returned if a record is received with an incorrect MAC. This alert also MUST be + * returned if an alert is sent because a TLSCiphertext decrypted in an invalid way: either it + * wasn't an even multiple of the block length, or its padding values, when checked, weren't + * correct. This message is always fatal and should never be observed in communication between + * proper implementations (except when messages were corrupted in the network). + */ + public static final short bad_record_mac = 20; + + /** + * This alert was used in some earlier versions of TLS, and may have permitted certain attacks + * against the CBC mode [CBCATT]. It MUST NOT be sent by compliant implementations. + */ + public static final short decryption_failed = 21; + + /** + * A TLSCiphertext record was received that had a length more than 2^14+2048 bytes, or a record + * decrypted to a TLSCompressed record with more than 2^14+1024 bytes. This message is always + * fatal and should never be observed in communication between proper implementations (except + * when messages were corrupted in the network). + */ + public static final short record_overflow = 22; + + /** + * The decompression function received improper input (e.g., data that would expand to excessive + * length). This message is always fatal and should never be observed in communication between + * proper implementations. + */ + public static final short decompression_failure = 30; + + /** + * Reception of a handshake_failure alert message indicates that the sender was unable to + * negotiate an acceptable set of security parameters given the options available. This is a + * fatal error. + */ + public static final short handshake_failure = 40; + + /** + * This alert was used in SSLv3 but not any version of TLS. It MUST NOT be sent by compliant + * implementations. + */ + public static final short no_certificate = 41; + + /** + * A certificate was corrupt, contained signatures that did not verify correctly, etc. + */ + public static final short bad_certificate = 42; + + /** + * A certificate was of an unsupported type. + */ + public static final short unsupported_certificate = 43; + + /** + * A certificate was revoked by its signer. + */ + public static final short certificate_revoked = 44; + + /** + * A certificate has expired or is not currently valid. + */ + public static final short certificate_expired = 45; + + /** + * Some other (unspecified) issue arose in processing the certificate, rendering it + * unacceptable. + */ + public static final short certificate_unknown = 46; + + /** + * A field in the handshake was out of range or inconsistent with other fields. This message is + * always fatal. + */ + public static final short illegal_parameter = 47; + + /** + * A valid certificate chain or partial chain was received, but the certificate was not accepted + * because the CA certificate could not be located or couldn't be matched with a known, trusted + * CA. This message is always fatal. + */ + public static final short unknown_ca = 48; + + /** + * A valid certificate was received, but when access control was applied, the sender decided not + * to proceed with negotiation. This message is always fatal. + */ + public static final short access_denied = 49; + + /** + * A message could not be decoded because some field was out of the specified range or the + * length of the message was incorrect. This message is always fatal and should never be + * observed in communication between proper implementations (except when messages were corrupted + * in the network). + */ + public static final short decode_error = 50; + + /** + * A handshake cryptographic operation failed, including being unable to correctly verify a + * signature or validate a Finished message. This message is always fatal. + */ + public static final short decrypt_error = 51; + + /** + * This alert was used in some earlier versions of TLS. It MUST NOT be sent by compliant + * implementations. + */ + public static final short export_restriction = 60; + + /** + * The protocol version the client has attempted to negotiate is recognized but not supported. + * (For example, old protocol versions might be avoided for security reasons.) This message is + * always fatal. + */ + public static final short protocol_version = 70; + + /** + * Returned instead of handshake_failure when a negotiation has failed specifically because the + * server requires ciphers more secure than those supported by the client. This message is + * always fatal. + */ + public static final short insufficient_security = 71; + + /** + * An internal error unrelated to the peer or the correctness of the protocol (such as a memory + * allocation failure) makes it impossible to continue. This message is always fatal. + */ + public static final short internal_error = 80; + + /** + * This handshake is being canceled for some reason unrelated to a protocol failure. If the user + * cancels an operation after the handshake is complete, just closing the connection by sending + * a close_notify is more appropriate. This alert should be followed by a close_notify. This + * message is generally a warning. + */ + public static final short user_canceled = 90; + + /** + * Sent by the client in response to a hello request or by the server in response to a client + * hello after initial handshaking. Either of these would normally lead to renegotiation; when + * that is not appropriate, the recipient should respond with this alert. At that point, the + * original requester can decide whether to proceed with the connection. One case where this + * would be appropriate is where a server has spawned a process to satisfy a request; the + * process might receive security parameters (key length, authentication, etc.) at startup, and + * it might be difficult to communicate changes to these parameters after that point. This + * message is always a warning. + */ + public static final short no_renegotiation = 100; + + /** + * Sent by clients that receive an extended server hello containing an extension that they did + * not put in the corresponding client hello. This message is always fatal. + */ + public static final short unsupported_extension = 110; + + /* + * RFC 3546 + */ + + /** + * This alert is sent by servers who are unable to retrieve a certificate chain from the URL + * supplied by the client (see Section 3.3). This message MAY be fatal - for example if client + * authentication is required by the server for the handshake to continue and the server is + * unable to retrieve the certificate chain, it may send a fatal alert. + */ + public static final short certificate_unobtainable = 111; + + /** + * This alert is sent by servers that receive a server_name extension request, but do not + * recognize the server name. This message MAY be fatal. + */ + public static final short unrecognized_name = 112; + + /** + * This alert is sent by clients that receive an invalid certificate status response (see + * Section 3.6). This message is always fatal. + */ + public static final short bad_certificate_status_response = 113; + + /** + * This alert is sent by servers when a certificate hash does not match a client provided + * certificate_hash. This message is always fatal. + */ + public static final short bad_certificate_hash_value = 114; + + /* + * RFC 4279 + */ + + /** + * If the server does not recognize the PSK identity, it MAY respond with an + * "unknown_psk_identity" alert message. + */ + public static final short unknown_psk_identity = 115; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlertLevel.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlertLevel.java new file mode 100644 index 000000000..3392c2a66 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlertLevel.java @@ -0,0 +1,10 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 7.2 + */ +public class AlertLevel +{ + public static final short warning = 1; + public static final short fatal = 2; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlwaysValidVerifyer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlwaysValidVerifyer.java new file mode 100644 index 000000000..de13e8503 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/AlwaysValidVerifyer.java @@ -0,0 +1,24 @@ +package org.spongycastle.crypto.tls; + +/** + * A certificate verifyer, that will always return true. + *
+ *+ * DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING. + *+ * + * @deprecated Perform certificate verification in TlsAuthentication implementation + */ +public class AlwaysValidVerifyer + implements CertificateVerifyer +{ + /** + * Return true. + * + * @see org.spongycastle.crypto.tls.CertificateVerifyer#isValid(org.spongycastle.asn1.x509.Certificate[]) + */ + public boolean isValid(org.spongycastle.asn1.x509.Certificate[] certs) + { + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/BulkCipherAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/BulkCipherAlgorithm.java new file mode 100644 index 000000000..c7382fe81 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/BulkCipherAlgorithm.java @@ -0,0 +1,24 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class BulkCipherAlgorithm +{ + + public static final int _null = 0; + public static final int rc4 = 1; + public static final int rc2 = 2; + public static final int des = 3; + public static final int _3des = 4; + public static final int des40 = 5; + + /* + * RFC 4346 + */ + public static final int aes = 6; + public static final int idea = 7; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ByteQueue.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ByteQueue.java new file mode 100644 index 000000000..d3cf8bf5d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ByteQueue.java @@ -0,0 +1,153 @@ +package org.spongycastle.crypto.tls; + +/** + * A queue for bytes. This file could be more optimized. + */ +public class ByteQueue +{ + /** + * @return The smallest number which can be written as 2^x which is bigger than i. + */ + public static final int nextTwoPow(int i) + { + /* + * This code is based of a lot of code I found on the Internet which mostly + * referenced a book called "Hacking delight". + */ + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i + 1; + } + + /** + * The initial size for our buffer. + */ + private static final int DEFAULT_CAPACITY = 1024; + + /** + * The buffer where we store our data. + */ + private byte[] databuf;; + + /** + * How many bytes at the beginning of the buffer are skipped. + */ + private int skipped = 0; + + /** + * How many bytes in the buffer are valid data. + */ + private int available = 0; + + public ByteQueue() + { + this(DEFAULT_CAPACITY); + } + + public ByteQueue(int capacity) + { + databuf = new byte[capacity]; + } + + /** + * Read data from the buffer. + * + * @param buf The buffer where the read data will be copied to. + * @param offset How many bytes to skip at the beginning of buf. + * @param len How many bytes to read at all. + * @param skip How many bytes from our data to skip. + */ + public void read(byte[] buf, int offset, int len, int skip) + { + if ((available - skip) < len) + { + throw new TlsRuntimeException("Not enough data to read"); + } + if ((buf.length - offset) < len) + { + throw new TlsRuntimeException("Buffer size of " + buf.length + + " is too small for a read of " + len + " bytes"); + } + System.arraycopy(databuf, skipped + skip, buf, offset, len); + } + + /** + * Add some data to our buffer. + * + * @param buf A byte-array to read data from. + * @param off How many bytes to skip at the beginning of the array. + * @param len How many bytes to read from the array. + */ + public void addData(byte[] buf, int off, int len) + { + if ((skipped + available + len) > databuf.length) + { + int desiredSize = ByteQueue.nextTwoPow(available + len); + if (desiredSize > databuf.length) + { + byte[] tmp = new byte[desiredSize]; + System.arraycopy(databuf, skipped, tmp, 0, available); + databuf = tmp; + } + else + { + System.arraycopy(databuf, skipped, databuf, 0, available); + } + skipped = 0; + } + + System.arraycopy(buf, off, databuf, skipped + available, len); + available += len; + } + + /** + * Remove some bytes from our data from the beginning. + * + * @param i How many bytes to remove. + */ + public void removeData(int i) + { + if (i > available) + { + throw new TlsRuntimeException("Cannot remove " + i + " bytes, only got " + available); + } + + /* + * Skip the data. + */ + available -= i; + skipped += i; + } + + /** + * Remove data from the buffer. + * + * @param buf The buffer where the removed data will be copied to. + * @param off How many bytes to skip at the beginning of buf. + * @param len How many bytes to read at all. + * @param skip How many bytes from our data to skip. + */ + public void removeData(byte[] buf, int off, int len, int skip) + { + read(buf, off, len, skip); + removeData(skip + len); + } + + public byte[] removeData(int len, int skip) + { + byte[] buf = new byte[len]; + removeData(buf, 0, len, skip); + return buf; + } + + /** + * @return The number of bytes which are available in this buffer. + */ + public int size() + { + return available; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertChainType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertChainType.java new file mode 100644 index 000000000..857496158 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertChainType.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.tls; + +/* + * RFC 3546 3.3. + */ +public class CertChainType +{ + public static final short individual_certs = 0; + public static final short pkipath = 1; + + public static boolean isValid(short certChainType) + { + return certChainType >= individual_certs && certChainType <= pkipath; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/Certificate.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/Certificate.java new file mode 100644 index 000000000..2af17ea1f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/Certificate.java @@ -0,0 +1,149 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Primitive; + +/** + * Parsing and encoding of a Certificate struct from RFC 4346. + * + *
+ * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<0..2^24-1>; + * } Certificate; + *+ * + * @see org.spongycastle.asn1.x509.Certificate + */ +public class Certificate +{ + public static final Certificate EMPTY_CHAIN = new Certificate( + new org.spongycastle.asn1.x509.Certificate[0]); + + protected org.spongycastle.asn1.x509.Certificate[] certificateList; + + public Certificate(org.spongycastle.asn1.x509.Certificate[] certificateList) + { + if (certificateList == null) + { + throw new IllegalArgumentException("'certificateList' cannot be null"); + } + + this.certificateList = certificateList; + } + + /** + * @deprecated use {@link #getCertificateList()} instead + */ + public org.spongycastle.asn1.x509.Certificate[] getCerts() + { + return getCertificateList(); + } + + /** + * @return an array of {@link org.spongycastle.asn1.x509.Certificate} representing a certificate + * chain. + */ + public org.spongycastle.asn1.x509.Certificate[] getCertificateList() + { + return cloneCertificateList(); + } + + public org.spongycastle.asn1.x509.Certificate getCertificateAt(int index) + { + return certificateList[index]; + } + + public int getLength() + { + return certificateList.length; + } + + /** + * @return
true
if this certificate chain contains no certificates, or
+ * false
otherwise.
+ */
+ public boolean isEmpty()
+ {
+ return certificateList.length == 0;
+ }
+
+ /**
+ * Encode this {@link Certificate} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output)
+ throws IOException
+ {
+ Vector derEncodings = new Vector(this.certificateList.length);
+
+ int totalLength = 0;
+ for (int i = 0; i < this.certificateList.length; ++i)
+ {
+ byte[] derEncoding = certificateList[i].getEncoded(ASN1Encoding.DER);
+ derEncodings.addElement(derEncoding);
+ totalLength += derEncoding.length + 3;
+ }
+
+ TlsUtils.checkUint24(totalLength);
+ TlsUtils.writeUint24(totalLength, output);
+
+ for (int i = 0; i < derEncodings.size(); ++i)
+ {
+ byte[] derEncoding = (byte[])derEncodings.elementAt(i);
+ TlsUtils.writeOpaque24(derEncoding, output);
+ }
+ }
+
+ /**
+ * Parse a {@link Certificate} from an {@link InputStream}.
+ *
+ * @param input the {@link InputStream} to parse from.
+ * @return a {@link Certificate} object.
+ * @throws IOException
+ */
+ public static Certificate parse(InputStream input)
+ throws IOException
+ {
+ int totalLength = TlsUtils.readUint24(input);
+ if (totalLength == 0)
+ {
+ return EMPTY_CHAIN;
+ }
+
+ byte[] certListData = TlsUtils.readFully(totalLength, input);
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(certListData);
+
+ Vector certificate_list = new Vector();
+ while (buf.available() > 0)
+ {
+ byte[] derEncoding = TlsUtils.readOpaque24(buf);
+ ASN1Primitive asn1Cert = TlsUtils.readDERObject(derEncoding);
+ certificate_list.addElement(org.spongycastle.asn1.x509.Certificate.getInstance(asn1Cert));
+ }
+
+ org.spongycastle.asn1.x509.Certificate[] certificateList = new org.spongycastle.asn1.x509.Certificate[certificate_list.size()];
+ for (int i = 0; i < certificate_list.size(); i++)
+ {
+ certificateList[i] = (org.spongycastle.asn1.x509.Certificate)certificate_list.elementAt(i);
+ }
+ return new Certificate(certificateList);
+ }
+
+ protected org.spongycastle.asn1.x509.Certificate[] cloneCertificateList()
+ {
+ org.spongycastle.asn1.x509.Certificate[] result = new org.spongycastle.asn1.x509.Certificate[certificateList.length];
+ System.arraycopy(certificateList, 0, result, 0, result.length);
+ return result;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateRequest.java
new file mode 100644
index 000000000..a4e5bf9e8
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateRequest.java
@@ -0,0 +1,164 @@
+package org.spongycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * Parsing and encoding of a CertificateRequest struct from RFC 4346.
+ *
+ * + * struct { + * ClientCertificateType certificate_types<1..2^8-1>; + * DistinguishedName certificate_authorities<3..2^16-1>; + * } CertificateRequest; + *+ * + * @see ClientCertificateType + * @see X500Name + */ +public class CertificateRequest +{ + protected short[] certificateTypes; + protected Vector supportedSignatureAlgorithms; + protected Vector certificateAuthorities; + + /* + * TODO RFC 5264 7.4.4 A list of the hash/signature algorithm pairs that the server is able to + * verify, listed in descending order of preference. + */ + + /** + * @param certificateTypes see {@link ClientCertificateType} for valid constants. + * @param certificateAuthorities a {@link Vector} of {@link X500Name}. + */ + public CertificateRequest(short[] certificateTypes, Vector supportedSignatureAlgorithms, Vector certificateAuthorities) + { + this.certificateTypes = certificateTypes; + this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; + this.certificateAuthorities = certificateAuthorities; + } + + /** + * @return an array of certificate types + * @see {@link ClientCertificateType} + */ + public short[] getCertificateTypes() + { + return certificateTypes; + } + + /** + * @return a {@link Vector} of {@link SignatureAndHashAlgorithm} (or null before TLS 1.2). + */ + public Vector getSupportedSignatureAlgorithms() + { + return supportedSignatureAlgorithms; + } + + /** + * @return a {@link Vector} of {@link X500Name} + */ + public Vector getCertificateAuthorities() + { + return certificateAuthorities; + } + + /** + * Encode this {@link CertificateRequest} to an {@link OutputStream}. + * + * @param output the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) + throws IOException + { + if (certificateTypes == null || certificateTypes.length == 0) + { + TlsUtils.writeUint8(0, output); + } + else + { + TlsUtils.writeUint8ArrayWithUint8Length(certificateTypes, output); + } + + if (supportedSignatureAlgorithms != null) + { + // TODO Check whether SignatureAlgorithm.anonymous is allowed here + TlsUtils.encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, output); + } + + if (certificateAuthorities == null || certificateAuthorities.isEmpty()) + { + TlsUtils.writeUint16(0, output); + } + else + { + Vector derEncodings = new Vector(certificateAuthorities.size()); + + int totalLength = 0; + for (int i = 0; i < certificateAuthorities.size(); ++i) + { + X500Name certificateAuthority = (X500Name)certificateAuthorities.elementAt(i); + byte[] derEncoding = certificateAuthority.getEncoded(ASN1Encoding.DER); + derEncodings.addElement(derEncoding); + totalLength += derEncoding.length; + } + + TlsUtils.checkUint16(totalLength); + TlsUtils.writeUint16(totalLength, output); + + for (int i = 0; i < derEncodings.size(); ++i) + { + byte[] encDN = (byte[])derEncodings.elementAt(i); + output.write(encDN); + } + } + } + + /** + * Parse a {@link CertificateRequest} from an {@link InputStream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link InputStream} to parse from. + * @return a {@link CertificateRequest} object. + * @throws IOException + */ + public static CertificateRequest parse(TlsContext context, InputStream input) + throws IOException + { + int numTypes = TlsUtils.readUint8(input); + short[] certificateTypes = new short[numTypes]; + for (int i = 0; i < numTypes; ++i) + { + certificateTypes[i] = TlsUtils.readUint8(input); + } + + Vector supportedSignatureAlgorithms = null; + if (TlsUtils.isTLSv12(context)) + { + // TODO Check whether SignatureAlgorithm.anonymous is allowed here + supportedSignatureAlgorithms = TlsUtils.parseSupportedSignatureAlgorithms(false, input); + } + + Vector certificateAuthorities = new Vector(); + byte[] certAuthData = TlsUtils.readOpaque16(input); + ByteArrayInputStream bis = new ByteArrayInputStream(certAuthData); + while (bis.available() > 0) + { + byte[] derEncoding = TlsUtils.readOpaque16(bis); + ASN1Primitive asn1 = TlsUtils.readDERObject(derEncoding); + certificateAuthorities.addElement(X500Name.getInstance(asn1)); + } + + return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatus.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatus.java new file mode 100644 index 000000000..14b0a0d9d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatus.java @@ -0,0 +1,105 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ocsp.OCSPResponse; + +public class CertificateStatus +{ + protected short statusType; + protected Object response; + + public CertificateStatus(short statusType, Object response) + { + if (!isCorrectType(statusType, response)) + { + throw new IllegalArgumentException("'response' is not an instance of the correct type"); + } + + this.statusType = statusType; + this.response = response; + } + + public short getStatusType() + { + return statusType; + } + + public Object getResponse() + { + return response; + } + + public OCSPResponse getOCSPResponse() + { + if (!isCorrectType(CertificateStatusType.ocsp, response)) + { + throw new IllegalStateException("'response' is not an OCSPResponse"); + } + return (OCSPResponse)response; + } + + /** + * Encode this {@link CertificateStatus} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + TlsUtils.writeUint8(statusType, output); + + switch (statusType) + { + case CertificateStatusType.ocsp: + byte[] derEncoding = ((OCSPResponse) response).getEncoded(ASN1Encoding.DER); + TlsUtils.writeOpaque24(derEncoding, output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link CertificateStatus} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link CertificateStatus} object. + * @throws IOException + */ + public static CertificateStatus parse(InputStream input) throws IOException + { + short status_type = TlsUtils.readUint8(input); + Object response; + + switch (status_type) + { + case CertificateStatusType.ocsp: + { + byte[] derEncoding = TlsUtils.readOpaque24(input); + response = OCSPResponse.getInstance(TlsUtils.readDERObject(derEncoding)); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new CertificateStatus(status_type, response); + } + + protected static boolean isCorrectType(short statusType, Object response) + { + switch (statusType) + { + case CertificateStatusType.ocsp: + return response instanceof OCSPResponse; + default: + throw new IllegalArgumentException("'statusType' is an unsupported value"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatusRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatusRequest.java new file mode 100644 index 000000000..163180571 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatusRequest.java @@ -0,0 +1,98 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class CertificateStatusRequest +{ + protected short statusType; + protected Object request; + + public CertificateStatusRequest(short statusType, Object request) + { + if (!isCorrectType(statusType, request)) + { + throw new IllegalArgumentException("'request' is not an instance of the correct type"); + } + + this.statusType = statusType; + this.request = request; + } + + public short getStatusType() + { + return statusType; + } + + public Object getRequest() + { + return request; + } + + public OCSPStatusRequest getOCSPStatusRequest() + { + if (!isCorrectType(CertificateStatusType.ocsp, request)) + { + throw new IllegalStateException("'request' is not an OCSPStatusRequest"); + } + return (OCSPStatusRequest)request; + } + + /** + * Encode this {@link CertificateStatusRequest} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + TlsUtils.writeUint8(statusType, output); + + switch (statusType) + { + case CertificateStatusType.ocsp: + ((OCSPStatusRequest) request).encode(output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link CertificateStatusRequest} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link CertificateStatusRequest} object. + * @throws IOException + */ + public static CertificateStatusRequest parse(InputStream input) throws IOException + { + short status_type = TlsUtils.readUint8(input); + Object result; + + switch (status_type) + { + case CertificateStatusType.ocsp: + result = OCSPStatusRequest.parse(input); + break; + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new CertificateStatusRequest(status_type, result); + } + + protected static boolean isCorrectType(short statusType, Object request) + { + switch (statusType) + { + case CertificateStatusType.ocsp: + return request instanceof OCSPStatusRequest; + default: + throw new IllegalArgumentException("'statusType' is an unsupported value"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatusType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatusType.java new file mode 100644 index 000000000..750902623 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateStatusType.java @@ -0,0 +1,9 @@ +package org.spongycastle.crypto.tls; + +public class CertificateStatusType +{ + /* + * RFC 3546 3.6 + */ + public static final short ocsp = 1; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateURL.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateURL.java new file mode 100644 index 000000000..f6b7df536 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateURL.java @@ -0,0 +1,133 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +/* + * RFC 3546 3.3 + */ +public class CertificateURL +{ + protected short type; + protected Vector urlAndHashList; + + /** + * @param type + * see {@link CertChainType} for valid constants. + * @param urlAndHashList + * a {@link Vector} of {@link URLAndHash}. + */ + public CertificateURL(short type, Vector urlAndHashList) + { + if (!CertChainType.isValid(type)) + { + throw new IllegalArgumentException("'type' is not a valid CertChainType value"); + } + if (urlAndHashList == null || urlAndHashList.isEmpty()) + { + throw new IllegalArgumentException("'urlAndHashList' must have length > 0"); + } + + this.type = type; + this.urlAndHashList = urlAndHashList; + } + + /** + * @return {@link CertChainType} + */ + public short getType() + { + return type; + } + + /** + * @return a {@link Vector} of {@link URLAndHash} + */ + public Vector getURLAndHashList() + { + return urlAndHashList; + } + + /** + * Encode this {@link CertificateURL} to an {@link OutputStream}. + * + * @param output the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) + throws IOException + { + TlsUtils.writeUint8(this.type, output); + + ListBuffer16 buf = new ListBuffer16(); + for (int i = 0; i < this.urlAndHashList.size(); ++i) + { + URLAndHash urlAndHash = (URLAndHash)this.urlAndHashList.elementAt(i); + urlAndHash.encode(buf); + } + buf.encodeTo(output); + } + + /** + * Parse a {@link CertificateURL} from an {@link InputStream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link InputStream} to parse from. + * @return a {@link CertificateURL} object. + * @throws IOException + */ + public static CertificateURL parse(TlsContext context, InputStream input) + throws IOException + { + short type = TlsUtils.readUint8(input); + if (!CertChainType.isValid(type)) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + int totalLength = TlsUtils.readUint16(input); + if (totalLength < 1) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + byte[] urlAndHashListData = TlsUtils.readFully(totalLength, input); + + ByteArrayInputStream buf = new ByteArrayInputStream(urlAndHashListData); + + Vector url_and_hash_list = new Vector(); + while (buf.available() > 0) + { + URLAndHash url_and_hash = URLAndHash.parse(context, buf); + url_and_hash_list.addElement(url_and_hash); + } + + return new CertificateURL(type, url_and_hash_list); + } + + // TODO Could be more generally useful + class ListBuffer16 extends ByteArrayOutputStream + { + ListBuffer16() throws IOException + { + // Reserve space for length + TlsUtils.writeUint16(0, this); + } + + void encodeTo(OutputStream output) throws IOException + { + // Patch actual length back in + int length = count - 2; + TlsUtils.checkUint16(length); + TlsUtils.writeUint16(length, buf, 0); + output.write(buf, 0, count); + buf = null; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateVerifyer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateVerifyer.java new file mode 100644 index 000000000..65c764a47 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CertificateVerifyer.java @@ -0,0 +1,16 @@ +package org.spongycastle.crypto.tls; + +/** + * This should be implemented by any class which can find out, if a given certificate + * chain is being accepted by an client. + * + * @deprecated Perform certificate verification in TlsAuthentication implementation + */ +public interface CertificateVerifyer +{ + /** + * @param certs The certs, which are part of the chain. + * @return True, if the chain is accepted, false otherwise. + */ + public boolean isValid(org.spongycastle.asn1.x509.Certificate[] certs); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ChangeCipherSpec.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ChangeCipherSpec.java new file mode 100644 index 000000000..b3a23c39f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ChangeCipherSpec.java @@ -0,0 +1,6 @@ +package org.spongycastle.crypto.tls; + +public class ChangeCipherSpec +{ + public static final short change_cipher_spec = 1; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CipherSuite.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CipherSuite.java new file mode 100644 index 000000000..f55031ba8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CipherSuite.java @@ -0,0 +1,297 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 A.5 + */ +public class CipherSuite +{ + public static final int TLS_NULL_WITH_NULL_NULL = 0x0000; + public static final int TLS_RSA_WITH_NULL_MD5 = 0x0001; + public static final int TLS_RSA_WITH_NULL_SHA = 0x0002; + public static final int TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003; + public static final int TLS_RSA_WITH_RC4_128_MD5 = 0x0004; + public static final int TLS_RSA_WITH_RC4_128_SHA = 0x0005; + public static final int TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006; + public static final int TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007; + public static final int TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008; + public static final int TLS_RSA_WITH_DES_CBC_SHA = 0x0009; + public static final int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A; + public static final int TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B; + public static final int TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C; + public static final int TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D; + public static final int TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E; + public static final int TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F; + public static final int TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010; + public static final int TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011; + public static final int TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012; + public static final int TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013; + public static final int TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014; + public static final int TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015; + public static final int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016; + public static final int TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017; + public static final int TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018; + public static final int TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019; + public static final int TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A; + public static final int TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B; + + /* + * Note: The cipher suite values { 0x00, 0x1C } and { 0x00, 0x1D } are reserved to avoid + * collision with Fortezza-based cipher suites in SSL 3. + */ + + /* + * RFC 3268 + */ + public static final int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F; + public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030; + public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031; + public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032; + public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033; + public static final int TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034; + public static final int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035; + public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036; + public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037; + public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038; + public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039; + public static final int TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A; + + /* + * RFC 4132 + */ + public static final int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041; + public static final int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042; + public static final int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043; + public static final int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044; + public static final int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045; + public static final int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046; + public static final int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084; + public static final int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085; + public static final int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086; + public static final int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087; + public static final int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088; + public static final int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089; + + /* + * RFC 4162 + */ + public static final int TLS_RSA_WITH_SEED_CBC_SHA = 0x0096; + public static final int TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097; + public static final int TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098; + public static final int TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099; + public static final int TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A; + public static final int TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B; + + /* + * RFC 4279 + */ + public static final int TLS_PSK_WITH_RC4_128_SHA = 0x008A; + public static final int TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B; + public static final int TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C; + public static final int TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D; + public static final int TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E; + public static final int TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F; + public static final int TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090; + public static final int TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091; + public static final int TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092; + public static final int TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093; + public static final int TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094; + public static final int TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095; + + /* + * RFC 4492 + */ + public static final int TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001; + public static final int TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002; + public static final int TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003; + public static final int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004; + public static final int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005; + public static final int TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006; + public static final int TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007; + public static final int TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008; + public static final int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009; + public static final int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A; + public static final int TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B; + public static final int TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C; + public static final int TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D; + public static final int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E; + public static final int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F; + public static final int TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010; + public static final int TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011; + public static final int TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012; + public static final int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013; + public static final int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014; + public static final int TLS_ECDH_anon_WITH_NULL_SHA = 0xC015; + public static final int TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016; + public static final int TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017; + public static final int TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018; + public static final int TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019; + + /* + * RFC 4785 + */ + public static final int TLS_PSK_WITH_NULL_SHA = 0x002C; + public static final int TLS_DHE_PSK_WITH_NULL_SHA = 0x002D; + public static final int TLS_RSA_PSK_WITH_NULL_SHA = 0x002E; + + /* + * RFC 5054 + */ + public static final int TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A; + public static final int TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B; + public static final int TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C; + public static final int TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D; + public static final int TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E; + public static final int TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F; + public static final int TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020; + public static final int TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021; + public static final int TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022; + + /* + * RFC 5246 + */ + public static final int TLS_RSA_WITH_NULL_SHA256 = 0x003B; + public static final int TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C; + public static final int TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D; + public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E; + public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F; + public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040; + public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067; + public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068; + public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069; + public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A; + public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B; + public static final int TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C; + public static final int TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D; + + /* + * RFC 5288 + */ + public static final int TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C; + public static final int TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D; + public static final int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E; + public static final int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F; + public static final int TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0; + public static final int TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1; + public static final int TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2; + public static final int TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3; + public static final int TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4; + public static final int TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5; + public static final int TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6; + public static final int TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7; + + /* + * RFC 5289 + */ + public static final int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023; + public static final int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024; + public static final int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025; + public static final int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026; + public static final int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027; + public static final int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028; + public static final int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029; + public static final int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A; + public static final int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B; + public static final int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C; + public static final int TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D; + public static final int TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E; + public static final int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F; + public static final int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030; + public static final int TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031; + public static final int TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032; + + /* + * RFC 5487 + */ + public static final int TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8; + public static final int TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9; + public static final int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA; + public static final int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB; + public static final int TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC; + public static final int TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD; + public static final int TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE; + public static final int TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF; + public static final int TLS_PSK_WITH_NULL_SHA256 = 0x00B0; + public static final int TLS_PSK_WITH_NULL_SHA384 = 0x00B1; + public static final int TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2; + public static final int TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3; + public static final int TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4; + public static final int TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5; + public static final int TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6; + public static final int TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7; + public static final int TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8; + public static final int TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9; + + /* + * RFC 5489 + */ + public static final int TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033; + public static final int TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034; + public static final int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035; + public static final int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036; + public static final int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037; + public static final int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038; + public static final int TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039; + public static final int TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A; + public static final int TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B; + + /* + * RFC 5746 + */ + public static final int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF; + + /* + * RFC 6655 + */ + public static final int TLS_RSA_WITH_AES_128_CCM = 0xC09C; + public static final int TLS_RSA_WITH_AES_256_CCM = 0xC09D; + public static final int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E; + public static final int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F; + public static final int TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0; + public static final int TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1; + public static final int TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2; + public static final int TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3; + public static final int TLS_PSK_WITH_AES_128_CCM = 0xC0A4; + public static final int TLS_PSK_WITH_AES_256_CCM = 0xC0A5; + public static final int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6; + public static final int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7; + public static final int TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8; + public static final int TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9; + public static final int TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA; + public static final int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB; + + /* + * TBD[draft-josefsson-salsa20-tls-02] + */ + static final int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF00; + static final int TLS_RSA_WITH_SALSA20_SHA1 = 0xFF01; + static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF02; + static final int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xFF03; + static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF04; + static final int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xFF05; + static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF06; + static final int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xFF07; + static final int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF08; + static final int TLS_PSK_WITH_SALSA20_SHA1 = 0xFF09; + static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0A; + static final int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xFF0B; + static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0C; + static final int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xFF0D; + static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0E; + static final int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xFF0F; + static final int TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF10; + static final int TLS_RSA_WITH_SALSA20_UMAC96 = 0xFF11; + static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF12; + static final int TLS_DHE_RSA_WITH_SALSA20_UMAC96 = 0xFF13; + static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF14; + static final int TLS_ECDHE_RSA_WITH_SALSA20_UMAC96 = 0xFF15; + static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF16; + static final int TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96 = 0xFF17; + static final int TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF18; + static final int TLS_PSK_WITH_SALSA20_UMAC96 = 0xFF19; + static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1A; + static final int TLS_DHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1B; + static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1C; + static final int TLS_RSA_PSK_WITH_SALSA20_UMAC96 = 0xFF1D; + static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1E; + static final int TLS_ECDHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1F; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CipherType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CipherType.java new file mode 100644 index 000000000..d260668ea --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CipherType.java @@ -0,0 +1,19 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class CipherType +{ + + public static final int stream = 0; + public static final int block = 1; + + /* + * RFC 5246 + */ + public static final int aead = 2; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ClientAuthenticationType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ClientAuthenticationType.java new file mode 100644 index 000000000..85d03fda5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ClientAuthenticationType.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +public class ClientAuthenticationType +{ + + /* + * RFC 5077 4 + */ + public static final short anonymous = 0; + public static final short certificate_based = 1; + public static final short psk = 2; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ClientCertificateType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ClientCertificateType.java new file mode 100644 index 000000000..e7fcef58b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ClientCertificateType.java @@ -0,0 +1,23 @@ +package org.spongycastle.crypto.tls; + +public class ClientCertificateType +{ + + /* + * RFC 4346 7.4.4 + */ + public static final short rsa_sign = 1; + public static final short dss_sign = 2; + public static final short rsa_fixed_dh = 3; + public static final short dss_fixed_dh = 4; + public static final short rsa_ephemeral_dh_RESERVED = 5; + public static final short dss_ephemeral_dh_RESERVED = 6; + public static final short fortezza_dms_RESERVED = 20; + + /* + * RFC 4492 5.5 + */ + public static final short ecdsa_sign = 64; + public static final short rsa_fixed_ecdh = 65; + public static final short ecdsa_fixed_ecdh = 66; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CombinedHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CombinedHash.java new file mode 100644 index 000000000..b32ad91ee --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CombinedHash.java @@ -0,0 +1,135 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.Digest; + +/** + * A combined hash, which implements md5(m) || sha1(m). + */ +class CombinedHash + implements TlsHandshakeHash +{ + protected TlsContext context; + protected Digest md5; + protected Digest sha1; + + CombinedHash() + { + this.md5 = TlsUtils.createHash(HashAlgorithm.md5); + this.sha1 = TlsUtils.createHash(HashAlgorithm.sha1); + } + + CombinedHash(CombinedHash t) + { + this.context = t.context; + this.md5 = TlsUtils.cloneHash(HashAlgorithm.md5, t.md5); + this.sha1 = TlsUtils.cloneHash(HashAlgorithm.sha1, t.sha1); + } + + public void init(TlsContext context) + { + this.context = context; + } + + public TlsHandshakeHash notifyPRFDetermined() + { + return this; + } + + public void trackHashAlgorithm(short hashAlgorithm) + { + throw new IllegalStateException("CombinedHash only supports calculating the legacy PRF for handshake hash"); + } + + public void sealHashAlgorithms() + { + } + + public TlsHandshakeHash stopTracking() + { + return new CombinedHash(this); + } + + public Digest forkPRFHash() + { + return new CombinedHash(this); + } + + public byte[] getFinalHash(short hashAlgorithm) + { + throw new IllegalStateException("CombinedHash doesn't support multiple hashes"); + } + + /** + * @see org.spongycastle.crypto.Digest#getAlgorithmName() + */ + public String getAlgorithmName() + { + return md5.getAlgorithmName() + " and " + sha1.getAlgorithmName(); + } + + /** + * @see org.spongycastle.crypto.Digest#getDigestSize() + */ + public int getDigestSize() + { + return md5.getDigestSize() + sha1.getDigestSize(); + } + + /** + * @see org.spongycastle.crypto.Digest#update(byte) + */ + public void update(byte in) + { + md5.update(in); + sha1.update(in); + } + + /** + * @see org.spongycastle.crypto.Digest#update(byte[], int, int) + */ + public void update(byte[] in, int inOff, int len) + { + md5.update(in, inOff, len); + sha1.update(in, inOff, len); + } + + /** + * @see org.spongycastle.crypto.Digest#doFinal(byte[], int) + */ + public int doFinal(byte[] out, int outOff) + { + if (context != null && TlsUtils.isSSL(context)) + { + ssl3Complete(md5, SSL3Mac.IPAD, SSL3Mac.OPAD, 48); + ssl3Complete(sha1, SSL3Mac.IPAD, SSL3Mac.OPAD, 40); + } + + int i1 = md5.doFinal(out, outOff); + int i2 = sha1.doFinal(out, outOff + i1); + return i1 + i2; + } + + /** + * @see org.spongycastle.crypto.Digest#reset() + */ + public void reset() + { + md5.reset(); + sha1.reset(); + } + + protected void ssl3Complete(Digest d, byte[] ipad, byte[] opad, int padLength) + { + byte[] master_secret = context.getSecurityParameters().masterSecret; + + d.update(master_secret, 0, master_secret.length); + d.update(ipad, 0, padLength); + + byte[] tmp = new byte[d.getDigestSize()]; + d.doFinal(tmp, 0); + + d.update(master_secret, 0, master_secret.length); + d.update(opad, 0, padLength); + d.update(tmp, 0, tmp.length); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CompressionMethod.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CompressionMethod.java new file mode 100644 index 000000000..2a19b7ff0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/CompressionMethod.java @@ -0,0 +1,24 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 6.1 + */ +public class CompressionMethod +{ + public static final short _null = 0; + + /** + * @deprecated use '_null' instead + */ + public static final short NULL = _null; + + /* + * RFC 3749 2 + */ + public static final short DEFLATE = 1; + + /* + * Values from 224 decimal (0xE0) through 255 decimal (0xFF) + * inclusive are reserved for private use. + */ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ConnectionEnd.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ConnectionEnd.java new file mode 100644 index 000000000..dd9f284c2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ConnectionEnd.java @@ -0,0 +1,14 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class ConnectionEnd +{ + + public static final int server = 0; + public static final int client = 1; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ContentType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ContentType.java new file mode 100644 index 000000000..8704dd809 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ContentType.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 6.2.1 + */ +public class ContentType +{ + public static final short change_cipher_spec = 20; + public static final short alert = 21; + public static final short handshake = 22; + public static final short application_data = 23; + public static final short heartbeat = 24; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSClientProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSClientProtocol.java new file mode 100644 index 000000000..70cf373e8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSClientProtocol.java @@ -0,0 +1,814 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.util.Arrays; + +public class DTLSClientProtocol + extends DTLSProtocol +{ + public DTLSClientProtocol(SecureRandom secureRandom) + { + super(secureRandom); + } + + public DTLSTransport connect(TlsClient client, DatagramTransport transport) + throws IOException + { + if (client == null) + { + throw new IllegalArgumentException("'client' cannot be null"); + } + if (transport == null) + { + throw new IllegalArgumentException("'transport' cannot be null"); + } + + SecurityParameters securityParameters = new SecurityParameters(); + securityParameters.entity = ConnectionEnd.client; + securityParameters.clientRandom = TlsProtocol.createRandomBlock(secureRandom); + + ClientHandshakeState state = new ClientHandshakeState(); + state.client = client; + state.clientContext = new TlsClientContextImpl(secureRandom, securityParameters); + client.init(state.clientContext); + + DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.clientContext, client, ContentType.handshake); + + TlsSession sessionToResume = state.client.getSessionToResume(); + if (sessionToResume != null) + { + SessionParameters sessionParameters = sessionToResume.exportSessionParameters(); + if (sessionParameters != null) + { + state.tlsSession = sessionToResume; + state.sessionParameters = sessionParameters; + } + } + + try + { + return clientHandshake(state, recordLayer); + } + catch (TlsFatalAlert fatalAlert) + { + recordLayer.fail(fatalAlert.getAlertDescription()); + throw fatalAlert; + } + catch (IOException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw e; + } + catch (RuntimeException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected DTLSTransport clientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer) + throws IOException + { + SecurityParameters securityParameters = state.clientContext.getSecurityParameters(); + DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.clientContext, recordLayer); + + byte[] clientHelloBody = generateClientHello(state, state.client); + handshake.sendMessage(HandshakeType.client_hello, clientHelloBody); + + DTLSReliableHandshake.Message serverMessage = handshake.receiveMessage(); + + while (serverMessage.getType() == HandshakeType.hello_verify_request) + { + ProtocolVersion recordLayerVersion = recordLayer.resetDiscoveredPeerVersion(); + ProtocolVersion client_version = state.clientContext.getClientVersion(); + + /* + * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0 regardless of + * the version of TLS that is expected to be negotiated. DTLS 1.2 and 1.0 clients MUST use + * the version solely to indicate packet formatting (which is the same in both DTLS 1.2 and + * 1.0) and not as part of version negotiation. + */ + if (!recordLayerVersion.isEqualOrEarlierVersionOf(client_version)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + byte[] cookie = processHelloVerifyRequest(state, serverMessage.getBody()); + byte[] patched = patchClientHelloWithCookie(clientHelloBody, cookie); + + handshake.resetHandshakeMessagesDigest(); + handshake.sendMessage(HandshakeType.client_hello, patched); + + serverMessage = handshake.receiveMessage(); + } + + if (serverMessage.getType() == HandshakeType.server_hello) + { + reportServerVersion(state, recordLayer.getDiscoveredPeerVersion()); + + processServerHello(state, serverMessage.getBody()); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + if (state.maxFragmentLength >= 0) + { + int plainTextLimit = 1 << (8 + state.maxFragmentLength); + recordLayer.setPlaintextLimit(plainTextLimit); + } + + securityParameters.cipherSuite = state.selectedCipherSuite; + securityParameters.compressionAlgorithm = state.selectedCompressionMethod; + securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.clientContext, state.selectedCipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has + * a verify_data_length equal to 12. This includes all existing cipher suites. + */ + securityParameters.verifyDataLength = 12; + + handshake.notifyHelloComplete(); + + boolean resumedSession = state.selectedSessionID.length > 0 && state.tlsSession != null + && Arrays.areEqual(state.selectedSessionID, state.tlsSession.getSessionID()); + + if (resumedSession) + { + if (securityParameters.getCipherSuite() != state.sessionParameters.getCipherSuite() + || securityParameters.getCompressionAlgorithm() != state.sessionParameters.getCompressionAlgorithm()) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + securityParameters.masterSecret = Arrays.clone(state.sessionParameters.getMasterSecret()); + recordLayer.initPendingEpoch(state.client.getCipher()); + + // NOTE: Calculated exclusive of the actual Finished message from the server + byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); + processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData); + + // NOTE: Calculated exclusive of the Finished message itself + byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); + handshake.sendMessage(HandshakeType.finished, clientVerifyData); + + handshake.finish(); + + state.clientContext.setResumableSession(state.tlsSession); + + state.client.notifyHandshakeComplete(); + + return new DTLSTransport(recordLayer); + } + + invalidateSession(state); + + if (state.selectedSessionID.length > 0) + { + state.tlsSession = new TlsSessionImpl(state.selectedSessionID, null); + } + + serverMessage = handshake.receiveMessage(); + + if (serverMessage.getType() == HandshakeType.supplemental_data) + { + processServerSupplementalData(state, serverMessage.getBody()); + serverMessage = handshake.receiveMessage(); + } + else + { + state.client.processServerSupplementalData(null); + } + + state.keyExchange = state.client.getKeyExchange(); + state.keyExchange.init(state.clientContext); + + Certificate serverCertificate = null; + + if (serverMessage.getType() == HandshakeType.certificate) + { + serverCertificate = processServerCertificate(state, serverMessage.getBody()); + serverMessage = handshake.receiveMessage(); + } + else + { + // Okay, Certificate is optional + state.keyExchange.skipServerCredentials(); + } + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.isEmpty()) + { + state.allowCertificateStatus = false; + } + + if (serverMessage.getType() == HandshakeType.certificate_status) + { + processCertificateStatus(state, serverMessage.getBody()); + serverMessage = handshake.receiveMessage(); + } + else + { + // Okay, CertificateStatus is optional + } + + if (serverMessage.getType() == HandshakeType.server_key_exchange) + { + processServerKeyExchange(state, serverMessage.getBody()); + serverMessage = handshake.receiveMessage(); + } + else + { + // Okay, ServerKeyExchange is optional + state.keyExchange.skipServerKeyExchange(); + } + + if (serverMessage.getType() == HandshakeType.certificate_request) + { + processCertificateRequest(state, serverMessage.getBody()); + + /* + * TODO Give the client a chance to immediately select the CertificateVerify hash + * algorithm here to avoid tracking the other hash algorithms unnecessarily? + */ + TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(), + state.certificateRequest.getSupportedSignatureAlgorithms()); + + serverMessage = handshake.receiveMessage(); + } + else + { + // Okay, CertificateRequest is optional + } + + if (serverMessage.getType() == HandshakeType.server_hello_done) + { + if (serverMessage.getBody().length != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + handshake.getHandshakeHash().sealHashAlgorithms(); + + Vector clientSupplementalData = state.client.getClientSupplementalData(); + if (clientSupplementalData != null) + { + byte[] supplementalDataBody = generateSupplementalData(clientSupplementalData); + handshake.sendMessage(HandshakeType.supplemental_data, supplementalDataBody); + } + + if (state.certificateRequest != null) + { + state.clientCredentials = state.authentication.getClientCredentials(state.certificateRequest); + + /* + * RFC 5246 If no suitable certificate is available, the client MUST send a certificate + * message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + Certificate clientCertificate = null; + if (state.clientCredentials != null) + { + clientCertificate = state.clientCredentials.getCertificate(); + } + if (clientCertificate == null) + { + clientCertificate = Certificate.EMPTY_CHAIN; + } + + byte[] certificateBody = generateCertificate(clientCertificate); + handshake.sendMessage(HandshakeType.certificate, certificateBody); + } + + if (state.clientCredentials != null) + { + state.keyExchange.processClientCredentials(state.clientCredentials); + } + else + { + state.keyExchange.skipClientCredentials(); + } + + byte[] clientKeyExchangeBody = generateClientKeyExchange(state); + handshake.sendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody); + + TlsProtocol.establishMasterSecret(state.clientContext, state.keyExchange); + recordLayer.initPendingEpoch(state.client.getCipher()); + + TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish(); + + if (state.clientCredentials != null && state.clientCredentials instanceof TlsSignerCredentials) + { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials; + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + byte[] hash; + + if (TlsUtils.isTLSv12(state.clientContext)) + { + signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm(); + if (signatureAndHashAlgorithm == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash()); + } + else + { + signatureAndHashAlgorithm = null; + hash = TlsProtocol.getCurrentPRFHash(state.clientContext, prepareFinishHash, null); + } + + byte[] signature = signerCredentials.generateCertificateSignature(hash); + DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature); + byte[] certificateVerifyBody = generateCertificateVerify(state, certificateVerify); + handshake.sendMessage(HandshakeType.certificate_verify, certificateVerifyBody); + } + + // NOTE: Calculated exclusive of the Finished message itself + byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); + handshake.sendMessage(HandshakeType.finished, clientVerifyData); + + if (state.expectSessionTicket) + { + serverMessage = handshake.receiveMessage(); + if (serverMessage.getType() == HandshakeType.session_ticket) + { + processNewSessionTicket(state, serverMessage.getBody()); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + // NOTE: Calculated exclusive of the actual Finished message from the server + byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); + processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData); + + handshake.finish(); + + if (state.tlsSession != null) + { + state.sessionParameters = new SessionParameters.Builder() + .setCipherSuite(securityParameters.cipherSuite) + .setCompressionAlgorithm(securityParameters.compressionAlgorithm) + .setMasterSecret(securityParameters.masterSecret) + .setPeerCertificate(serverCertificate) + .build(); + + state.tlsSession = TlsUtils.importSession(state.tlsSession.getSessionID(), state.sessionParameters); + + state.clientContext.setResumableSession(state.tlsSession); + } + + state.client.notifyHandshakeComplete(); + + return new DTLSTransport(recordLayer); + } + + protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + certificateVerify.encode(buf); + return buf.toByteArray(); + } + + protected byte[] generateClientHello(ClientHandshakeState state, TlsClient client) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + ProtocolVersion client_version = client.getClientVersion(); + if (!client_version.isDTLS()) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + state.clientContext.setClientVersion(client_version); + TlsUtils.writeVersion(client_version, buf); + + buf.write(state.clientContext.getSecurityParameters().getClientRandom()); + + // Session ID + byte[] session_id = TlsUtils.EMPTY_BYTES; + if (state.tlsSession != null) + { + session_id = state.tlsSession.getSessionID(); + if (session_id == null || session_id.length > 32) + { + session_id = TlsUtils.EMPTY_BYTES; + } + } + TlsUtils.writeOpaque8(session_id, buf); + + // Cookie + TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf); + + /* + * Cipher suites + */ + state.offeredCipherSuites = client.getCipherSuites(); + + // Integer -> byte[] + state.clientExtensions = client.getClientExtensions(); + + // Cipher Suites (and SCSV) + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); + + boolean noSCSV = !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if (noRenegExt && noSCSV) + { + // TODO Consider whether to default to a client extension instead + state.offeredCipherSuites = Arrays.append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + TlsUtils.writeUint16ArrayWithUint16Length(state.offeredCipherSuites, buf); + } + + // TODO Add support for compression + // Compression methods + // state.offeredCompressionMethods = client.getCompressionMethods(); + state.offeredCompressionMethods = new short[]{ CompressionMethod._null }; + + TlsUtils.writeUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf); + + // Extensions + if (state.clientExtensions != null) + { + TlsProtocol.writeExtensions(buf, state.clientExtensions); + } + + return buf.toByteArray(); + } + + protected byte[] generateClientKeyExchange(ClientHandshakeState state) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + state.keyExchange.generateClientKeyExchange(buf); + return buf.toByteArray(); + } + + protected void invalidateSession(ClientHandshakeState state) + { + if (state.sessionParameters != null) + { + state.sessionParameters.clear(); + state.sessionParameters = null; + } + + if (state.tlsSession != null) + { + state.tlsSession.invalidate(); + state.tlsSession = null; + } + } + + protected void processCertificateRequest(ClientHandshakeState state, byte[] body) + throws IOException + { + if (state.authentication == null) + { + /* + * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server to + * request client identification. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + state.certificateRequest = CertificateRequest.parse(state.clientContext, buf); + + TlsProtocol.assertEmpty(buf); + + state.keyExchange.validateCertificateRequest(state.certificateRequest); + } + + protected void processCertificateStatus(ClientHandshakeState state, byte[] body) + throws IOException + { + if (!state.allowCertificateStatus) + { + /* + * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the + * server MUST have included an extension of type "status_request" with empty + * "extension_data" in the extended server hello.. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + state.certificateStatus = CertificateStatus.parse(buf); + + TlsProtocol.assertEmpty(buf); + + // TODO[RFC 3546] Figure out how to provide this to the client/authentication. + } + + protected byte[] processHelloVerifyRequest(ClientHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + ProtocolVersion server_version = TlsUtils.readVersion(buf); + byte[] cookie = TlsUtils.readOpaque8(buf); + + TlsProtocol.assertEmpty(buf); + + // TODO Seems this behaviour is not yet in line with OpenSSL for DTLS 1.2 +// reportServerVersion(state, server_version); + if (!server_version.isEqualOrEarlierVersionOf(state.clientContext.getClientVersion())) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + /* + * RFC 6347 This specification increases the cookie size limit to 255 bytes for greater + * future flexibility. The limit remains 32 for previous versions of DTLS. + */ + if (!ProtocolVersion.DTLSv12.isEqualOrEarlierVersionOf(server_version) && cookie.length > 32) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return cookie; + } + + protected void processNewSessionTicket(ClientHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf); + + TlsProtocol.assertEmpty(buf); + + state.client.notifyNewSessionTicket(newSessionTicket); + } + + protected Certificate processServerCertificate(ClientHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + Certificate serverCertificate = Certificate.parse(buf); + + TlsProtocol.assertEmpty(buf); + + state.keyExchange.processServerCertificate(serverCertificate); + state.authentication = state.client.getAuthentication(); + state.authentication.notifyServerCertificate(serverCertificate); + + return serverCertificate; + } + + protected void processServerHello(ClientHandshakeState state, byte[] body) + throws IOException + { + SecurityParameters securityParameters = state.clientContext.getSecurityParameters(); + + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + ProtocolVersion server_version = TlsUtils.readVersion(buf); + reportServerVersion(state, server_version); + + securityParameters.serverRandom = TlsUtils.readFully(32, buf); + + state.selectedSessionID = TlsUtils.readOpaque8(buf); + if (state.selectedSessionID.length > 32) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + state.client.notifySessionID(state.selectedSessionID); + + state.selectedCipherSuite = TlsUtils.readUint16(buf); + if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite) + || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.illegal_parameter); + + state.client.notifySelectedCipherSuite(state.selectedCipherSuite); + + state.selectedCompressionMethod = TlsUtils.readUint8(buf); + if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + state.client.notifySelectedCompressionMethod(state.selectedCompressionMethod); + + /* + * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server + * hello message when the client has requested extended functionality via the extended + * client hello message specified in Section 2.1. ... Note that the extended server hello + * message is only sent in response to an extended client hello message. This prevents the + * possibility that the extended server hello message could "break" existing TLS 1.0 + * clients. + */ + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + + // Integer -> byte[] + Hashtable serverExtensions = TlsProtocol.readExtensions(buf); + + /* + * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an + * extended client hello message. However, see RFC 5746 exception below. We always include + * the SCSV, so an Extended Server Hello is always allowed. + */ + if (serverExtensions != null) + { + Enumeration e = serverExtensions.keys(); + while (e.hasMoreElements()) + { + Integer extType = (Integer)e.nextElement(); + + /* + * RFC 5746 Note that sending a "renegotiation_info" extension in response to a + * ClientHello containing only the SCSV is an explicit exception to the prohibition + * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is + * only allowed because the client is signaling its willingness to receive the + * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. TLS implementations + * MUST continue to comply with Section 7.4.1.4 for all other extensions. + */ + if (!extType.equals(TlsProtocol.EXT_RenegotiationInfo) + && null == TlsUtils.getExtensionData(state.clientExtensions, extType)) + { + /* + * RFC 3546 2.3 Note that for all extension types (including those defined in + * future), the extension type MUST NOT appear in the extended server hello + * unless the same extension type appeared in the corresponding client hello. + * Thus clients MUST abort the handshake if they receive an extension type in + * the extended server hello that they did not request in the associated + * (extended) client hello. + */ + throw new TlsFatalAlert(AlertDescription.unsupported_extension); + } + } + + /* + * RFC 5746 3.4. Client Behavior: Initial Handshake + */ + { + /* + * When a ServerHello is received, the client MUST check if it includes the + * "renegotiation_info" extension: + */ + byte[] renegExtData = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo); + if (renegExtData != null) + { + /* + * If the extension is present, set the secure_renegotiation flag to TRUE. The + * client MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake (by sending a fatal + * handshake_failure alert). + */ + state.secure_renegotiation = true; + + if (!Arrays.constantTimeAreEqual(renegExtData, + TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + } + + state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions, + AlertDescription.illegal_parameter); + + securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(serverExtensions); + + state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, + TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter); + + state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, + TlsProtocol.EXT_SessionTicket, AlertDescription.illegal_parameter); + } + + state.client.notifySecureRenegotiation(state.secure_renegotiation); + + if (state.clientExtensions != null) + { + state.client.processServerExtensions(serverExtensions); + } + } + + protected void processServerKeyExchange(ClientHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + state.keyExchange.processServerKeyExchange(buf); + + TlsProtocol.assertEmpty(buf); + } + + protected void processServerSupplementalData(ClientHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + Vector serverSupplementalData = TlsProtocol.readSupplementalDataMessage(buf); + state.client.processServerSupplementalData(serverSupplementalData); + } + + protected void reportServerVersion(ClientHandshakeState state, ProtocolVersion server_version) + throws IOException + { + TlsClientContextImpl clientContext = state.clientContext; + ProtocolVersion currentServerVersion = clientContext.getServerVersion(); + if (null == currentServerVersion) + { + clientContext.setServerVersion(server_version); + state.client.notifyServerVersion(server_version); + } + else if (!currentServerVersion.equals(server_version)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + protected static byte[] patchClientHelloWithCookie(byte[] clientHelloBody, byte[] cookie) + throws IOException + { + int sessionIDPos = 34; + int sessionIDLength = TlsUtils.readUint8(clientHelloBody, sessionIDPos); + + int cookieLengthPos = sessionIDPos + 1 + sessionIDLength; + int cookiePos = cookieLengthPos + 1; + + byte[] patched = new byte[clientHelloBody.length + cookie.length]; + System.arraycopy(clientHelloBody, 0, patched, 0, cookieLengthPos); + TlsUtils.checkUint8(cookie.length); + TlsUtils.writeUint8(cookie.length, patched, cookieLengthPos); + System.arraycopy(cookie, 0, patched, cookiePos, cookie.length); + System.arraycopy(clientHelloBody, cookiePos, patched, cookiePos + cookie.length, clientHelloBody.length + - cookiePos); + + return patched; + } + + protected static class ClientHandshakeState + { + TlsClient client = null; + TlsClientContextImpl clientContext = null; + TlsSession tlsSession = null; + SessionParameters sessionParameters = null; + SessionParameters.Builder sessionParametersBuilder = null; + int[] offeredCipherSuites = null; + short[] offeredCompressionMethods = null; + Hashtable clientExtensions = null; + byte[] selectedSessionID = null; + int selectedCipherSuite = -1; + short selectedCompressionMethod = -1; + boolean secure_renegotiation = false; + short maxFragmentLength = -1; + boolean allowCertificateStatus = false; + boolean expectSessionTicket = false; + TlsKeyExchange keyExchange = null; + TlsAuthentication authentication = null; + CertificateStatus certificateStatus = null; + CertificateRequest certificateRequest = null; + TlsCredentials clientCredentials = null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSEpoch.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSEpoch.java new file mode 100644 index 000000000..6d87f9a5c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSEpoch.java @@ -0,0 +1,53 @@ +package org.spongycastle.crypto.tls; + +class DTLSEpoch +{ + + private final DTLSReplayWindow replayWindow = new DTLSReplayWindow(); + + private final int epoch; + private final TlsCipher cipher; + + private long sequence_number = 0; + + DTLSEpoch(int epoch, TlsCipher cipher) + { + if (epoch < 0) + { + throw new IllegalArgumentException("'epoch' must be >= 0"); + } + if (cipher == null) + { + throw new IllegalArgumentException("'cipher' cannot be null"); + } + + this.epoch = epoch; + this.cipher = cipher; + } + + long allocateSequenceNumber() + { + // TODO Check for overflow + return sequence_number++; + } + + TlsCipher getCipher() + { + return cipher; + } + + int getEpoch() + { + return epoch; + } + + DTLSReplayWindow getReplayWindow() + { + return replayWindow; + } + + long getSequence_number() + { + return sequence_number; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSHandshakeRetransmit.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSHandshakeRetransmit.java new file mode 100644 index 000000000..f0b0086cc --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSHandshakeRetransmit.java @@ -0,0 +1,9 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +interface DTLSHandshakeRetransmit +{ + void receivedHandshakeRecord(int epoch, byte[] buf, int off, int len) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSProtocol.java new file mode 100644 index 000000000..60ca4fe1f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSProtocol.java @@ -0,0 +1,90 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.util.Arrays; + +public abstract class DTLSProtocol +{ + protected final SecureRandom secureRandom; + + protected DTLSProtocol(SecureRandom secureRandom) + { + if (secureRandom == null) + { + throw new IllegalArgumentException("'secureRandom' cannot be null"); + } + + this.secureRandom = secureRandom; + } + + protected void processFinished(byte[] body, byte[] expected_verify_data) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf); + + TlsProtocol.assertEmpty(buf); + + if (!Arrays.constantTimeAreEqual(expected_verify_data, verify_data)) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + protected static short evaluateMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions, + short alertDescription) throws IOException + { + short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions); + if (maxFragmentLength >= 0 && maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions)) + { + throw new TlsFatalAlert(alertDescription); + } + return maxFragmentLength; + } + + protected static byte[] generateCertificate(Certificate certificate) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + certificate.encode(buf); + return buf.toByteArray(); + } + + protected static byte[] generateSupplementalData(Vector supplementalData) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + TlsProtocol.writeSupplementalData(buf, supplementalData); + return buf.toByteArray(); + } + + protected static void validateSelectedCipherSuite(int selectedCipherSuite, short alertDescription) + throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5: + case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + // TODO Alert + throw new IllegalStateException("RC4 MUST NOT be used with DTLS"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReassembler.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReassembler.java new file mode 100644 index 000000000..d88b636c6 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReassembler.java @@ -0,0 +1,136 @@ +package org.spongycastle.crypto.tls; + +import java.util.Vector; + +class DTLSReassembler +{ + + private final short msg_type; + private final byte[] body; + + private Vector missing = new Vector(); + + DTLSReassembler(short msg_type, int length) + { + this.msg_type = msg_type; + this.body = new byte[length]; + this.missing.addElement(new Range(0, length)); + } + + short getType() + { + return msg_type; + } + + byte[] getBodyIfComplete() + { + return missing.isEmpty() ? body : null; + } + + void contributeFragment(short msg_type, int length, byte[] buf, int off, int fragment_offset, + int fragment_length) + { + + int fragment_end = fragment_offset + fragment_length; + + if (this.msg_type != msg_type || this.body.length != length || fragment_end > length) + { + return; + } + + if (fragment_length == 0) + { + // NOTE: Empty messages still require an empty fragment to complete it + if (fragment_offset == 0 && !missing.isEmpty()) + { + Range firstRange = (Range)missing.firstElement(); + if (firstRange.getEnd() == 0) + { + missing.removeElementAt(0); + } + } + return; + } + + for (int i = 0; i < missing.size(); ++i) + { + Range range = (Range)missing.elementAt(i); + if (range.getStart() >= fragment_end) + { + break; + } + if (range.getEnd() > fragment_offset) + { + + int copyStart = Math.max(range.getStart(), fragment_offset); + int copyEnd = Math.min(range.getEnd(), fragment_end); + int copyLength = copyEnd - copyStart; + + System.arraycopy(buf, off + copyStart - fragment_offset, body, copyStart, + copyLength); + + if (copyStart == range.getStart()) + { + if (copyEnd == range.getEnd()) + { + missing.removeElementAt(i--); + } + else + { + range.setStart(copyEnd); + } + } + else + { + if (copyEnd == range.getEnd()) + { + range.setEnd(copyStart); + } + else + { + missing.insertElementAt(new Range(copyEnd, range.getEnd()), ++i); + range.setEnd(copyStart); + } + } + } + } + } + + void reset() + { + this.missing.removeAllElements(); + this.missing.addElement(new Range(0, body.length)); + } + + private static class Range + { + + private int start, end; + + Range(int start, int end) + { + this.start = start; + this.end = end; + } + + public int getStart() + { + return start; + } + + public void setStart(int start) + { + this.start = start; + } + + public int getEnd() + { + return end; + } + + public void setEnd(int end) + { + this.end = end; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java new file mode 100644 index 000000000..6586599fb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java @@ -0,0 +1,520 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +class DTLSRecordLayer + implements DatagramTransport +{ + private static final int RECORD_HEADER_LENGTH = 13; + private static final int MAX_FRAGMENT_LENGTH = 1 << 14; + private static final long TCP_MSL = 1000L * 60 * 2; + private static final long RETRANSMIT_TIMEOUT = TCP_MSL * 2; + + private final DatagramTransport transport; + private final TlsContext context; + private final TlsPeer peer; + + private final ByteQueue recordQueue = new ByteQueue(); + + private volatile boolean closed = false; + private volatile boolean failed = false; + private volatile ProtocolVersion discoveredPeerVersion = null; + private volatile boolean inHandshake; + private volatile int plaintextLimit; + private DTLSEpoch currentEpoch, pendingEpoch; + private DTLSEpoch readEpoch, writeEpoch; + + private DTLSHandshakeRetransmit retransmit = null; + private DTLSEpoch retransmitEpoch = null; + private long retransmitExpiry = 0; + + DTLSRecordLayer(DatagramTransport transport, TlsContext context, TlsPeer peer, short contentType) + { + this.transport = transport; + this.context = context; + this.peer = peer; + + this.inHandshake = true; + + this.currentEpoch = new DTLSEpoch(0, new TlsNullCipher(context)); + this.pendingEpoch = null; + this.readEpoch = currentEpoch; + this.writeEpoch = currentEpoch; + + setPlaintextLimit(MAX_FRAGMENT_LENGTH); + } + + void setPlaintextLimit(int plaintextLimit) + { + this.plaintextLimit = plaintextLimit; + } + + ProtocolVersion getDiscoveredPeerVersion() + { + return discoveredPeerVersion; + } + + ProtocolVersion resetDiscoveredPeerVersion() + { + ProtocolVersion result = discoveredPeerVersion; + discoveredPeerVersion = null; + return result; + } + + void initPendingEpoch(TlsCipher pendingCipher) + { + if (pendingEpoch != null) + { + throw new IllegalStateException(); + } + + /* + * TODO "In order to ensure that any given sequence/epoch pair is unique, implementations + * MUST NOT allow the same epoch value to be reused within two times the TCP maximum segment + * lifetime." + */ + + // TODO Check for overflow + this.pendingEpoch = new DTLSEpoch(writeEpoch.getEpoch() + 1, pendingCipher); + } + + void handshakeSuccessful(DTLSHandshakeRetransmit retransmit) + { + if (readEpoch == currentEpoch || writeEpoch == currentEpoch) + { + // TODO + throw new IllegalStateException(); + } + + if (retransmit != null) + { + this.retransmit = retransmit; + this.retransmitEpoch = currentEpoch; + this.retransmitExpiry = System.currentTimeMillis() + RETRANSMIT_TIMEOUT; + } + + this.inHandshake = false; + this.currentEpoch = pendingEpoch; + this.pendingEpoch = null; + } + + void resetWriteEpoch() + { + if (retransmitEpoch != null) + { + this.writeEpoch = retransmitEpoch; + } + else + { + this.writeEpoch = currentEpoch; + } + } + + public int getReceiveLimit() + throws IOException + { + return Math.min(this.plaintextLimit, + readEpoch.getCipher().getPlaintextLimit(transport.getReceiveLimit() - RECORD_HEADER_LENGTH)); + } + + public int getSendLimit() + throws IOException + { + return Math.min(this.plaintextLimit, + writeEpoch.getCipher().getPlaintextLimit(transport.getSendLimit() - RECORD_HEADER_LENGTH)); + } + + public int receive(byte[] buf, int off, int len, int waitMillis) + throws IOException + { + byte[] record = null; + + for (;;) + { + int receiveLimit = Math.min(len, getReceiveLimit()) + RECORD_HEADER_LENGTH; + if (record == null || record.length < receiveLimit) + { + record = new byte[receiveLimit]; + } + + try + { + if (retransmit != null && System.currentTimeMillis() > retransmitExpiry) + { + retransmit = null; + retransmitEpoch = null; + } + + int received = receiveRecord(record, 0, receiveLimit, waitMillis); + if (received < 0) + { + return received; + } + if (received < RECORD_HEADER_LENGTH) + { + continue; + } + int length = TlsUtils.readUint16(record, 11); + if (received != (length + RECORD_HEADER_LENGTH)) + { + continue; + } + + short type = TlsUtils.readUint8(record, 0); + + // TODO Support user-specified custom protocols? + switch (type) + { + case ContentType.alert: + case ContentType.application_data: + case ContentType.change_cipher_spec: + case ContentType.handshake: + case ContentType.heartbeat: + break; + default: + // TODO Exception? + continue; + } + + int epoch = TlsUtils.readUint16(record, 3); + + DTLSEpoch recordEpoch = null; + if (epoch == readEpoch.getEpoch()) + { + recordEpoch = readEpoch; + } + else if (type == ContentType.handshake && retransmitEpoch != null + && epoch == retransmitEpoch.getEpoch()) + { + recordEpoch = retransmitEpoch; + } + + if (recordEpoch == null) + { + continue; + } + + long seq = TlsUtils.readUint48(record, 5); + if (recordEpoch.getReplayWindow().shouldDiscard(seq)) + { + continue; + } + + ProtocolVersion version = TlsUtils.readVersion(record, 1); + if (discoveredPeerVersion != null && !discoveredPeerVersion.equals(version)) + { + continue; + } + + byte[] plaintext = recordEpoch.getCipher().decodeCiphertext( + getMacSequenceNumber(recordEpoch.getEpoch(), seq), type, record, RECORD_HEADER_LENGTH, + received - RECORD_HEADER_LENGTH); + + recordEpoch.getReplayWindow().reportAuthenticated(seq); + + if (plaintext.length > this.plaintextLimit) + { + continue; + } + + if (discoveredPeerVersion == null) + { + discoveredPeerVersion = version; + } + + switch (type) + { + case ContentType.alert: + { + if (plaintext.length == 2) + { + short alertLevel = plaintext[0]; + short alertDescription = plaintext[1]; + + peer.notifyAlertReceived(alertLevel, alertDescription); + + if (alertLevel == AlertLevel.fatal) + { + fail(alertDescription); + throw new TlsFatalAlert(alertDescription); + } + + // TODO Can close_notify be a fatal alert? + if (alertDescription == AlertDescription.close_notify) + { + closeTransport(); + } + } + else + { + // TODO What exception? + } + + continue; + } + case ContentType.application_data: + { + if (inHandshake) + { + // TODO Consider buffering application data for new epoch that arrives + // out-of-order with the Finished message + continue; + } + break; + } + case ContentType.change_cipher_spec: + { + // Implicitly receive change_cipher_spec and change to pending cipher state + + for (int i = 0; i < plaintext.length; ++i) + { + short message = TlsUtils.readUint8(plaintext, i); + if (message != ChangeCipherSpec.change_cipher_spec) + { + continue; + } + + if (pendingEpoch != null) + { + readEpoch = pendingEpoch; + } + } + + continue; + } + case ContentType.handshake: + { + if (!inHandshake) + { + if (retransmit != null) + { + retransmit.receivedHandshakeRecord(epoch, plaintext, 0, plaintext.length); + } + + // TODO Consider support for HelloRequest + continue; + } + break; + } + case ContentType.heartbeat: + { + // TODO[RFC 6520] + continue; + } + } + + /* + * NOTE: If we receive any non-handshake data in the new epoch implies the peer has + * received our final flight. + */ + if (!inHandshake && retransmit != null) + { + this.retransmit = null; + this.retransmitEpoch = null; + } + + System.arraycopy(plaintext, 0, buf, off, plaintext.length); + return plaintext.length; + } + catch (IOException e) + { + // NOTE: Assume this is a timeout for the moment + throw e; + } + } + } + + public void send(byte[] buf, int off, int len) + throws IOException + { + short contentType = ContentType.application_data; + + if (this.inHandshake || this.writeEpoch == this.retransmitEpoch) + { + contentType = ContentType.handshake; + + short handshakeType = TlsUtils.readUint8(buf, off); + if (handshakeType == HandshakeType.finished) + { + DTLSEpoch nextEpoch = null; + if (this.inHandshake) + { + nextEpoch = pendingEpoch; + } + else if (this.writeEpoch == this.retransmitEpoch) + { + nextEpoch = currentEpoch; + } + + if (nextEpoch == null) + { + // TODO + throw new IllegalStateException(); + } + + // Implicitly send change_cipher_spec and change to pending cipher state + + // TODO Send change_cipher_spec and finished records in single datagram? + byte[] data = new byte[]{ 1 }; + sendRecord(ContentType.change_cipher_spec, data, 0, data.length); + + writeEpoch = nextEpoch; + } + } + + sendRecord(contentType, buf, off, len); + } + + public void close() + throws IOException + { + if (!closed) + { + if (inHandshake) + { + warn(AlertDescription.user_canceled, "User canceled handshake"); + } + closeTransport(); + } + } + + void fail(short alertDescription) + { + if (!closed) + { + try + { + raiseAlert(AlertLevel.fatal, alertDescription, null, null); + } + catch (Exception e) + { + // Ignore + } + + failed = true; + + closeTransport(); + } + } + + void warn(short alertDescription, String message) + throws IOException + { + raiseAlert(AlertLevel.warning, alertDescription, message, null); + } + + private void closeTransport() + { + if (!closed) + { + /* + * RFC 5246 7.2.1. Unless some other fatal alert has been transmitted, each party is + * required to send a close_notify alert before closing the write side of the + * connection. The other party MUST respond with a close_notify alert of its own and + * close down the connection immediately, discarding any pending writes. + */ + + try + { + if (!failed) + { + warn(AlertDescription.close_notify, null); + } + transport.close(); + } + catch (Exception e) + { + // Ignore + } + + closed = true; + } + } + + private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause) + throws IOException + { + peer.notifyAlertRaised(alertLevel, alertDescription, message, cause); + + byte[] error = new byte[2]; + error[0] = (byte)alertLevel; + error[1] = (byte)alertDescription; + + sendRecord(ContentType.alert, error, 0, 2); + } + + private int receiveRecord(byte[] buf, int off, int len, int waitMillis) + throws IOException + { + if (recordQueue.size() > 0) + { + int length = 0; + if (recordQueue.size() >= RECORD_HEADER_LENGTH) + { + byte[] lengthBytes = new byte[2]; + recordQueue.read(lengthBytes, 0, 2, 11); + length = TlsUtils.readUint16(lengthBytes, 0); + } + + int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length); + recordQueue.removeData(buf, off, received, 0); + return received; + } + + int received = transport.receive(buf, off, len, waitMillis); + if (received >= RECORD_HEADER_LENGTH) + { + int fragmentLength = TlsUtils.readUint16(buf, off + 11); + int recordLength = RECORD_HEADER_LENGTH + fragmentLength; + if (received > recordLength) + { + recordQueue.addData(buf, off + recordLength, received - recordLength); + received = recordLength; + } + } + + return received; + } + + private void sendRecord(short contentType, byte[] buf, int off, int len) + throws IOException + { + if (len > this.plaintextLimit) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (len < 1 && contentType != ContentType.application_data) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + int recordEpoch = writeEpoch.getEpoch(); + long recordSequenceNumber = writeEpoch.allocateSequenceNumber(); + + byte[] ciphertext = writeEpoch.getCipher().encodePlaintext( + getMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len); + + // TODO Check the ciphertext length? + + byte[] record = new byte[ciphertext.length + RECORD_HEADER_LENGTH]; + TlsUtils.writeUint8(contentType, record, 0); + ProtocolVersion version = discoveredPeerVersion != null ? discoveredPeerVersion : context.getClientVersion(); + TlsUtils.writeVersion(version, record, 1); + TlsUtils.writeUint16(recordEpoch, record, 3); + TlsUtils.writeUint48(recordSequenceNumber, record, 5); + TlsUtils.writeUint16(ciphertext.length, record, 11); + System.arraycopy(ciphertext, 0, record, RECORD_HEADER_LENGTH, ciphertext.length); + + transport.send(record, 0, record.length); + } + + private static long getMacSequenceNumber(int epoch, long sequence_number) + { + return ((long)epoch << 48) | sequence_number; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReliableHandshake.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReliableHandshake.java new file mode 100644 index 000000000..df03fe264 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReliableHandshake.java @@ -0,0 +1,456 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.util.Integers; + +class DTLSReliableHandshake +{ + private final static int MAX_RECEIVE_AHEAD = 10; + + private final DTLSRecordLayer recordLayer; + + private TlsHandshakeHash handshakeHash; + + private Hashtable currentInboundFlight = new Hashtable(); + private Hashtable previousInboundFlight = null; + private Vector outboundFlight = new Vector(); + private boolean sending = true; + + private int message_seq = 0, next_receive_seq = 0; + + DTLSReliableHandshake(TlsContext context, DTLSRecordLayer transport) + { + this.recordLayer = transport; + this.handshakeHash = new DeferredHash(); + this.handshakeHash.init(context); + } + + void notifyHelloComplete() + { + this.handshakeHash = handshakeHash.notifyPRFDetermined(); + } + + TlsHandshakeHash getHandshakeHash() + { + return handshakeHash; + } + + TlsHandshakeHash prepareToFinish() + { + TlsHandshakeHash result = handshakeHash; + this.handshakeHash = handshakeHash.stopTracking(); + return result; + } + + void sendMessage(short msg_type, byte[] body) + throws IOException + { + TlsUtils.checkUint24(body.length); + + if (!sending) + { + checkInboundFlight(); + sending = true; + outboundFlight.removeAllElements(); + } + + Message message = new Message(message_seq++, msg_type, body); + + outboundFlight.addElement(message); + + writeMessage(message); + updateHandshakeMessagesDigest(message); + } + + byte[] receiveMessageBody(short msg_type) + throws IOException + { + Message message = receiveMessage(); + if (message.getType() != msg_type) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + return message.getBody(); + } + + Message receiveMessage() + throws IOException + { + if (sending) + { + sending = false; + prepareInboundFlight(); + } + + // Check if we already have the next message waiting + { + DTLSReassembler next = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(next_receive_seq)); + if (next != null) + { + byte[] body = next.getBodyIfComplete(); + if (body != null) + { + previousInboundFlight = null; + return updateHandshakeMessagesDigest(new Message(next_receive_seq++, next.getType(), body)); + } + } + } + + byte[] buf = null; + + // TODO Check the conditions under which we should reset this + int readTimeoutMillis = 1000; + + for (; ; ) + { + int receiveLimit = recordLayer.getReceiveLimit(); + if (buf == null || buf.length < receiveLimit) + { + buf = new byte[receiveLimit]; + } + + // TODO Handle records containing multiple handshake messages + + try + { + for (; ; ) + { + int received = recordLayer.receive(buf, 0, receiveLimit, readTimeoutMillis); + if (received < 0) + { + break; + } + if (received < 12) + { + continue; + } + int fragment_length = TlsUtils.readUint24(buf, 9); + if (received != (fragment_length + 12)) + { + continue; + } + int seq = TlsUtils.readUint16(buf, 4); + if (seq > (next_receive_seq + MAX_RECEIVE_AHEAD)) + { + continue; + } + short msg_type = TlsUtils.readUint8(buf, 0); + int length = TlsUtils.readUint24(buf, 1); + int fragment_offset = TlsUtils.readUint24(buf, 6); + if (fragment_offset + fragment_length > length) + { + continue; + } + + if (seq < next_receive_seq) + { + /* + * NOTE: If we receive the previous flight of incoming messages in full + * again, retransmit our last flight + */ + if (previousInboundFlight != null) + { + DTLSReassembler reassembler = (DTLSReassembler)previousInboundFlight.get(Integers + .valueOf(seq)); + if (reassembler != null) + { + + reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset, + fragment_length); + + if (checkAll(previousInboundFlight)) + { + + resendOutboundFlight(); + + /* + * TODO[DTLS] implementations SHOULD back off handshake packet + * size during the retransmit backoff. + */ + readTimeoutMillis = Math.min(readTimeoutMillis * 2, 60000); + + resetAll(previousInboundFlight); + } + } + } + } + else + { + + DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq)); + if (reassembler == null) + { + reassembler = new DTLSReassembler(msg_type, length); + currentInboundFlight.put(Integers.valueOf(seq), reassembler); + } + + reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset, fragment_length); + + if (seq == next_receive_seq) + { + byte[] body = reassembler.getBodyIfComplete(); + if (body != null) + { + previousInboundFlight = null; + return updateHandshakeMessagesDigest(new Message(next_receive_seq++, + reassembler.getType(), body)); + } + } + } + } + } + catch (IOException e) + { + // NOTE: Assume this is a timeout for the moment + } + + resendOutboundFlight(); + + /* + * TODO[DTLS] implementations SHOULD back off handshake packet size during the + * retransmit backoff. + */ + readTimeoutMillis = Math.min(readTimeoutMillis * 2, 60000); + } + } + + void finish() + { + DTLSHandshakeRetransmit retransmit = null; + if (!sending) + { + checkInboundFlight(); + } + else if (currentInboundFlight != null) + { + /* + * RFC 6347 4.2.4. In addition, for at least twice the default MSL defined for [TCP], + * when in the FINISHED state, the node that transmits the last flight (the server in an + * ordinary handshake or the client in a resumed handshake) MUST respond to a retransmit + * of the peer's last flight with a retransmit of the last flight. + */ + retransmit = new DTLSHandshakeRetransmit() + { + public void receivedHandshakeRecord(int epoch, byte[] buf, int off, int len) + throws IOException + { + /* + * TODO Need to handle the case where the previous inbound flight contains + * messages from two epochs. + */ + if (len < 12) + { + return; + } + int fragment_length = TlsUtils.readUint24(buf, off + 9); + if (len != (fragment_length + 12)) + { + return; + } + int seq = TlsUtils.readUint16(buf, off + 4); + if (seq >= next_receive_seq) + { + return; + } + + short msg_type = TlsUtils.readUint8(buf, off); + + // TODO This is a hack that only works until we try to support renegotiation + int expectedEpoch = msg_type == HandshakeType.finished ? 1 : 0; + if (epoch != expectedEpoch) + { + return; + } + + int length = TlsUtils.readUint24(buf, off + 1); + int fragment_offset = TlsUtils.readUint24(buf, off + 6); + if (fragment_offset + fragment_length > length) + { + return; + } + + DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq)); + if (reassembler != null) + { + reassembler.contributeFragment(msg_type, length, buf, off + 12, fragment_offset, + fragment_length); + if (checkAll(currentInboundFlight)) + { + resendOutboundFlight(); + resetAll(currentInboundFlight); + } + } + } + }; + } + + recordLayer.handshakeSuccessful(retransmit); + } + + void resetHandshakeMessagesDigest() + { + handshakeHash.reset(); + } + + /** + * Check that there are no "extra" messages left in the current inbound flight + */ + private void checkInboundFlight() + { + Enumeration e = currentInboundFlight.keys(); + while (e.hasMoreElements()) + { + Integer key = (Integer)e.nextElement(); + if (key.intValue() >= next_receive_seq) + { + // TODO Should this be considered an error? + } + } + } + + private void prepareInboundFlight() + { + resetAll(currentInboundFlight); + previousInboundFlight = currentInboundFlight; + currentInboundFlight = new Hashtable(); + } + + private void resendOutboundFlight() + throws IOException + { + recordLayer.resetWriteEpoch(); + for (int i = 0; i < outboundFlight.size(); ++i) + { + writeMessage((Message)outboundFlight.elementAt(i)); + } + } + + private Message updateHandshakeMessagesDigest(Message message) + throws IOException + { + if (message.getType() != HandshakeType.hello_request) + { + byte[] body = message.getBody(); + byte[] buf = new byte[12]; + TlsUtils.writeUint8(message.getType(), buf, 0); + TlsUtils.writeUint24(body.length, buf, 1); + TlsUtils.writeUint16(message.getSeq(), buf, 4); + TlsUtils.writeUint24(0, buf, 6); + TlsUtils.writeUint24(body.length, buf, 9); + handshakeHash.update(buf, 0, buf.length); + handshakeHash.update(body, 0, body.length); + } + return message; + } + + private void writeMessage(Message message) + throws IOException + { + int sendLimit = recordLayer.getSendLimit(); + int fragmentLimit = sendLimit - 12; + + // TODO Support a higher minimum fragment size? + if (fragmentLimit < 1) + { + // TODO Should we be throwing an exception here? + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + int length = message.getBody().length; + + // NOTE: Must still send a fragment if body is empty + int fragment_offset = 0; + do + { + int fragment_length = Math.min(length - fragment_offset, fragmentLimit); + writeHandshakeFragment(message, fragment_offset, fragment_length); + fragment_offset += fragment_length; + } + while (fragment_offset < length); + } + + private void writeHandshakeFragment(Message message, int fragment_offset, int fragment_length) + throws IOException + { + RecordLayerBuffer fragment = new RecordLayerBuffer(12 + fragment_length); + TlsUtils.writeUint8(message.getType(), fragment); + TlsUtils.writeUint24(message.getBody().length, fragment); + TlsUtils.writeUint16(message.getSeq(), fragment); + TlsUtils.writeUint24(fragment_offset, fragment); + TlsUtils.writeUint24(fragment_length, fragment); + fragment.write(message.getBody(), fragment_offset, fragment_length); + + fragment.sendToRecordLayer(recordLayer); + } + + private static boolean checkAll(Hashtable inboundFlight) + { + Enumeration e = inboundFlight.elements(); + while (e.hasMoreElements()) + { + if (((DTLSReassembler)e.nextElement()).getBodyIfComplete() == null) + { + return false; + } + } + return true; + } + + private static void resetAll(Hashtable inboundFlight) + { + Enumeration e = inboundFlight.elements(); + while (e.hasMoreElements()) + { + ((DTLSReassembler)e.nextElement()).reset(); + } + } + + static class Message + { + private final int message_seq; + private final short msg_type; + private final byte[] body; + + private Message(int message_seq, short msg_type, byte[] body) + { + this.message_seq = message_seq; + this.msg_type = msg_type; + this.body = body; + } + + public int getSeq() + { + return message_seq; + } + + public short getType() + { + return msg_type; + } + + public byte[] getBody() + { + return body; + } + } + + static class RecordLayerBuffer extends ByteArrayOutputStream + { + RecordLayerBuffer(int size) + { + super(size); + } + + void sendToRecordLayer(DTLSRecordLayer recordLayer) throws IOException + { + recordLayer.send(buf, 0, count); + buf = null; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReplayWindow.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReplayWindow.java new file mode 100644 index 000000000..1ce21b5b3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSReplayWindow.java @@ -0,0 +1,91 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4347 4.1.2.5 Anti-replay + * + * Support fast rejection of duplicate records by maintaining a sliding receive window + */ +class DTLSReplayWindow +{ + + private static final long VALID_SEQ_MASK = 0x0000FFFFFFFFFFFFL; + + private static final long WINDOW_SIZE = 64L; + + private long latestConfirmedSeq = -1; + private long bitmap = 0; + + /** + * Check whether a received record with the given sequence number should be rejected as a duplicate. + * + * @param seq the 48-bit DTLSPlainText.sequence_number field of a received record. + * @return true if the record should be discarded without further processing. + */ + boolean shouldDiscard(long seq) + { + if ((seq & VALID_SEQ_MASK) != seq) + { + return true; + } + + if (seq <= latestConfirmedSeq) + { + long diff = latestConfirmedSeq - seq; + if (diff >= WINDOW_SIZE) + { + return true; + } + if ((bitmap & (1L << diff)) != 0) + { + return true; + } + } + + return false; + } + + /** + * Report that a received record with the given sequence number passed authentication checks. + * + * @param seq the 48-bit DTLSPlainText.sequence_number field of an authenticated record. + */ + void reportAuthenticated(long seq) + { + if ((seq & VALID_SEQ_MASK) != seq) + { + throw new IllegalArgumentException("'seq' out of range"); + } + + if (seq <= latestConfirmedSeq) + { + long diff = latestConfirmedSeq - seq; + if (diff < WINDOW_SIZE) + { + bitmap |= (1L << diff); + } + } + else + { + long diff = seq - latestConfirmedSeq; + if (diff >= WINDOW_SIZE) + { + bitmap = 1; + } + else + { + bitmap <<= (int)diff; // for earlier JDKs + bitmap |= 1; + } + latestConfirmedSeq = seq; + } + } + + /** + * When a new epoch begins, sequence numbers begin again at 0 + */ + void reset() + { + latestConfirmedSeq = -1; + bitmap = 0; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java new file mode 100644 index 000000000..1d05711e4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java @@ -0,0 +1,649 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.util.Arrays; + +public class DTLSServerProtocol + extends DTLSProtocol +{ + protected boolean verifyRequests = true; + + public DTLSServerProtocol(SecureRandom secureRandom) + { + super(secureRandom); + } + + public boolean getVerifyRequests() + { + return verifyRequests; + } + + public void setVerifyRequests(boolean verifyRequests) + { + this.verifyRequests = verifyRequests; + } + + public DTLSTransport accept(TlsServer server, DatagramTransport transport) + throws IOException + { + if (server == null) + { + throw new IllegalArgumentException("'server' cannot be null"); + } + if (transport == null) + { + throw new IllegalArgumentException("'transport' cannot be null"); + } + + SecurityParameters securityParameters = new SecurityParameters(); + securityParameters.entity = ConnectionEnd.server; + securityParameters.serverRandom = TlsProtocol.createRandomBlock(secureRandom); + + ServerHandshakeState state = new ServerHandshakeState(); + state.server = server; + state.serverContext = new TlsServerContextImpl(secureRandom, securityParameters); + server.init(state.serverContext); + + DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.serverContext, server, ContentType.handshake); + + // TODO Need to handle sending of HelloVerifyRequest without entering a full connection + + try + { + return serverHandshake(state, recordLayer); + } + catch (TlsFatalAlert fatalAlert) + { + recordLayer.fail(fatalAlert.getAlertDescription()); + throw fatalAlert; + } + catch (IOException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw e; + } + catch (RuntimeException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer) + throws IOException + { + SecurityParameters securityParameters = state.serverContext.getSecurityParameters(); + DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer); + + DTLSReliableHandshake.Message clientMessage = handshake.receiveMessage(); + + { + // NOTE: After receiving a record from the client, we discover the record layer version + ProtocolVersion client_version = recordLayer.getDiscoveredPeerVersion(); + // TODO Read RFCs for guidance on the expected record layer version number + state.serverContext.setClientVersion(client_version); + } + + if (clientMessage.getType() == HandshakeType.client_hello) + { + processClientHello(state, clientMessage.getBody()); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + { + byte[] serverHelloBody = generateServerHello(state); + + if (state.maxFragmentLength >= 0) + { + int plainTextLimit = 1 << (8 + state.maxFragmentLength); + recordLayer.setPlaintextLimit(plainTextLimit); + } + + securityParameters.cipherSuite = state.selectedCipherSuite; + securityParameters.compressionAlgorithm = state.selectedCompressionMethod; + securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.serverContext, + state.selectedCipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length + * has a verify_data_length equal to 12. This includes all existing cipher suites. + */ + securityParameters.verifyDataLength = 12; + + handshake.sendMessage(HandshakeType.server_hello, serverHelloBody); + } + + handshake.notifyHelloComplete(); + + Vector serverSupplementalData = state.server.getServerSupplementalData(); + if (serverSupplementalData != null) + { + byte[] supplementalDataBody = generateSupplementalData(serverSupplementalData); + handshake.sendMessage(HandshakeType.supplemental_data, supplementalDataBody); + } + + state.keyExchange = state.server.getKeyExchange(); + state.keyExchange.init(state.serverContext); + + state.serverCredentials = state.server.getCredentials(); + + Certificate serverCertificate = null; + + if (state.serverCredentials == null) + { + state.keyExchange.skipServerCredentials(); + } + else + { + state.keyExchange.processServerCredentials(state.serverCredentials); + + serverCertificate = state.serverCredentials.getCertificate(); + byte[] certificateBody = generateCertificate(serverCertificate); + handshake.sendMessage(HandshakeType.certificate, certificateBody); + } + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.isEmpty()) + { + state.allowCertificateStatus = false; + } + + if (state.allowCertificateStatus) + { + CertificateStatus certificateStatus = state.server.getCertificateStatus(); + if (certificateStatus != null) + { + byte[] certificateStatusBody = generateCertificateStatus(state, certificateStatus); + handshake.sendMessage(HandshakeType.certificate_status, certificateStatusBody); + } + } + + byte[] serverKeyExchange = state.keyExchange.generateServerKeyExchange(); + if (serverKeyExchange != null) + { + handshake.sendMessage(HandshakeType.server_key_exchange, serverKeyExchange); + } + + if (state.serverCredentials != null) + { + state.certificateRequest = state.server.getCertificateRequest(); + if (state.certificateRequest != null) + { + state.keyExchange.validateCertificateRequest(state.certificateRequest); + + byte[] certificateRequestBody = generateCertificateRequest(state, state.certificateRequest); + handshake.sendMessage(HandshakeType.certificate_request, certificateRequestBody); + + TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(), + state.certificateRequest.getSupportedSignatureAlgorithms()); + } + } + + handshake.sendMessage(HandshakeType.server_hello_done, TlsUtils.EMPTY_BYTES); + + handshake.getHandshakeHash().sealHashAlgorithms(); + + clientMessage = handshake.receiveMessage(); + + if (clientMessage.getType() == HandshakeType.supplemental_data) + { + processClientSupplementalData(state, clientMessage.getBody()); + clientMessage = handshake.receiveMessage(); + } + else + { + state.server.processClientSupplementalData(null); + } + + if (state.certificateRequest == null) + { + state.keyExchange.skipClientCredentials(); + } + else + { + if (clientMessage.getType() == HandshakeType.certificate) + { + processClientCertificate(state, clientMessage.getBody()); + clientMessage = handshake.receiveMessage(); + } + else + { + if (TlsUtils.isTLSv12(state.serverContext)) + { + /* + * RFC 5246 If no suitable certificate is available, the client MUST send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + notifyClientCertificate(state, Certificate.EMPTY_CHAIN); + } + } + + if (clientMessage.getType() == HandshakeType.client_key_exchange) + { + processClientKeyExchange(state, clientMessage.getBody()); + } + else + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange); + recordLayer.initPendingEpoch(state.server.getCipher()); + + TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish(); + + /* + * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing + * capability (i.e., all certificates except those containing fixed Diffie-Hellman + * parameters). + */ + if (expectCertificateVerifyMessage(state)) + { + byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify); + processCertificateVerify(state, certificateVerifyBody, prepareFinishHash); + } + + // NOTE: Calculated exclusive of the actual Finished message from the client + byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.client_finished, + TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null)); + processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedClientVerifyData); + + if (state.expectSessionTicket) + { + NewSessionTicket newSessionTicket = state.server.getNewSessionTicket(); + byte[] newSessionTicketBody = generateNewSessionTicket(state, newSessionTicket); + handshake.sendMessage(HandshakeType.session_ticket, newSessionTicketBody); + } + + // NOTE: Calculated exclusive of the Finished message itself + byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null)); + handshake.sendMessage(HandshakeType.finished, serverVerifyData); + + handshake.finish(); + + state.server.notifyHandshakeComplete(); + + return new DTLSTransport(recordLayer); + } + + protected byte[] generateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + certificateRequest.encode(buf); + return buf.toByteArray(); + } + + protected byte[] generateCertificateStatus(ServerHandshakeState state, CertificateStatus certificateStatus) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + certificateStatus.encode(buf); + return buf.toByteArray(); + } + + protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + newSessionTicket.encode(buf); + return buf.toByteArray(); + } + + protected byte[] generateServerHello(ServerHandshakeState state) + throws IOException + { + SecurityParameters securityParameters = state.serverContext.getSecurityParameters(); + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + ProtocolVersion server_version = state.server.getServerVersion(); + if (!server_version.isEqualOrEarlierVersionOf(state.serverContext.getClientVersion())) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + // TODO Read RFCs for guidance on the expected record layer version number + // recordStream.setReadVersion(server_version); + // recordStream.setWriteVersion(server_version); + // recordStream.setRestrictReadVersion(true); + state.serverContext.setServerVersion(server_version); + + TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf); + + buf.write(securityParameters.getServerRandom()); + + /* + * The server may return an empty session_id to indicate that the session will not be cached + * and therefore cannot be resumed. + */ + TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf); + + state.selectedCipherSuite = state.server.getSelectedCipherSuite(); + if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite) + || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error); + + state.selectedCompressionMethod = state.server.getSelectedCompressionMethod(); + if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + TlsUtils.writeUint16(state.selectedCipherSuite, buf); + TlsUtils.writeUint8(state.selectedCompressionMethod, buf); + + state.serverExtensions = state.server.getServerExtensions(); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + if (state.secure_renegotiation) + { + byte[] renegExtData = TlsUtils.getExtensionData(state.serverExtensions, TlsProtocol.EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); + + if (noRenegExt) + { + /* + * Note that sending a "renegotiation_info" extension in response to a ClientHello + * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, + * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed + * because the client is signaling its willingness to receive the extension via the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + + /* + * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty + * "renegotiation_info" extension in the ServerHello message. + */ + state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions); + state.serverExtensions.put(TlsProtocol.EXT_RenegotiationInfo, + TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)); + } + } + + if (state.serverExtensions != null) + { + state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, state.serverExtensions, + AlertDescription.internal_error); + + securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(state.serverExtensions); + + state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions, + TlsExtensionsUtils.EXT_status_request, AlertDescription.internal_error); + + state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions, + TlsProtocol.EXT_SessionTicket, AlertDescription.internal_error); + + TlsProtocol.writeExtensions(buf, state.serverExtensions); + } + + return buf.toByteArray(); + } + + protected void notifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate) + throws IOException + { + if (state.certificateRequest == null) + { + throw new IllegalStateException(); + } + + if (state.clientCertificate != null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + state.clientCertificate = clientCertificate; + + if (clientCertificate.isEmpty()) + { + state.keyExchange.skipClientCredentials(); + } + else + { + + /* + * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request + * message was non-empty, one of the certificates in the certificate chain SHOULD be + * issued by one of the listed CAs. + */ + + state.clientCertificateType = TlsUtils.getClientCertificateType(clientCertificate, + state.serverCredentials.getCertificate()); + + state.keyExchange.processClientCertificate(clientCertificate); + } + + /* + * RFC 5246 7.4.6. If the client does not send any certificates, the server MAY at its + * discretion either continue the handshake without client authentication, or respond with a + * fatal handshake_failure alert. Also, if some aspect of the certificate chain was + * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its + * discretion either continue the handshake (considering the client unauthenticated) or send + * a fatal alert. + */ + state.server.notifyClientCertificate(clientCertificate); + } + + protected void processClientCertificate(ServerHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + Certificate clientCertificate = Certificate.parse(buf); + + TlsProtocol.assertEmpty(buf); + + notifyClientCertificate(state, clientCertificate); + } + + protected void processCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + DigitallySigned clientCertificateVerify = DigitallySigned.parse(state.serverContext, buf); + + TlsProtocol.assertEmpty(buf); + + // Verify the CertificateVerify message contains a correct signature. + try + { + // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned + byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null); + + org.spongycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); + + TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType); + tlsSigner.init(state.serverContext); + tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(), + clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + + protected void processClientHello(ServerHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + // TODO Read RFCs for guidance on the expected record layer version number + ProtocolVersion client_version = TlsUtils.readVersion(buf); + if (!client_version.isDTLS()) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + /* + * Read the client random + */ + byte[] client_random = TlsUtils.readFully(32, buf); + + byte[] sessionID = TlsUtils.readOpaque8(buf); + if (sessionID.length > 32) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347 + byte[] cookie = TlsUtils.readOpaque8(buf); + + int cipher_suites_length = TlsUtils.readUint16(buf); + if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + /* + * NOTE: "If the session_id field is not empty (implying a session resumption request) this + * vector must include at least the cipher_suite from that session." + */ + state.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf); + + int compression_methods_length = TlsUtils.readUint8(buf); + if (compression_methods_length < 1) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + state.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf); + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + state.clientExtensions = TlsProtocol.readExtensions(buf); + + state.serverContext.setClientVersion(client_version); + + state.server.notifyClientVersion(client_version); + + state.serverContext.getSecurityParameters().clientRandom = client_random; + + state.server.notifyOfferedCipherSuites(state.offeredCipherSuites); + state.server.notifyOfferedCompressionMethods(state.offeredCompressionMethods); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + + /* + * When a ClientHello is received, the server MUST check if it includes the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag + * to TRUE. + */ + if (Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + { + state.secure_renegotiation = true; + } + + /* + * The server MUST check if the "renegotiation_info" extension is included in the + * ClientHello. + */ + byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo); + if (renegExtData != null) + { + /* + * If the extension is present, set secure_renegotiation flag to TRUE. The + * server MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake. + */ + state.secure_renegotiation = true; + + if (!Arrays.constantTimeAreEqual(renegExtData, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + } + + state.server.notifySecureRenegotiation(state.secure_renegotiation); + + if (state.clientExtensions != null) + { + state.server.processClientExtensions(state.clientExtensions); + } + } + + protected void processClientKeyExchange(ServerHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + + state.keyExchange.processClientKeyExchange(buf); + + TlsProtocol.assertEmpty(buf); + } + + protected void processClientSupplementalData(ServerHandshakeState state, byte[] body) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(body); + Vector clientSupplementalData = TlsProtocol.readSupplementalDataMessage(buf); + state.server.processClientSupplementalData(clientSupplementalData); + } + + protected boolean expectCertificateVerifyMessage(ServerHandshakeState state) + { + return state.clientCertificateType >= 0 && TlsUtils.hasSigningCapability(state.clientCertificateType); + } + + protected static class ServerHandshakeState + { + TlsServer server = null; + TlsServerContextImpl serverContext = null; + int[] offeredCipherSuites; + short[] offeredCompressionMethods; + Hashtable clientExtensions; + int selectedCipherSuite = -1; + short selectedCompressionMethod = -1; + boolean secure_renegotiation = false; + short maxFragmentLength = -1; + boolean allowCertificateStatus = false; + boolean expectSessionTicket = false; + Hashtable serverExtensions = null; + TlsKeyExchange keyExchange = null; + TlsCredentials serverCredentials = null; + CertificateRequest certificateRequest = null; + short clientCertificateType = -1; + Certificate clientCertificate = null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSTransport.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSTransport.java new file mode 100644 index 000000000..2df9ef7ce --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSTransport.java @@ -0,0 +1,81 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public class DTLSTransport + implements DatagramTransport +{ + + private final DTLSRecordLayer recordLayer; + + DTLSTransport(DTLSRecordLayer recordLayer) + { + this.recordLayer = recordLayer; + } + + public int getReceiveLimit() + throws IOException + { + return recordLayer.getReceiveLimit(); + } + + public int getSendLimit() + throws IOException + { + return recordLayer.getSendLimit(); + } + + public int receive(byte[] buf, int off, int len, int waitMillis) + throws IOException + { + try + { + return recordLayer.receive(buf, off, len, waitMillis); + } + catch (TlsFatalAlert fatalAlert) + { + recordLayer.fail(fatalAlert.getAlertDescription()); + throw fatalAlert; + } + catch (IOException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw e; + } + catch (RuntimeException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public void send(byte[] buf, int off, int len) + throws IOException + { + try + { + recordLayer.send(buf, off, len); + } + catch (TlsFatalAlert fatalAlert) + { + recordLayer.fail(fatalAlert.getAlertDescription()); + throw fatalAlert; + } + catch (IOException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw e; + } + catch (RuntimeException e) + { + recordLayer.fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public void close() + throws IOException + { + recordLayer.close(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DatagramTransport.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DatagramTransport.java new file mode 100644 index 000000000..761029b04 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DatagramTransport.java @@ -0,0 +1,22 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface DatagramTransport +{ + + int getReceiveLimit() + throws IOException; + + int getSendLimit() + throws IOException; + + int receive(byte[] buf, int off, int len, int waitMillis) + throws IOException; + + void send(byte[] buf, int off, int len) + throws IOException; + + void close() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsAgreementCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsAgreementCredentials.java new file mode 100644 index 000000000..be981d8ab --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsAgreementCredentials.java @@ -0,0 +1,78 @@ +package org.spongycastle.crypto.tls; + +import java.math.BigInteger; + +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.agreement.DHBasicAgreement; +import org.spongycastle.crypto.agreement.ECDHBasicAgreement; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.util.BigIntegers; + +public class DefaultTlsAgreementCredentials + extends AbstractTlsAgreementCredentials +{ + protected Certificate certificate; + protected AsymmetricKeyParameter privateKey; + + protected BasicAgreement basicAgreement; + protected boolean truncateAgreement; + + public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey) + { + if (certificate == null) + { + throw new IllegalArgumentException("'certificate' cannot be null"); + } + if (certificate.isEmpty()) + { + throw new IllegalArgumentException("'certificate' cannot be empty"); + } + if (privateKey == null) + { + throw new IllegalArgumentException("'privateKey' cannot be null"); + } + if (!privateKey.isPrivate()) + { + throw new IllegalArgumentException("'privateKey' must be private"); + } + + if (privateKey instanceof DHPrivateKeyParameters) + { + basicAgreement = new DHBasicAgreement(); + truncateAgreement = true; + } + else if (privateKey instanceof ECPrivateKeyParameters) + { + basicAgreement = new ECDHBasicAgreement(); + truncateAgreement = false; + } + else + { + throw new IllegalArgumentException("'privateKey' type not supported: " + + privateKey.getClass().getName()); + } + + this.certificate = certificate; + this.privateKey = privateKey; + } + + public Certificate getCertificate() + { + return certificate; + } + + public byte[] generateAgreement(AsymmetricKeyParameter peerPublicKey) + { + basicAgreement.init(privateKey); + BigInteger agreementValue = basicAgreement.calculateAgreement(peerPublicKey); + + if (truncateAgreement) + { + return BigIntegers.asUnsignedByteArray(agreementValue); + } + + return BigIntegers.asUnsignedByteArray(basicAgreement.getFieldSize(), agreementValue); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsCipherFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsCipherFactory.java new file mode 100644 index 000000000..aa41af0b5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsCipherFactory.java @@ -0,0 +1,218 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.engines.AESFastEngine; +import org.spongycastle.crypto.engines.CamelliaEngine; +import org.spongycastle.crypto.engines.DESedeEngine; +import org.spongycastle.crypto.engines.RC4Engine; +import org.spongycastle.crypto.engines.SEEDEngine; +import org.spongycastle.crypto.engines.Salsa20Engine; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.modes.AEADBlockCipher; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.modes.CCMBlockCipher; +import org.spongycastle.crypto.modes.GCMBlockCipher; + +public class DefaultTlsCipherFactory + extends AbstractTlsCipherFactory +{ + public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) + throws IOException + { + switch (encryptionAlgorithm) + { + case EncryptionAlgorithm._3DES_EDE_CBC: + return createDESedeCipher(context, macAlgorithm); + case EncryptionAlgorithm.AES_128_CBC: + return createAESCipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.AES_128_CCM: + // NOTE: Ignores macAlgorithm + return createCipher_AES_CCM(context, 16, 16); + case EncryptionAlgorithm.AES_128_CCM_8: + // NOTE: Ignores macAlgorithm + return createCipher_AES_CCM(context, 16, 8); + case EncryptionAlgorithm.AES_256_CCM: + // NOTE: Ignores macAlgorithm + return createCipher_AES_CCM(context, 32, 16); + case EncryptionAlgorithm.AES_256_CCM_8: + // NOTE: Ignores macAlgorithm + return createCipher_AES_CCM(context, 32, 8); + case EncryptionAlgorithm.AES_128_GCM: + // NOTE: Ignores macAlgorithm + return createCipher_AES_GCM(context, 16, 16); + case EncryptionAlgorithm.AES_256_CBC: + return createAESCipher(context, 32, macAlgorithm); + case EncryptionAlgorithm.AES_256_GCM: + // NOTE: Ignores macAlgorithm + return createCipher_AES_GCM(context, 32, 16); + case EncryptionAlgorithm.CAMELLIA_128_CBC: + return createCamelliaCipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.CAMELLIA_256_CBC: + return createCamelliaCipher(context, 32, macAlgorithm); + case EncryptionAlgorithm.ESTREAM_SALSA20: + return createSalsa20Cipher(context, 12, 32, macAlgorithm); + case EncryptionAlgorithm.NULL: + return createNullCipher(context, macAlgorithm); + case EncryptionAlgorithm.RC4_128: + return createRC4Cipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.SALSA20: + return createSalsa20Cipher(context, 20, 32, macAlgorithm); + case EncryptionAlgorithm.SEED_CBC: + return createSEEDCipher(context, macAlgorithm); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected TlsBlockCipher createAESCipher(TlsContext context, int cipherKeySize, int macAlgorithm) + throws IOException + { + return new TlsBlockCipher(context, createAESBlockCipher(), createAESBlockCipher(), + createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize); + } + + protected TlsAEADCipher createCipher_AES_CCM(TlsContext context, int cipherKeySize, int macSize) + throws IOException + { + return new TlsAEADCipher(context, createAEADBlockCipher_AES_CCM(), + createAEADBlockCipher_AES_CCM(), cipherKeySize, macSize); + } + + protected TlsAEADCipher createCipher_AES_GCM(TlsContext context, int cipherKeySize, int macSize) + throws IOException + { + return new TlsAEADCipher(context, createAEADBlockCipher_AES_GCM(), + createAEADBlockCipher_AES_GCM(), cipherKeySize, macSize); + } + + protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm) + throws IOException + { + return new TlsBlockCipher(context, createCamelliaBlockCipher(), + createCamelliaBlockCipher(), createHMACDigest(macAlgorithm), + createHMACDigest(macAlgorithm), cipherKeySize); + } + + protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm) + throws IOException + { + return new TlsBlockCipher(context, createDESedeBlockCipher(), createDESedeBlockCipher(), + createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 24); + } + + protected TlsNullCipher createNullCipher(TlsContext context, int macAlgorithm) + throws IOException + { + return new TlsNullCipher(context, createHMACDigest(macAlgorithm), + createHMACDigest(macAlgorithm)); + } + + protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm) + throws IOException + { + return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(), + createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize); + } + + protected TlsStreamCipher createSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm) + throws IOException + { + /* + * TODO To be able to support UMAC96, we need to give the TlsStreamCipher a Mac instead of + * assuming HMAC and passing a digest. + */ + return new TlsStreamCipher(context, createSalsa20StreamCipher(rounds), createSalsa20StreamCipher(rounds), + createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize); + } + + protected TlsBlockCipher createSEEDCipher(TlsContext context, int macAlgorithm) + throws IOException + { + return new TlsBlockCipher(context, createSEEDBlockCipher(), createSEEDBlockCipher(), + createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 16); + } + + protected BlockCipher createAESBlockCipher() + { + return new CBCBlockCipher(new AESFastEngine()); + } + + protected AEADBlockCipher createAEADBlockCipher_AES_CCM() + { + return new CCMBlockCipher(new AESFastEngine()); + } + + protected AEADBlockCipher createAEADBlockCipher_AES_GCM() + { + // TODO Consider allowing custom configuration of multiplier + return new GCMBlockCipher(new AESFastEngine()); + } + + protected BlockCipher createCamelliaBlockCipher() + { + return new CBCBlockCipher(new CamelliaEngine()); + } + + protected BlockCipher createDESedeBlockCipher() + { + return new CBCBlockCipher(new DESedeEngine()); + } + + protected StreamCipher createRC4StreamCipher() + { + return new RC4Engine(); + } + + protected StreamCipher createSalsa20StreamCipher(int rounds) + { + return new Salsa20Engine(rounds); + } + + protected BlockCipher createSEEDBlockCipher() + { + return new CBCBlockCipher(new SEEDEngine()); + } + + protected Digest createHMACDigest(int macAlgorithm) throws IOException + { + switch (macAlgorithm) + { + case MACAlgorithm._null: + return null; + case MACAlgorithm.hmac_md5: + return new MD5Digest(); + case MACAlgorithm.hmac_sha1: + return new SHA1Digest(); + case MACAlgorithm.hmac_sha256: + return new SHA256Digest(); + case MACAlgorithm.hmac_sha384: + return new SHA384Digest(); + case MACAlgorithm.hmac_sha512: + return new SHA512Digest(); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected Mac createMac(int macAlgorithm) throws IOException + { + switch (macAlgorithm) + { + // TODO Need an implementation of UMAC +// case MACAlgorithm.umac96: +// return + default: + return new HMac(createHMACDigest(macAlgorithm)); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsClient.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsClient.java new file mode 100644 index 000000000..603481239 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsClient.java @@ -0,0 +1,378 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public abstract class DefaultTlsClient + extends AbstractTlsClient +{ + public DefaultTlsClient() + { + super(); + } + + public DefaultTlsClient(TlsCipherFactory cipherFactory) + { + super(cipherFactory); + } + + public int[] getCipherSuites() + { + return new int[] { CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA }; + } + + public TlsKeyExchange getKeyExchange() + throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + return createDHKeyExchange(KeyExchangeAlgorithm.DH_DSS); + + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + return createDHKeyExchange(KeyExchangeAlgorithm.DH_RSA); + + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_DSS); + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA); + + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA); + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96: + return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA); + + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96: + return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA); + + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return createRSAKeyExchange(); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public TlsCipher getCipher() + throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96: + return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96); + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96); + + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SEED_CBC, MACAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected TlsKeyExchange createDHKeyExchange(int keyExchange) + { + return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, null); + } + + protected TlsKeyExchange createDHEKeyExchange(int keyExchange) + { + return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, null); + } + + protected TlsKeyExchange createECDHKeyExchange(int keyExchange) + { + return new TlsECDHKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, + serverECPointFormats); + } + + protected TlsKeyExchange createECDHEKeyExchange(int keyExchange) + { + return new TlsECDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, + serverECPointFormats); + } + + protected TlsKeyExchange createRSAKeyExchange() + { + return new TlsRSAKeyExchange(supportedSignatureAlgorithms); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsEncryptionCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsEncryptionCredentials.java new file mode 100644 index 000000000..417405ed8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsEncryptionCredentials.java @@ -0,0 +1,75 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.RSAKeyParameters; + +public class DefaultTlsEncryptionCredentials + extends AbstractTlsEncryptionCredentials +{ + protected TlsContext context; + protected Certificate certificate; + protected AsymmetricKeyParameter privateKey; + + public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate, + AsymmetricKeyParameter privateKey) + { + if (certificate == null) + { + throw new IllegalArgumentException("'certificate' cannot be null"); + } + if (certificate.isEmpty()) + { + throw new IllegalArgumentException("'certificate' cannot be empty"); + } + if (privateKey == null) + { + throw new IllegalArgumentException("'privateKey' cannot be null"); + } + if (!privateKey.isPrivate()) + { + throw new IllegalArgumentException("'privateKey' must be private"); + } + + if (privateKey instanceof RSAKeyParameters) + { + } + else + { + throw new IllegalArgumentException("'privateKey' type not supported: " + + privateKey.getClass().getName()); + } + + this.context = context; + this.certificate = certificate; + this.privateKey = privateKey; + } + + public Certificate getCertificate() + { + return certificate; + } + + public byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret) + throws IOException + { + + PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine()); + encoding.init(false, new ParametersWithRandom(this.privateKey, context.getSecureRandom())); + + try + { + return encoding.processBlock(encryptedPreMasterSecret, 0, + encryptedPreMasterSecret.length); + } + catch (InvalidCipherTextException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsServer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsServer.java new file mode 100644 index 000000000..95cca885f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsServer.java @@ -0,0 +1,455 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.agreement.DHStandardGroups; +import org.spongycastle.crypto.params.DHParameters; + +public abstract class DefaultTlsServer + extends AbstractTlsServer +{ + + public DefaultTlsServer() + { + super(); + } + + public DefaultTlsServer(TlsCipherFactory cipherFactory) + { + super(cipherFactory); + } + + protected TlsEncryptionCredentials getRSAEncryptionCredentials() + throws IOException + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected TlsSignerCredentials getRSASignerCredentials() + throws IOException + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected DHParameters getDHParameters() + { + return DHStandardGroups.rfc5114_1024_160; + } + + protected int[] getCipherSuites() + { + return new int[]{CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,}; + } + + public TlsCredentials getCredentials() + throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return getRSAEncryptionCredentials(); + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + return getRSASignerCredentials(); + + default: + /* + * Note: internal error here; selected a key exchange we don't implement! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public TlsKeyExchange getKeyExchange() + throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + return createDHKeyExchange(KeyExchangeAlgorithm.DH_DSS); + + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + return createDHKeyExchange(KeyExchangeAlgorithm.DH_RSA); + + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_DSS); + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA); + + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA); + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96: + return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA); + + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96: + return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA); + + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return createRSAKeyExchange(); + + default: + /* + * Note: internal error here; selected a key exchange we don't implement! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public TlsCipher getCipher() + throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96: + return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96); + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96); + + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SEED_CBC, MACAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; selected a cipher suite we don't implement! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected TlsKeyExchange createDHKeyExchange(int keyExchange) + { + return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, getDHParameters()); + } + + protected TlsKeyExchange createDHEKeyExchange(int keyExchange) + { + return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, getDHParameters()); + } + + protected TlsKeyExchange createECDHKeyExchange(int keyExchange) + { + return new TlsECDHKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, + serverECPointFormats); + } + + protected TlsKeyExchange createECDHEKeyExchange(int keyExchange) + { + return new TlsECDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, + serverECPointFormats); + } + + protected TlsKeyExchange createRSAKeyExchange() + { + return new TlsRSAKeyExchange(supportedSignatureAlgorithms); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsSignerCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsSignerCredentials.java new file mode 100755 index 000000000..51ac6e882 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DefaultTlsSignerCredentials.java @@ -0,0 +1,104 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; + +public class DefaultTlsSignerCredentials + extends AbstractTlsSignerCredentials +{ + protected TlsContext context; + protected Certificate certificate; + protected AsymmetricKeyParameter privateKey; + protected SignatureAndHashAlgorithm signatureAndHashAlgorithm; + + protected TlsSigner signer; + + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey) + { + this(context, certificate, privateKey, null); + } + + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + if (certificate == null) + { + throw new IllegalArgumentException("'certificate' cannot be null"); + } + if (certificate.isEmpty()) + { + throw new IllegalArgumentException("'certificate' cannot be empty"); + } + if (privateKey == null) + { + throw new IllegalArgumentException("'privateKey' cannot be null"); + } + if (!privateKey.isPrivate()) + { + throw new IllegalArgumentException("'privateKey' must be private"); + } + if (TlsUtils.isTLSv12(context) && signatureAndHashAlgorithm == null) + { + throw new IllegalArgumentException("'signatureAndHashAlgorithm' cannot be null for (D)TLS 1.2+"); + } + + if (privateKey instanceof RSAKeyParameters) + { + this.signer = new TlsRSASigner(); + } + else if (privateKey instanceof DSAPrivateKeyParameters) + { + this.signer = new TlsDSSSigner(); + } + else if (privateKey instanceof ECPrivateKeyParameters) + { + this.signer = new TlsECDSASigner(); + } + else + { + throw new IllegalArgumentException("'privateKey' type not supported: " + privateKey.getClass().getName()); + } + + this.signer.init(context); + + this.context = context; + this.certificate = certificate; + this.privateKey = privateKey; + this.signatureAndHashAlgorithm = signatureAndHashAlgorithm; + } + + public Certificate getCertificate() + { + return certificate; + } + + public byte[] generateCertificateSignature(byte[] hash) + throws IOException + { + try + { + if (TlsUtils.isTLSv12(context)) + { + return signer.generateRawSignature(signatureAndHashAlgorithm, privateKey, hash); + } + else + { + return signer.generateRawSignature(privateKey, hash); + } + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm() + { + return signatureAndHashAlgorithm; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DeferredHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DeferredHash.java new file mode 100644 index 000000000..9ac7d346d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DeferredHash.java @@ -0,0 +1,207 @@ +package org.spongycastle.crypto.tls; + +import java.util.Enumeration; +import java.util.Hashtable; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.util.Shorts; + +/** + * Buffers input until the hash algorithm is determined. + */ +class DeferredHash + implements TlsHandshakeHash +{ + protected static final int BUFFERING_HASH_LIMIT = 4; + + protected TlsContext context; + + private DigestInputBuffer buf; + private Hashtable hashes; + private Short prfHashAlgorithm; + + DeferredHash() + { + this.buf = new DigestInputBuffer(); + this.hashes = new Hashtable(); + this.prfHashAlgorithm = null; + } + + private DeferredHash(Short prfHashAlgorithm, Digest prfHash) + { + this.buf = null; + this.hashes = new Hashtable(); + this.prfHashAlgorithm = prfHashAlgorithm; + hashes.put(prfHashAlgorithm, prfHash); + } + + public void init(TlsContext context) + { + this.context = context; + } + + public TlsHandshakeHash notifyPRFDetermined() + { + int prfAlgorithm = context.getSecurityParameters().getPrfAlgorithm(); + if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy) + { + CombinedHash legacyHash = new CombinedHash(); + legacyHash.init(context); + buf.updateDigest(legacyHash); + return legacyHash.notifyPRFDetermined(); + } + + this.prfHashAlgorithm = Shorts.valueOf(TlsUtils.getHashAlgorithmForPRFAlgorithm(prfAlgorithm)); + + checkTrackingHash(prfHashAlgorithm); + + return this; + } + + public void trackHashAlgorithm(short hashAlgorithm) + { + if (buf == null) + { + throw new IllegalStateException("Too late to track more hash algorithms"); + } + + checkTrackingHash(Shorts.valueOf(hashAlgorithm)); + } + + public void sealHashAlgorithms() + { + checkStopBuffering(); + } + + public TlsHandshakeHash stopTracking() + { + Digest prfHash = TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm)); + if (buf != null) + { + buf.updateDigest(prfHash); + } + DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash); + result.init(context); + return result; + } + + public Digest forkPRFHash() + { + checkStopBuffering(); + + if (buf != null) + { + Digest prfHash = TlsUtils.createHash(prfHashAlgorithm.shortValue()); + buf.updateDigest(prfHash); + return prfHash; + } + + return TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm)); + } + + public byte[] getFinalHash(short hashAlgorithm) + { + Digest d = (Digest)hashes.get(Shorts.valueOf(hashAlgorithm)); + if (d == null) + { + throw new IllegalStateException("HashAlgorithm " + hashAlgorithm + " is not being tracked"); + } + + d = TlsUtils.cloneHash(hashAlgorithm, d); + if (buf != null) + { + buf.updateDigest(d); + } + + byte[] bs = new byte[d.getDigestSize()]; + d.doFinal(bs, 0); + return bs; + } + + public String getAlgorithmName() + { + throw new IllegalStateException("Use fork() to get a definite Digest"); + } + + public int getDigestSize() + { + throw new IllegalStateException("Use fork() to get a definite Digest"); + } + + public void update(byte input) + { + if (buf != null) + { + buf.write(input); + return; + } + + Enumeration e = hashes.elements(); + while (e.hasMoreElements()) + { + Digest hash = (Digest)e.nextElement(); + hash.update(input); + } + } + + public void update(byte[] input, int inOff, int len) + { + if (buf != null) + { + buf.write(input, inOff, len); + return; + } + + Enumeration e = hashes.elements(); + while (e.hasMoreElements()) + { + Digest hash = (Digest)e.nextElement(); + hash.update(input, inOff, len); + } + } + + public int doFinal(byte[] output, int outOff) + { + throw new IllegalStateException("Use fork() to get a definite Digest"); + } + + public void reset() + { + if (buf != null) + { + buf.reset(); + return; + } + + Enumeration e = hashes.elements(); + while (e.hasMoreElements()) + { + Digest hash = (Digest)e.nextElement(); + hash.reset(); + } + } + + protected void checkStopBuffering() + { + if (buf != null && hashes.size() <= BUFFERING_HASH_LIMIT) + { + Enumeration e = hashes.elements(); + while (e.hasMoreElements()) + { + Digest hash = (Digest)e.nextElement(); + buf.updateDigest(hash); + } + + this.buf = null; + } + } + + protected void checkTrackingHash(Short hashAlgorithm) + { + if (!hashes.containsKey(hashAlgorithm)) + { + Digest hash = TlsUtils.createHash(hashAlgorithm.shortValue()); + hashes.put(hashAlgorithm, hash); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigestAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigestAlgorithm.java new file mode 100644 index 000000000..9f3cb5570 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigestAlgorithm.java @@ -0,0 +1,23 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + * + * @deprecated use MACAlgorithm constants instead + */ +public class DigestAlgorithm +{ + public static final int NULL = 0; + public static final int MD5 = 1; + public static final int SHA = 2; + + /* + * RFC 5246 + */ + public static final int SHA256 = 3; + public static final int SHA384 = 4; + public static final int SHA512 = 5; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigestInputBuffer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigestInputBuffer.java new file mode 100644 index 000000000..a8f6594e5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigestInputBuffer.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; + +import org.spongycastle.crypto.Digest; + +class DigestInputBuffer extends ByteArrayOutputStream +{ + void updateDigest(Digest d) + { + d.update(this.buf, 0, count); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigitallySigned.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigitallySigned.java new file mode 100644 index 000000000..8ab8a8d48 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DigitallySigned.java @@ -0,0 +1,72 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class DigitallySigned +{ + protected SignatureAndHashAlgorithm algorithm; + protected byte[] signature; + + public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature) + { + if (signature == null) + { + throw new IllegalArgumentException("'signature' cannot be null"); + } + + this.algorithm = algorithm; + this.signature = signature; + } + + /** + * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2). + */ + public SignatureAndHashAlgorithm getAlgorithm() + { + return algorithm; + } + + public byte[] getSignature() + { + return signature; + } + + /** + * Encode this {@link DigitallySigned} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + if (algorithm != null) + { + algorithm.encode(output); + } + TlsUtils.writeOpaque16(signature, output); + } + + /** + * Parse a {@link DigitallySigned} from an {@link InputStream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link InputStream} to parse from. + * @return a {@link DigitallySigned} object. + * @throws IOException + */ + public static DigitallySigned parse(TlsContext context, InputStream input) throws IOException + { + SignatureAndHashAlgorithm algorithm = null; + if (TlsUtils.isTLSv12(context)) + { + algorithm = SignatureAndHashAlgorithm.parse(input); + } + byte[] signature = TlsUtils.readOpaque16(input); + return new DigitallySigned(algorithm, signature); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECBasisType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECBasisType.java new file mode 100644 index 000000000..678384f9c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECBasisType.java @@ -0,0 +1,11 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4492 5.4. (Errata ID: 2389) + */ +public class ECBasisType +{ + + public static final short ec_basis_trinomial = 1; + public static final short ec_basis_pentanomial = 2; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECCurveType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECCurveType.java new file mode 100644 index 000000000..01266b08e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECCurveType.java @@ -0,0 +1,28 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4492 5.4 + */ +public class ECCurveType +{ + /** + * Indicates the elliptic curve domain parameters are conveyed verbosely, and the + * underlying finite field is a prime field. + */ + public static final short explicit_prime = 1; + + /** + * Indicates the elliptic curve domain parameters are conveyed verbosely, and the + * underlying finite field is a characteristic-2 field. + */ + public static final short explicit_char2 = 2; + + /** + * Indicates that a named curve is used. This option SHOULD be used when applicable. + */ + public static final short named_curve = 3; + + /* + * Values 248 through 255 are reserved for private use. + */ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECPointFormat.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECPointFormat.java new file mode 100644 index 000000000..794bc7574 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ECPointFormat.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4492 5.1.2 + */ +public class ECPointFormat +{ + public static final short uncompressed = 0; + public static final short ansiX962_compressed_prime = 1; + public static final short ansiX962_compressed_char2 = 2; + + /* + * reserved (248..255) + */ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/EncryptionAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/EncryptionAlgorithm.java new file mode 100644 index 000000000..6b607726a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/EncryptionAlgorithm.java @@ -0,0 +1,57 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class EncryptionAlgorithm +{ + + public static final int NULL = 0; + public static final int RC4_40 = 1; + public static final int RC4_128 = 2; + public static final int RC2_CBC_40 = 3; + public static final int IDEA_CBC = 4; + public static final int DES40_CBC = 5; + public static final int DES_CBC = 6; + public static final int _3DES_EDE_CBC = 7; + + /* + * RFC 3268 + */ + public static final int AES_128_CBC = 8; + public static final int AES_256_CBC = 9; + + /* + * RFC 5289 + */ + public static final int AES_128_GCM = 10; + public static final int AES_256_GCM = 11; + + /* + * RFC 4132 + */ + public static final int CAMELLIA_128_CBC = 12; + public static final int CAMELLIA_256_CBC = 13; + + /* + * RFC 4162 + */ + public static final int SEED_CBC = 14; + + /* + * RFC 6655 + */ + public static final int AES_128_CCM = 15; + public static final int AES_128_CCM_8 = 16; + public static final int AES_256_CCM = 17; + public static final int AES_256_CCM_8 = 18; + + /* + * TBD[draft-josefsson-salsa20-tls-02] + */ + static final int ESTREAM_SALSA20 = 100; + static final int SALSA20 = 101; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ExporterLabel.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ExporterLabel.java new file mode 100644 index 000000000..419680a8f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ExporterLabel.java @@ -0,0 +1,31 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 5705 + */ +public class ExporterLabel +{ + /* + * RFC 5246 + */ + public static final String client_finished = "client finished"; + public static final String server_finished = "server finished"; + public static final String master_secret = "master secret"; + public static final String key_expansion = "key expansion"; + + /* + * RFC 5216 + */ + public static final String client_EAP_encryption = "client EAP encryption"; + + /* + * RFC 5281 + */ + public static final String ttls_keying_material = "ttls keying material"; + public static final String ttls_challenge = "ttls challenge"; + + /* + * RFC 5764 + */ + public static final String dtls_srtp = "EXTRACTOR-dtls_srtp"; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ExtensionType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ExtensionType.java new file mode 100644 index 000000000..0b50769de --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ExtensionType.java @@ -0,0 +1,55 @@ +package org.spongycastle.crypto.tls; + +public class ExtensionType +{ + /* + * RFC 2546 2.3. + */ + public static final int server_name = 0; + public static final int max_fragment_length = 1; + public static final int client_certificate_url = 2; + public static final int trusted_ca_keys = 3; + public static final int truncated_hmac = 4; + public static final int status_request = 5; + + /* + * RFC 4681 + */ + public static final int user_mapping = 6; + + /* + * RFC 4492 5.1. + */ + public static final int elliptic_curves = 10; + public static final int ec_point_formats = 11; + + /* + * RFC 5054 2.8.1. + */ + public static final int srp = 12; + + /* + * RFC 5077 7. + */ + public static final int session_ticket = 35; + + /* + * RFC 5246 7.4.1.4. + */ + public static final int signature_algorithms = 13; + + /* + * RFC 5764 9. + */ + public static final int use_srtp = 14; + + /* + * RFC 6520 6. + */ + public static final int heartbeat = 15; + + /* + * RFC 5746 3.2. + */ + public static final int renegotiation_info = 0xff01; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HandshakeType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HandshakeType.java new file mode 100644 index 000000000..f03135f1f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HandshakeType.java @@ -0,0 +1,39 @@ +package org.spongycastle.crypto.tls; + +public class HandshakeType +{ + /* + * RFC 2246 7.4 + */ + public static final short hello_request = 0; + public static final short client_hello = 1; + public static final short server_hello = 2; + public static final short certificate = 11; + public static final short server_key_exchange = 12; + public static final short certificate_request = 13; + public static final short server_hello_done = 14; + public static final short certificate_verify = 15; + public static final short client_key_exchange = 16; + public static final short finished = 20; + + /* + * RFC 3546 2.4 + */ + public static final short certificate_url = 21; + public static final short certificate_status = 22; + + /* + * (DTLS) RFC 4347 4.3.2 + */ + public static final short hello_verify_request = 3; + + /* + * RFC 4680 + */ + public static final short supplemental_data = 23; + + /* + * RFC 5077 + */ + public static final short session_ticket = 4; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HashAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HashAlgorithm.java new file mode 100644 index 000000000..0e32be9ca --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HashAlgorithm.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 5246 7.4.1.4.1 + */ +public class HashAlgorithm +{ + public static final short none = 0; + public static final short md5 = 1; + public static final short sha1 = 2; + public static final short sha224 = 3; + public static final short sha256 = 4; + public static final short sha384 = 5; + public static final short sha512 = 6; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatExtension.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatExtension.java new file mode 100644 index 000000000..4c325014c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatExtension.java @@ -0,0 +1,56 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class HeartbeatExtension +{ + protected short mode; + + public HeartbeatExtension(short mode) + { + if (!HeartbeatMode.isValid(mode)) + { + throw new IllegalArgumentException("'mode' is not a valid HeartbeatMode value"); + } + + this.mode = mode; + } + + public short getMode() + { + return mode; + } + + /** + * Encode this {@link HeartbeatExtension} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + TlsUtils.writeUint8(mode, output); + } + + /** + * Parse a {@link HeartbeatExtension} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link HeartbeatExtension} object. + * @throws IOException + */ + public static HeartbeatExtension parse(InputStream input) throws IOException + { + short mode = TlsUtils.readUint8(input); + if (!HeartbeatMode.isValid(mode)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return new HeartbeatExtension(mode); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMessage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMessage.java new file mode 100644 index 000000000..60fc2529b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMessage.java @@ -0,0 +1,108 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.io.Streams; + +public class HeartbeatMessage +{ + protected short type; + protected byte[] payload; + protected int paddingLength; + + public HeartbeatMessage(short type, byte[] payload, int paddingLength) + { + if (!HeartbeatMessageType.isValid(type)) + { + throw new IllegalArgumentException("'type' is not a valid HeartbeatMessageType value"); + } + if (payload == null || payload.length >= (1 << 16)) + { + throw new IllegalArgumentException("'payload' must have length < 2^16"); + } + if (paddingLength < 16) + { + throw new IllegalArgumentException("'paddingLength' must be at least 16"); + } + + this.type = type; + this.payload = payload; + this.paddingLength = paddingLength; + } + + /** + * Encode this {@link HeartbeatMessage} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(TlsContext context, OutputStream output) throws IOException + { + TlsUtils.writeUint8(type, output); + + TlsUtils.checkUint16(payload.length); + TlsUtils.writeUint16(payload.length, output); + output.write(payload); + + byte[] padding = new byte[paddingLength]; + context.getSecureRandom().nextBytes(padding); + output.write(padding); + } + + /** + * Parse a {@link HeartbeatMessage} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link HeartbeatMessage} object. + * @throws IOException + */ + public static HeartbeatMessage parse(InputStream input) throws IOException + { + short type = TlsUtils.readUint8(input); + if (!HeartbeatMessageType.isValid(type)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + int payload_length = TlsUtils.readUint16(input); + + PayloadBuffer buf = new PayloadBuffer(); + Streams.pipeAll(input, buf); + + byte[] payload = buf.toTruncatedByteArray(payload_length); + if (payload == null) + { + /* + * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the + * received HeartbeatMessage MUST be discarded silently. + */ + return null; + } + + int padding_length = buf.size() - payload.length; + + return new HeartbeatMessage(type, payload, padding_length); + } + + static class PayloadBuffer extends ByteArrayOutputStream + { + byte[] toTruncatedByteArray(int payloadLength) + { + /* + * RFC 6520 4. The padding_length MUST be at least 16. + */ + int minimumCount = payloadLength + 16; + if (count < minimumCount) + { + return null; + } + return Arrays.copyOf(buf, payloadLength); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMessageType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMessageType.java new file mode 100644 index 000000000..2d21a1e8e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMessageType.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.tls; + +/* + * RFC 6520 3. + */ +public class HeartbeatMessageType +{ + public static final short heartbeat_request = 1; + public static final short heartbeat_response = 2; + + public static boolean isValid(short heartbeatMessageType) + { + return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMode.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMode.java new file mode 100644 index 000000000..1c9472b26 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/HeartbeatMode.java @@ -0,0 +1,15 @@ +package org.spongycastle.crypto.tls; + +/* + * RFC 6520 + */ +public class HeartbeatMode +{ + public static final short peer_allowed_to_send = 1; + public static final short peer_not_allowed_to_send = 2; + + public static boolean isValid(short heartbeatMode) + { + return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/KeyExchangeAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/KeyExchangeAlgorithm.java new file mode 100644 index 000000000..4c41bac49 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/KeyExchangeAlgorithm.java @@ -0,0 +1,52 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class KeyExchangeAlgorithm +{ + public static final int NULL = 0; + public static final int RSA = 1; + public static final int RSA_EXPORT = 2; + public static final int DHE_DSS = 3; + public static final int DHE_DSS_EXPORT = 4; + public static final int DHE_RSA = 5; + public static final int DHE_RSA_EXPORT = 6; + public static final int DH_DSS = 7; + public static final int DH_DSS_EXPORT = 8; + public static final int DH_RSA = 9; + public static final int DH_RSA_EXPORT = 10; + public static final int DH_anon = 11; + public static final int DH_anon_EXPORT = 12; + + /* + * RFC 4279 + */ + public static final int PSK = 13; + public static final int DHE_PSK = 14; + public static final int RSA_PSK = 15; + + /* + * RFC 4429 + */ + public static final int ECDH_ECDSA = 16; + public static final int ECDHE_ECDSA = 17; + public static final int ECDH_RSA = 18; + public static final int ECDHE_RSA = 19; + public static final int ECDH_anon = 20; + + /* + * RFC 5054 + */ + public static final int SRP = 21; + public static final int SRP_DSS = 22; + public static final int SRP_RSA = 23; + + /* + * RFC 5489 + */ + public static final int ECDHE_PSK = 24; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/LegacyTlsAuthentication.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/LegacyTlsAuthentication.java new file mode 100644 index 000000000..5e8a67eed --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/LegacyTlsAuthentication.java @@ -0,0 +1,28 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +/** + * A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication + * + * @deprecated + */ +public class LegacyTlsAuthentication + extends ServerOnlyTlsAuthentication +{ + protected CertificateVerifyer verifyer; + + public LegacyTlsAuthentication(CertificateVerifyer verifyer) + { + this.verifyer = verifyer; + } + + public void notifyServerCertificate(Certificate serverCertificate) + throws IOException + { + if (!this.verifyer.isValid(serverCertificate.getCertificateList())) + { + throw new TlsFatalAlert(AlertDescription.user_canceled); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/LegacyTlsClient.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/LegacyTlsClient.java new file mode 100644 index 000000000..32384ff99 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/LegacyTlsClient.java @@ -0,0 +1,33 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +/** + * A temporary class to use LegacyTlsAuthentication + * + * @deprecated + */ +public class LegacyTlsClient + extends DefaultTlsClient +{ + /** + * @deprecated + */ + protected CertificateVerifyer verifyer; + + /** + * @deprecated + */ + public LegacyTlsClient(CertificateVerifyer verifyer) + { + super(); + + this.verifyer = verifyer; + } + + public TlsAuthentication getAuthentication() + throws IOException + { + return new LegacyTlsAuthentication(verifyer); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/MACAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/MACAlgorithm.java new file mode 100644 index 000000000..9f2ad384f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/MACAlgorithm.java @@ -0,0 +1,28 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 2246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class MACAlgorithm +{ + public static final int _null = 0; + public static final int md5 = 1; + public static final int sha = 2; + + /* + * RFC 5246 + */ + public static final int hmac_md5 = md5; + public static final int hmac_sha1 = sha; + public static final int hmac_sha256 = 3; + public static final int hmac_sha384 = 4; + public static final int hmac_sha512 = 5; + + /* + * TBD[draft-josefsson-salsa20-tls-02] + */ + static final int umac96 = 100; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/MaxFragmentLength.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/MaxFragmentLength.java new file mode 100644 index 000000000..12aa8f37b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/MaxFragmentLength.java @@ -0,0 +1,17 @@ +package org.spongycastle.crypto.tls; + +public class MaxFragmentLength +{ + /* + * RFC 3546 3.2. + */ + public static short pow2_9 = 1; + public static short pow2_10 = 2; + public static short pow2_11 = 3; + public static short pow2_12 = 4; + + public static boolean isValid(short maxFragmentLength) + { + return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NameType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NameType.java new file mode 100644 index 000000000..86ca0ef07 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NameType.java @@ -0,0 +1,9 @@ +package org.spongycastle.crypto.tls; + +public class NameType +{ + /* + * RFC 3546 3.1. + */ + public static final short host_name = 0; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NamedCurve.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NamedCurve.java new file mode 100644 index 000000000..ec40602f1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NamedCurve.java @@ -0,0 +1,66 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4492 5.1.1 + * + * The named curves defined here are those specified in SEC 2 [13]. Note that many of these curves + * are also recommended in ANSI X9.62 [7] and FIPS 186-2 [11]. Values 0xFE00 through 0xFEFF are + * reserved for private use. Values 0xFF01 and 0xFF02 indicate that the client supports arbitrary + * prime and characteristic-2 curves, respectively (the curve parameters must be encoded explicitly + * in ECParameters). + */ +public class NamedCurve +{ + public static final int sect163k1 = 1; + public static final int sect163r1 = 2; + public static final int sect163r2 = 3; + public static final int sect193r1 = 4; + public static final int sect193r2 = 5; + public static final int sect233k1 = 6; + public static final int sect233r1 = 7; + public static final int sect239k1 = 8; + public static final int sect283k1 = 9; + public static final int sect283r1 = 10; + public static final int sect409k1 = 11; + public static final int sect409r1 = 12; + public static final int sect571k1 = 13; + public static final int sect571r1 = 14; + public static final int secp160k1 = 15; + public static final int secp160r1 = 16; + public static final int secp160r2 = 17; + public static final int secp192k1 = 18; + public static final int secp192r1 = 19; + public static final int secp224k1 = 20; + public static final int secp224r1 = 21; + public static final int secp256k1 = 22; + public static final int secp256r1 = 23; + public static final int secp384r1 = 24; + public static final int secp521r1 = 25; + + /* + * RFC 7027 + */ + public static final int brainpoolP256r1 = 26; + public static final int brainpoolP384r1 = 27; + public static final int brainpoolP512r1 = 28; + + /* + * reserved (0xFE00..0xFEFF) + */ + + public static final int arbitrary_explicit_prime_curves = 0xFF01; + public static final int arbitrary_explicit_char2_curves = 0xFF02; + + public static boolean refersToASpecificNamedCurve(int namedCurve) + { + switch (namedCurve) + { + case arbitrary_explicit_prime_curves: + case arbitrary_explicit_char2_curves: + return false; + default: + return true; + } + } + +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NewSessionTicket.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NewSessionTicket.java new file mode 100644 index 000000000..d9d67c280 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/NewSessionTicket.java @@ -0,0 +1,55 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class NewSessionTicket +{ + protected long ticketLifetimeHint; + protected byte[] ticket; + + public NewSessionTicket(long ticketLifetimeHint, byte[] ticket) + { + this.ticketLifetimeHint = ticketLifetimeHint; + this.ticket = ticket; + } + + public long getTicketLifetimeHint() + { + return ticketLifetimeHint; + } + + public byte[] getTicket() + { + return ticket; + } + + /** + * Encode this {@link NewSessionTicket} to an {@link OutputStream}. + * + * @param output the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) + throws IOException + { + TlsUtils.writeUint32(ticketLifetimeHint, output); + TlsUtils.writeOpaque16(ticket, output); + } + + /** + * Parse a {@link NewSessionTicket} from an {@link InputStream}. + * + * @param input the {@link InputStream} to parse from. + * @return a {@link NewSessionTicket} object. + * @throws IOException + */ + public static NewSessionTicket parse(InputStream input) + throws IOException + { + long ticketLifetimeHint = TlsUtils.readUint32(input); + byte[] ticket = TlsUtils.readOpaque16(input); + return new NewSessionTicket(ticketLifetimeHint, ticket); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/OCSPStatusRequest.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/OCSPStatusRequest.java new file mode 100644 index 000000000..d95bea9bd --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/OCSPStatusRequest.java @@ -0,0 +1,131 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ocsp.ResponderID; +import org.spongycastle.asn1.x509.Extensions; + +/** + * RFC 3546 3.6 + */ +public class OCSPStatusRequest +{ + protected Vector responderIDList; + protected Extensions requestExtensions; + + /** + * @param responderIDList + * a {@link Vector} of {@link ResponderID}, specifying the list of trusted OCSP + * responders. An empty list has the special meaning that the responders are + * implicitly known to the server - e.g., by prior arrangement. + * @param requestExtensions + * OCSP request extensions. A null value means that there are no extensions. + */ + public OCSPStatusRequest(Vector responderIDList, Extensions requestExtensions) + { + this.responderIDList = responderIDList; + this.requestExtensions = requestExtensions; + } + + /** + * @return a {@link Vector} of {@link ResponderID} + */ + public Vector getResponderIDList() + { + return responderIDList; + } + + /** + * @return OCSP request extensions + */ + public Extensions getRequestExtensions() + { + return requestExtensions; + } + + /** + * Encode this {@link OCSPStatusRequest} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + if (responderIDList == null || responderIDList.isEmpty()) + { + TlsUtils.writeUint16(0, output); + } + else + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + for (int i = 0; i < responderIDList.size(); ++i) + { + ResponderID responderID = (ResponderID) responderIDList.elementAt(i); + byte[] derEncoding = responderID.getEncoded(ASN1Encoding.DER); + TlsUtils.writeOpaque16(derEncoding, buf); + } + TlsUtils.checkUint16(buf.size()); + TlsUtils.writeUint16(buf.size(), output); + buf.writeTo(output); + } + + if (requestExtensions == null) + { + TlsUtils.writeUint16(0, output); + } + else + { + byte[] derEncoding = requestExtensions.getEncoded(ASN1Encoding.DER); + TlsUtils.checkUint16(derEncoding.length); + TlsUtils.writeUint16(derEncoding.length, output); + output.write(derEncoding); + } + } + + /** + * Parse a {@link OCSPStatusRequest} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link OCSPStatusRequest} object. + * @throws IOException + */ + public static OCSPStatusRequest parse(InputStream input) throws IOException + { + Vector responderIDList = new Vector(); + { + int length = TlsUtils.readUint16(input); + if (length > 0) + { + byte[] data = TlsUtils.readFully(length, input); + ByteArrayInputStream buf = new ByteArrayInputStream(data); + do + { + byte[] derEncoding = TlsUtils.readOpaque16(buf); + ResponderID responderID = ResponderID.getInstance(TlsUtils.readDERObject(derEncoding)); + responderIDList.addElement(responderID); + } + while (buf.available() > 0); + } + } + + Extensions requestExtensions = null; + { + int length = TlsUtils.readUint16(input); + if (length > 0) + { + byte[] derEncoding = TlsUtils.readFully(length, input); + requestExtensions = Extensions.getInstance(TlsUtils.readDERObject(derEncoding)); + } + } + + return new OCSPStatusRequest(responderIDList, requestExtensions); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/PRFAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/PRFAlgorithm.java new file mode 100644 index 000000000..62abbec63 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/PRFAlgorithm.java @@ -0,0 +1,23 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 5246 + * + * Note that the values here are implementation-specific and arbitrary. It is recommended not to + * depend on the particular values (e.g. serialization). + */ +public class PRFAlgorithm +{ + + /* + * Placeholder to refer to the legacy TLS algorithm + */ + public static final int tls_prf_legacy = 0; + + public static final int tls_prf_sha256 = 1; + + /* + * Implied by RFC 5288 + */ + public static final int tls_prf_sha384 = 2; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/PSKTlsClient.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/PSKTlsClient.java new file mode 100644 index 000000000..2955c1c00 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/PSKTlsClient.java @@ -0,0 +1,240 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public abstract class PSKTlsClient + extends AbstractTlsClient +{ + protected TlsPSKIdentity pskIdentity; + + public PSKTlsClient(TlsPSKIdentity pskIdentity) + { + super(); + this.pskIdentity = pskIdentity; + } + + public PSKTlsClient(TlsCipherFactory cipherFactory, TlsPSKIdentity pskIdentity) + { + super(cipherFactory); + this.pskIdentity = pskIdentity; + } + + public int[] getCipherSuites() + { + return new int[] { CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA }; + } + + public TlsKeyExchange getKeyExchange() throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK); + + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96: + return createPSKKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK); + + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_SALSA20_UMAC96: + return createPSKKeyExchange(KeyExchangeAlgorithm.PSK); + + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_UMAC96: + return createPSKKeyExchange(KeyExchangeAlgorithm.RSA_PSK); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public TlsCipher getCipher() throws IOException + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null); + + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null); + + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null); + + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96: + return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96); + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_PSK_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_UMAC96: + return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected TlsKeyExchange createPSKKeyExchange(int keyExchange) + { + return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, namedCurves, + clientECPointFormats, serverECPointFormats); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ProtocolVersion.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ProtocolVersion.java new file mode 100644 index 000000000..3aeefe42b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ProtocolVersion.java @@ -0,0 +1,125 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public final class ProtocolVersion +{ + public static final ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0"); + public static final ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0"); + public static final ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1"); + public static final ProtocolVersion TLSv12 = new ProtocolVersion(0x0303, "TLS 1.2"); + public static final ProtocolVersion DTLSv10 = new ProtocolVersion(0xFEFF, "DTLS 1.0"); + public static final ProtocolVersion DTLSv12 = new ProtocolVersion(0xFEFD, "DTLS 1.2"); + + private int version; + private String name; + + private ProtocolVersion(int v, String name) + { + this.version = v & 0xffff; + this.name = name; + } + + public int getFullVersion() + { + return version; + } + + public int getMajorVersion() + { + return version >> 8; + } + + public int getMinorVersion() + { + return version & 0xff; + } + + public boolean isDTLS() + { + return getMajorVersion() == 0xFE; + } + + public boolean isSSL() + { + return this == SSLv3; + } + + public ProtocolVersion getEquivalentTLSVersion() + { + if (!isDTLS()) + { + return this; + } + if (this == DTLSv10) + { + return TLSv11; + } + return TLSv12; + } + + public boolean isEqualOrEarlierVersionOf(ProtocolVersion version) + { + if (getMajorVersion() != version.getMajorVersion()) + { + return false; + } + int diffMinorVersion = version.getMinorVersion() - getMinorVersion(); + return isDTLS() ? diffMinorVersion <= 0 : diffMinorVersion >= 0; + } + + public boolean isLaterVersionOf(ProtocolVersion version) + { + if (getMajorVersion() != version.getMajorVersion()) + { + return false; + } + int diffMinorVersion = version.getMinorVersion() - getMinorVersion(); + return isDTLS() ? diffMinorVersion > 0 : diffMinorVersion < 0; + } + + public boolean equals(Object obj) + { + return this == obj; + } + + public int hashCode() + { + return version; + } + + public static ProtocolVersion get(int major, int minor) + throws IOException + { + switch (major) + { + case 0x03: + switch (minor) + { + case 0x00: + return SSLv3; + case 0x01: + return TLSv10; + case 0x02: + return TLSv11; + case 0x03: + return TLSv12; + } + case 0xFE: + switch (minor) + { + case 0xFF: + return DTLSv10; + case 0xFD: + return DTLSv12; + } + } + + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public String toString() + { + return name; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/RecordStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/RecordStream.java new file mode 100644 index 000000000..01cc00526 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/RecordStream.java @@ -0,0 +1,361 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.crypto.Digest; + +/** + * An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3. + */ +class RecordStream +{ + private static int DEFAULT_PLAINTEXT_LIMIT = (1 << 14); + + private TlsProtocol handler; + private InputStream input; + private OutputStream output; + private TlsCompression pendingCompression = null, readCompression = null, writeCompression = null; + private TlsCipher pendingCipher = null, readCipher = null, writeCipher = null; + private long readSeqNo = 0, writeSeqNo = 0; + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + private TlsContext context = null; + private TlsHandshakeHash handshakeHash = null; + + private ProtocolVersion readVersion = null, writeVersion = null; + private boolean restrictReadVersion = true; + + private int plaintextLimit, compressedLimit, ciphertextLimit; + + RecordStream(TlsProtocol handler, InputStream input, OutputStream output) + { + this.handler = handler; + this.input = input; + this.output = output; + this.readCompression = new TlsNullCompression(); + this.writeCompression = this.readCompression; + this.readCipher = new TlsNullCipher(context); + this.writeCipher = this.readCipher; + + setPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT); + } + + void init(TlsContext context) + { + this.context = context; + this.handshakeHash = new DeferredHash(); + this.handshakeHash.init(context); + } + + int getPlaintextLimit() + { + return plaintextLimit; + } + + void setPlaintextLimit(int plaintextLimit) + { + this.plaintextLimit = plaintextLimit; + this.compressedLimit = this.plaintextLimit + 1024; + this.ciphertextLimit = this.compressedLimit + 1024; + } + + ProtocolVersion getReadVersion() + { + return readVersion; + } + + void setReadVersion(ProtocolVersion readVersion) + { + this.readVersion = readVersion; + } + + void setWriteVersion(ProtocolVersion writeVersion) + { + this.writeVersion = writeVersion; + } + + /** + * RFC 5246 E.1. "Earlier versions of the TLS specification were not fully clear on what the + * record layer version number (TLSPlaintext.version) should contain when sending ClientHello + * (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers + * compliant with this specification MUST accept any value {03,XX} as the record layer version + * number for ClientHello." + */ + void setRestrictReadVersion(boolean enabled) + { + this.restrictReadVersion = enabled; + } + + void setPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher) + { + this.pendingCompression = tlsCompression; + this.pendingCipher = tlsCipher; + } + + void sentWriteCipherSpec() + throws IOException + { + if (pendingCompression == null || pendingCipher == null) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + this.writeCompression = this.pendingCompression; + this.writeCipher = this.pendingCipher; + this.writeSeqNo = 0; + } + + void receivedReadCipherSpec() + throws IOException + { + if (pendingCompression == null || pendingCipher == null) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + this.readCompression = this.pendingCompression; + this.readCipher = this.pendingCipher; + this.readSeqNo = 0; + } + + void finaliseHandshake() + throws IOException + { + if (readCompression != pendingCompression || writeCompression != pendingCompression + || readCipher != pendingCipher || writeCipher != pendingCipher) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + pendingCompression = null; + pendingCipher = null; + } + + public boolean readRecord() + throws IOException + { + byte[] recordHeader = TlsUtils.readAllOrNothing(5, input); + if (recordHeader == null) + { + return false; + } + + short type = TlsUtils.readUint8(recordHeader, 0); + + /* + * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an + * unexpected_message alert. + */ + checkType(type, AlertDescription.unexpected_message); + + if (!restrictReadVersion) + { + int version = TlsUtils.readVersionRaw(recordHeader, 1); + if ((version & 0xffffff00) != 0x0300) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + else + { + ProtocolVersion version = TlsUtils.readVersion(recordHeader, 1); + if (readVersion == null) + { + readVersion = version; + } + else if (!version.equals(readVersion)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + int length = TlsUtils.readUint16(recordHeader, 3); + byte[] plaintext = decodeAndVerify(type, input, length); + handler.processRecord(type, plaintext, 0, plaintext.length); + return true; + } + + protected byte[] decodeAndVerify(short type, InputStream input, int len) + throws IOException + { + checkLength(len, ciphertextLimit, AlertDescription.record_overflow); + + byte[] buf = TlsUtils.readFully(len, input); + byte[] decoded = readCipher.decodeCiphertext(readSeqNo++, type, buf, 0, buf.length); + + checkLength(decoded.length, compressedLimit, AlertDescription.record_overflow); + + /* + * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for + * ensuring that messages cannot cause internal buffer overflows. + */ + OutputStream cOut = readCompression.decompress(buffer); + if (cOut != buffer) + { + cOut.write(decoded, 0, decoded.length); + cOut.flush(); + decoded = getBufferContents(); + } + + /* + * RFC 5264 6.2.2. If the decompression function encounters a TLSCompressed.fragment that + * would decompress to a length in excess of 2^14 bytes, it should report a fatal + * decompression failure error. + */ + checkLength(decoded.length, plaintextLimit, AlertDescription.decompression_failure); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (decoded.length < 1 && type != ContentType.application_data) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return decoded; + } + + protected void writeRecord(short type, byte[] plaintext, int plaintextOffset, int plaintextLength) + throws IOException + { + /* + * RFC 5264 6. Implementations MUST NOT send record types not defined in this document + * unless negotiated by some extension. + */ + checkType(type, AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 The length should not exceed 2^14. + */ + checkLength(plaintextLength, plaintextLimit, AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (plaintextLength < 1 && type != ContentType.application_data) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (type == ContentType.handshake) + { + updateHandshakeData(plaintext, plaintextOffset, plaintextLength); + } + + OutputStream cOut = writeCompression.compress(buffer); + + byte[] ciphertext; + if (cOut == buffer) + { + ciphertext = writeCipher.encodePlaintext(writeSeqNo++, type, plaintext, plaintextOffset, plaintextLength); + } + else + { + cOut.write(plaintext, plaintextOffset, plaintextLength); + cOut.flush(); + byte[] compressed = getBufferContents(); + + /* + * RFC5264 6.2.2. Compression must be lossless and may not increase the content length + * by more than 1024 bytes. + */ + checkLength(compressed.length, plaintextLength + 1024, AlertDescription.internal_error); + + ciphertext = writeCipher.encodePlaintext(writeSeqNo++, type, compressed, 0, compressed.length); + } + + /* + * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048. + */ + checkLength(ciphertext.length, ciphertextLimit, AlertDescription.internal_error); + + byte[] record = new byte[ciphertext.length + 5]; + TlsUtils.writeUint8(type, record, 0); + TlsUtils.writeVersion(writeVersion, record, 1); + TlsUtils.writeUint16(ciphertext.length, record, 3); + System.arraycopy(ciphertext, 0, record, 5, ciphertext.length); + output.write(record); + output.flush(); + } + + void notifyHelloComplete() + { + this.handshakeHash = handshakeHash.notifyPRFDetermined(); + } + + TlsHandshakeHash getHandshakeHash() + { + return handshakeHash; + } + + TlsHandshakeHash prepareToFinish() + { + TlsHandshakeHash result = handshakeHash; + this.handshakeHash = handshakeHash.stopTracking(); + return result; + } + + void updateHandshakeData(byte[] message, int offset, int len) + { + handshakeHash.update(message, offset, len); + } + + protected void safeClose() + { + try + { + input.close(); + } + catch (IOException e) + { + } + + try + { + output.close(); + } + catch (IOException e) + { + } + } + + protected void flush() + throws IOException + { + output.flush(); + } + + private byte[] getBufferContents() + { + byte[] contents = buffer.toByteArray(); + buffer.reset(); + return contents; + } + + private static void checkType(short type, short alertDescription) + throws IOException + { + switch (type) + { + case ContentType.application_data: + case ContentType.alert: + case ContentType.change_cipher_spec: + case ContentType.handshake: + case ContentType.heartbeat: + break; + default: + throw new TlsFatalAlert(alertDescription); + } + } + + private static void checkLength(int length, int limit, short alertDescription) + throws IOException + { + if (length > limit) + { + throw new TlsFatalAlert(alertDescription); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SRPTlsClient.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SRPTlsClient.java new file mode 100644 index 000000000..65aa6ceec --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SRPTlsClient.java @@ -0,0 +1,121 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.util.Arrays; + +public abstract class SRPTlsClient + extends AbstractTlsClient +{ + /** + * @deprecated use TlsSRPUtils.EXT_SRP instead + */ + public static final Integer EXT_SRP = TlsSRPUtils.EXT_SRP; + + protected byte[] identity; + protected byte[] password; + + public SRPTlsClient(byte[] identity, byte[] password) + { + super(); + this.identity = Arrays.clone(identity); + this.password = Arrays.clone(password); + } + + public SRPTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password) + { + super(cipherFactory); + this.identity = Arrays.clone(identity); + this.password = Arrays.clone(password); + } + + public int[] getCipherSuites() + { + return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA }; + } + + public Hashtable getClientExtensions() + throws IOException + { + Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions()); + TlsSRPUtils.addSRPExtension(clientExtensions, this.identity); + return clientExtensions; + } + + public void processServerExtensions(Hashtable serverExtensions) + throws IOException + { + if (!TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsSRPUtils.EXT_SRP, AlertDescription.illegal_parameter)) + { + // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server + } + } + + public TlsKeyExchange getKeyExchange() + throws IOException + { + + switch (selectedCipherSuite) + { + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return createSRPKeyExchange(KeyExchangeAlgorithm.SRP); + + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + return createSRPKeyExchange(KeyExchangeAlgorithm.SRP_RSA); + + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + return createSRPKeyExchange(KeyExchangeAlgorithm.SRP_DSS); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public TlsCipher getCipher() + throws IOException + { + + switch (selectedCipherSuite) + { + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1); + + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected TlsKeyExchange createSRPKeyExchange(int keyExchange) + { + return new TlsSRPKeyExchange(keyExchange, supportedSignatureAlgorithms, identity, password); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SRTPProtectionProfile.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SRTPProtectionProfile.java new file mode 100644 index 000000000..6581b54a2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SRTPProtectionProfile.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +public class SRTPProtectionProfile +{ + /* + * RFC 5764 4.1.2. + */ + public static final int SRTP_AES128_CM_HMAC_SHA1_80 = 0x0001; + public static final int SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002; + public static final int SRTP_NULL_HMAC_SHA1_80 = 0x0005; + public static final int SRTP_NULL_HMAC_SHA1_32 = 0x0006; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SSL3Mac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SSL3Mac.java new file mode 100644 index 000000000..bd4e37f95 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SSL3Mac.java @@ -0,0 +1,114 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.util.Arrays; + +/** + * HMAC implementation based on original internet draft for HMAC (RFC 2104) + * + * The difference is that padding is concatenated versus XORed with the key + * + * H(K + opad, H(K + ipad, text)) + */ +public class SSL3Mac + implements Mac +{ + private final static byte IPAD_BYTE = (byte)0x36; + private final static byte OPAD_BYTE = (byte)0x5C; + + static final byte[] IPAD = genPad(IPAD_BYTE, 48); + static final byte[] OPAD = genPad(OPAD_BYTE, 48); + + private Digest digest; + + private byte[] secret; + private int padLength; + + /** + * Base constructor for one of the standard digest algorithms that the byteLength of + * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1. + * + * @param digest the digest. + */ + public SSL3Mac(Digest digest) + { + this.digest = digest; + + if (digest.getDigestSize() == 20) + { + this.padLength = 40; + } + else + { + this.padLength = 48; + } + } + + public String getAlgorithmName() + { + return digest.getAlgorithmName() + "/SSL3MAC"; + } + + public Digest getUnderlyingDigest() + { + return digest; + } + + public void init(CipherParameters params) + { + secret = Arrays.clone(((KeyParameter)params).getKey()); + + reset(); + } + + public int getMacSize() + { + return digest.getDigestSize(); + } + + public void update(byte in) + { + digest.update(in); + } + + public void update(byte[] in, int inOff, int len) + { + digest.update(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + byte[] tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + + digest.update(secret, 0, secret.length); + digest.update(OPAD, 0, padLength); + digest.update(tmp, 0, tmp.length); + + int len = digest.doFinal(out, outOff); + + reset(); + + return len; + } + + /** + * Reset the mac generator. + */ + public void reset() + { + digest.reset(); + digest.update(secret, 0, secret.length); + digest.update(IPAD, 0, padLength); + } + + private static byte[] genPad(byte b, int count) + { + byte[] padding = new byte[count]; + Arrays.fill(padding, b); + return padding; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SecurityParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SecurityParameters.java new file mode 100644 index 000000000..c5d543946 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SecurityParameters.java @@ -0,0 +1,90 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.util.Arrays; + +public class SecurityParameters +{ + int entity = -1; + int cipherSuite = -1; + short compressionAlgorithm = -1; + int prfAlgorithm = -1; + int verifyDataLength = -1; + byte[] masterSecret = null; + byte[] clientRandom = null; + byte[] serverRandom = null; + + // TODO Keep these internal, since it's maybe not the ideal place for them + short maxFragmentLength = -1; + boolean truncatedHMac = false; + + void copySessionParametersFrom(SecurityParameters other) + { + this.entity = other.entity; + this.cipherSuite = other.cipherSuite; + this.compressionAlgorithm = other.compressionAlgorithm; + this.prfAlgorithm = other.prfAlgorithm; + this.verifyDataLength = other.verifyDataLength; + this.masterSecret = Arrays.clone(other.masterSecret); + } + + void clear() + { + if (this.masterSecret != null) + { + Arrays.fill(this.masterSecret, (byte)0); + this.masterSecret = null; + } + } + + /** + * @return {@link ConnectionEnd} + */ + public int getEntity() + { + return entity; + } + + /** + * @return {@link CipherSuite} + */ + public int getCipherSuite() + { + return cipherSuite; + } + + /** + * @return {@link CompressionMethod} + */ + public short getCompressionAlgorithm() + { + return compressionAlgorithm; + } + + /** + * @return {@link PRFAlgorithm} + */ + public int getPrfAlgorithm() + { + return prfAlgorithm; + } + + public int getVerifyDataLength() + { + return verifyDataLength; + } + + public byte[] getMasterSecret() + { + return masterSecret; + } + + public byte[] getClientRandom() + { + return clientRandom; + } + + public byte[] getServerRandom() + { + return serverRandom; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerDHParams.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerDHParams.java new file mode 100644 index 000000000..fa22e629d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerDHParams.java @@ -0,0 +1,63 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; + +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; + +public class ServerDHParams +{ + protected DHPublicKeyParameters publicKey; + + public ServerDHParams(DHPublicKeyParameters publicKey) + { + if (publicKey == null) + { + throw new IllegalArgumentException("'publicKey' cannot be null"); + } + + this.publicKey = publicKey; + } + + public DHPublicKeyParameters getPublicKey() + { + return publicKey; + } + + /** + * Encode this {@link ServerDHParams} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + DHParameters dhParameters = publicKey.getParameters(); + BigInteger Ys = publicKey.getY(); + + TlsDHUtils.writeDHParameter(dhParameters.getP(), output); + TlsDHUtils.writeDHParameter(dhParameters.getG(), output); + TlsDHUtils.writeDHParameter(Ys, output); + } + + /** + * Parse a {@link ServerDHParams} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link ServerDHParams} object. + * @throws IOException + */ + public static ServerDHParams parse(InputStream input) throws IOException + { + BigInteger p = TlsDHUtils.readDHParameter(input); + BigInteger g = TlsDHUtils.readDHParameter(input); + BigInteger Ys = TlsDHUtils.readDHParameter(input); + + return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerName.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerName.java new file mode 100644 index 000000000..8e2602913 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerName.java @@ -0,0 +1,112 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.util.Strings; + +public class ServerName +{ + protected short nameType; + protected Object name; + + public ServerName(short nameType, Object name) + { + if (!isCorrectType(nameType, name)) + { + throw new IllegalArgumentException("'name' is not an instance of the correct type"); + } + + this.nameType = nameType; + this.name = name; + } + + public short getNameType() + { + return nameType; + } + + public Object getName() + { + return name; + } + + public String getHostName() + { + if (!isCorrectType(NameType.host_name, name)) + { + throw new IllegalStateException("'name' is not a HostName string"); + } + return (String)name; + } + + /** + * Encode this {@link ServerName} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + TlsUtils.writeUint8(nameType, output); + + switch (nameType) + { + case NameType.host_name: + byte[] utf8Encoding = Strings.toUTF8ByteArray((String)name); + if (utf8Encoding.length < 1) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + TlsUtils.writeOpaque16(utf8Encoding, output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link ServerName} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link ServerName} object. + * @throws IOException + */ + public static ServerName parse(InputStream input) throws IOException + { + short name_type = TlsUtils.readUint8(input); + Object name; + + switch (name_type) + { + case NameType.host_name: + { + byte[] utf8Encoding = TlsUtils.readOpaque16(input); + if (utf8Encoding.length < 1) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + name = Strings.fromUTF8ByteArray(utf8Encoding); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new ServerName(name_type, name); + } + + protected static boolean isCorrectType(short nameType, Object name) + { + switch (nameType) + { + case NameType.host_name: + return name instanceof String; + default: + throw new IllegalArgumentException("'name' is an unsupported value"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerNameList.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerNameList.java new file mode 100644 index 000000000..67c56e53e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerNameList.java @@ -0,0 +1,86 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +public class ServerNameList +{ + protected Vector serverNameList; + + /** + * @param serverNameList a {@link Vector} of {@link ServerName}. + */ + public ServerNameList(Vector serverNameList) + { + if (serverNameList == null || serverNameList.isEmpty()) + { + throw new IllegalArgumentException("'serverNameList' must not be null or empty"); + } + + this.serverNameList = serverNameList; + } + + /** + * @return a {@link Vector} of {@link ServerName}. + */ + public Vector getServerNameList() + { + return serverNameList; + } + + /** + * Encode this {@link ServerNameList} to an {@link OutputStream}. + * + * @param output + * the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + for (int i = 0; i < serverNameList.size(); ++i) + { + ServerName entry = (ServerName)serverNameList.elementAt(i); + entry.encode(buf); + } + + TlsUtils.checkUint16(buf.size()); + TlsUtils.writeUint16(buf.size(), output); + buf.writeTo(output); + } + + /** + * Parse a {@link ServerNameList} from an {@link InputStream}. + * + * @param input + * the {@link InputStream} to parse from. + * @return a {@link ServerNameList} object. + * @throws IOException + */ + public static ServerNameList parse(InputStream input) throws IOException + { + int length = TlsUtils.readUint16(input); + if (length < 1) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + byte[] data = TlsUtils.readFully(length, input); + + ByteArrayInputStream buf = new ByteArrayInputStream(data); + + Vector server_name_list = new Vector(); + while (buf.available() > 0) + { + ServerName entry = ServerName.parse(buf); + server_name_list.addElement(entry); + } + + return new ServerNameList(server_name_list); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerOnlyTlsAuthentication.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerOnlyTlsAuthentication.java new file mode 100644 index 000000000..325209bad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/ServerOnlyTlsAuthentication.java @@ -0,0 +1,10 @@ +package org.spongycastle.crypto.tls; + +public abstract class ServerOnlyTlsAuthentication + implements TlsAuthentication +{ + public final TlsCredentials getClientCredentials(CertificateRequest certificateRequest) + { + return null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SessionParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SessionParameters.java new file mode 100644 index 000000000..cf8ec194c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SessionParameters.java @@ -0,0 +1,142 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.util.Arrays; + +public final class SessionParameters +{ + public static final class Builder + { + private int cipherSuite = -1; + private short compressionAlgorithm = -1; + private byte[] masterSecret = null; + private Certificate peerCertificate = null; + private byte[] encodedServerExtensions = null; + + public Builder() + { + } + + public SessionParameters build() + { + validate(this.cipherSuite >= 0, "cipherSuite"); + validate(this.compressionAlgorithm >= 0, "compressionAlgorithm"); + validate(this.masterSecret != null, "masterSecret"); + return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate, + encodedServerExtensions); + } + + public Builder setCipherSuite(int cipherSuite) + { + this.cipherSuite = cipherSuite; + return this; + } + + public Builder setCompressionAlgorithm(short compressionAlgorithm) + { + this.compressionAlgorithm = compressionAlgorithm; + return this; + } + + public Builder setMasterSecret(byte[] masterSecret) + { + this.masterSecret = masterSecret; + return this; + } + + public Builder setPeerCertificate(Certificate peerCertificate) + { + this.peerCertificate = peerCertificate; + return this; + } + + public Builder setServerExtensions(Hashtable serverExtensions) + throws IOException + { + if (serverExtensions == null) + { + encodedServerExtensions = null; + } + else + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + TlsProtocol.writeExtensions(buf, serverExtensions); + encodedServerExtensions = buf.toByteArray(); + } + return this; + } + + private void validate(boolean condition, String parameter) + { + if (!condition) + { + throw new IllegalStateException("Required session parameter '" + parameter + "' not configured"); + } + } + } + + private int cipherSuite; + private short compressionAlgorithm; + private byte[] masterSecret; + private Certificate peerCertificate; + private byte[] encodedServerExtensions; + + private SessionParameters(int cipherSuite, short compressionAlgorithm, byte[] masterSecret, + Certificate peerCertificate, byte[] encodedServerExtensions) + { + this.cipherSuite = cipherSuite; + this.compressionAlgorithm = compressionAlgorithm; + this.masterSecret = Arrays.clone(masterSecret); + this.peerCertificate = peerCertificate; + this.encodedServerExtensions = encodedServerExtensions; + } + + public void clear() + { + if (this.masterSecret != null) + { + Arrays.fill(this.masterSecret, (byte)0); + } + } + + public SessionParameters copy() + { + return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate, + encodedServerExtensions); + } + + public int getCipherSuite() + { + return cipherSuite; + } + + public short getCompressionAlgorithm() + { + return compressionAlgorithm; + } + + public byte[] getMasterSecret() + { + return masterSecret; + } + + public Certificate getPeerCertificate() + { + return peerCertificate; + } + + public Hashtable readServerExtensions() throws IOException + { + if (encodedServerExtensions == null) + { + return null; + } + + ByteArrayInputStream buf = new ByteArrayInputStream(encodedServerExtensions); + return TlsProtocol.readExtensions(buf); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignatureAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignatureAlgorithm.java new file mode 100644 index 000000000..43cf38cee --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignatureAlgorithm.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 5246 7.4.1.4.1 (in RFC 2246, there were no specific values assigned) + */ +public class SignatureAlgorithm +{ + + public static final short anonymous = 0; + public static final short rsa = 1; + public static final short dsa = 2; + public static final short ecdsa = 3; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignatureAndHashAlgorithm.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignatureAndHashAlgorithm.java new file mode 100644 index 000000000..1c1827e56 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignatureAndHashAlgorithm.java @@ -0,0 +1,96 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * RFC 5246 7.4.1.4.1 + */ +public class SignatureAndHashAlgorithm +{ + protected short hash; + protected short signature; + + /** + * @param hash {@link HashAlgorithm} + * @param signature {@link SignatureAlgorithm} + */ + public SignatureAndHashAlgorithm(short hash, short signature) + { + if (!TlsUtils.isValidUint8(hash)) + { + throw new IllegalArgumentException("'hash' should be a uint8"); + } + if (!TlsUtils.isValidUint8(signature)) + { + throw new IllegalArgumentException("'signature' should be a uint8"); + } + if (signature == SignatureAlgorithm.anonymous) + { + throw new IllegalArgumentException("'signature' MUST NOT be \"anonymous\""); + } + + this.hash = hash; + this.signature = signature; + } + + /** + * @return {@link HashAlgorithm} + */ + public short getHash() + { + return hash; + } + + /** + * @return {@link SignatureAlgorithm} + */ + public short getSignature() + { + return signature; + } + + public boolean equals(Object obj) + { + if (!(obj instanceof SignatureAndHashAlgorithm)) + { + return false; + } + SignatureAndHashAlgorithm other = (SignatureAndHashAlgorithm)obj; + return other.getHash() == getHash() && other.getSignature() == getSignature(); + } + + public int hashCode() + { + return (getHash() << 16) | getSignature(); + } + + /** + * Encode this {@link SignatureAndHashAlgorithm} to an {@link OutputStream}. + * + * @param output the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) + throws IOException + { + TlsUtils.writeUint8(hash, output); + TlsUtils.writeUint8(signature, output); + } + + /** + * Parse a {@link SignatureAndHashAlgorithm} from an {@link InputStream}. + * + * @param input the {@link InputStream} to parse from. + * @return a {@link SignatureAndHashAlgorithm} object. + * @throws IOException + */ + public static SignatureAndHashAlgorithm parse(InputStream input) + throws IOException + { + short hash = TlsUtils.readUint8(input); + short signature = TlsUtils.readUint8(input); + return new SignatureAndHashAlgorithm(hash, signature); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignerInputBuffer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignerInputBuffer.java new file mode 100644 index 000000000..39a68e6c5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SignerInputBuffer.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; + +import org.spongycastle.crypto.Signer; + +class SignerInputBuffer extends ByteArrayOutputStream +{ + void updateSigner(Signer s) + { + s.update(this.buf, 0, count); + } +} \ No newline at end of file diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SupplementalDataEntry.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SupplementalDataEntry.java new file mode 100644 index 000000000..2633623d2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SupplementalDataEntry.java @@ -0,0 +1,23 @@ +package org.spongycastle.crypto.tls; + +public class SupplementalDataEntry +{ + protected int dataType; + protected byte[] data; + + public SupplementalDataEntry(int dataType, byte[] data) + { + this.dataType = dataType; + this.data = data; + } + + public int getDataType() + { + return dataType; + } + + public byte[] getData() + { + return data; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SupplementalDataType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SupplementalDataType.java new file mode 100644 index 000000000..5cabe8c3e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/SupplementalDataType.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4680 + */ +public class SupplementalDataType +{ + /* + * RFC 4681 + */ + public static final int user_mapping_data = 0; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAEADCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAEADCipher.java new file mode 100644 index 000000000..0ddc2fe5b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAEADCipher.java @@ -0,0 +1,193 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.modes.AEADBlockCipher; +import org.spongycastle.crypto.params.AEADParameters; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.util.Arrays; + +public class TlsAEADCipher + implements TlsCipher +{ + protected TlsContext context; + protected int macSize; + protected int nonce_explicit_length; + + protected AEADBlockCipher encryptCipher; + protected AEADBlockCipher decryptCipher; + + protected byte[] encryptImplicitNonce, decryptImplicitNonce; + + public TlsAEADCipher(TlsContext context, AEADBlockCipher clientWriteCipher, AEADBlockCipher serverWriteCipher, + int cipherKeySize, int macSize) throws IOException + { + if (!TlsUtils.isTLSv12(context)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + this.context = context; + this.macSize = macSize; + + // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers + this.nonce_explicit_length = 8; + + // TODO SecurityParameters.fixed_iv_length + int fixed_iv_length = 4; + + int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length); + + byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size); + + int offset = 0; + + KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + byte[] client_write_IV = Arrays.copyOfRange(key_block, offset, offset + fixed_iv_length); + offset += fixed_iv_length; + byte[] server_write_IV = Arrays.copyOfRange(key_block, offset, offset + fixed_iv_length); + offset += fixed_iv_length; + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + KeyParameter encryptKey, decryptKey; + if (context.isServer()) + { + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + this.encryptImplicitNonce = server_write_IV; + this.decryptImplicitNonce = client_write_IV; + encryptKey = server_write_key; + decryptKey = client_write_key; + } + else + { + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + this.encryptImplicitNonce = client_write_IV; + this.decryptImplicitNonce = server_write_IV; + encryptKey = client_write_key; + decryptKey = server_write_key; + } + + byte[] dummyNonce = new byte[fixed_iv_length + nonce_explicit_length]; + + this.encryptCipher.init(true, new AEADParameters(encryptKey, 8 * macSize, dummyNonce)); + this.decryptCipher.init(false, new AEADParameters(decryptKey, 8 * macSize, dummyNonce)); + } + + public int getPlaintextLimit(int ciphertextLimit) + { + // TODO We ought to be able to ask the decryptCipher (independently of it's current state!) + return ciphertextLimit - macSize - nonce_explicit_length; + } + + public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) + throws IOException + { + byte[] nonce = new byte[this.encryptImplicitNonce.length + nonce_explicit_length]; + System.arraycopy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.length); + + /* + * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number. + * + * (May need review for other AEAD ciphers). + */ + TlsUtils.writeUint64(seqNo, nonce, encryptImplicitNonce.length); + + int plaintextOffset = offset; + int plaintextLength = len; + int ciphertextLength = encryptCipher.getOutputSize(plaintextLength); + + byte[] output = new byte[nonce_explicit_length + ciphertextLength]; + System.arraycopy(nonce, encryptImplicitNonce.length, output, 0, nonce_explicit_length); + int outputPos = nonce_explicit_length; + + byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength); + AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData); + + try + { + encryptCipher.init(true, parameters); + outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos); + outputPos += encryptCipher.doFinal(output, outputPos); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (outputPos != output.length) + { + // NOTE: Existing AEAD cipher implementations all give exact output lengths + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return output; + } + + public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len) + throws IOException + { + if (getPlaintextLimit(len) < 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + byte[] nonce = new byte[this.decryptImplicitNonce.length + nonce_explicit_length]; + System.arraycopy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.length); + System.arraycopy(ciphertext, offset, nonce, decryptImplicitNonce.length, nonce_explicit_length); + + int ciphertextOffset = offset + nonce_explicit_length; + int ciphertextLength = len - nonce_explicit_length; + int plaintextLength = decryptCipher.getOutputSize(ciphertextLength); + + byte[] output = new byte[plaintextLength]; + int outputPos = 0; + + byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength); + AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData); + + try + { + decryptCipher.init(false, parameters); + outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos); + outputPos += decryptCipher.doFinal(output, outputPos); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + + if (outputPos != output.length) + { + // NOTE: Existing AEAD cipher implementations all give exact output lengths + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return output; + } + + protected byte[] getAdditionalData(long seqNo, short type, int len) + throws IOException + { + /* + * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + + * TLSCompressed.length + */ + + byte[] additional_data = new byte[13]; + TlsUtils.writeUint64(seqNo, additional_data, 0); + TlsUtils.writeUint8(type, additional_data, 8); + TlsUtils.writeVersion(context.getServerVersion(), additional_data, 9); + TlsUtils.writeUint16(len, additional_data, 11); + + return additional_data; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAgreementCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAgreementCredentials.java new file mode 100644 index 000000000..c7fe94969 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAgreementCredentials.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +public interface TlsAgreementCredentials + extends TlsCredentials +{ + + byte[] generateAgreement(AsymmetricKeyParameter peerPublicKey) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAuthentication.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAuthentication.java new file mode 100755 index 000000000..8783dd750 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsAuthentication.java @@ -0,0 +1,26 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface TlsAuthentication +{ + /** + * Called by the protocol handler to report the server certificate + * Note: this method is responsible for certificate verification and validation + * + * @param serverCertificate the server certificate received + * @throws IOException + */ + void notifyServerCertificate(Certificate serverCertificate) + throws IOException; + + /** + * Return client credentials in response to server's certificate request + * + * @param certificateRequest details of the certificate request + * @return a TlsCredentials object or null for no client authentication + * @throws IOException + */ + TlsCredentials getClientCredentials(CertificateRequest certificateRequest) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsBlockCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsBlockCipher.java new file mode 100644 index 000000000..bb290c77c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsBlockCipher.java @@ -0,0 +1,386 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.security.SecureRandom; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.util.Arrays; + +/** + * A generic TLS 1.0-1.1 / SSLv3 block cipher. This can be used for AES or 3DES for example. + */ +public class TlsBlockCipher + implements TlsCipher +{ + private static boolean encryptThenMAC = false; + + protected TlsContext context; + protected byte[] randomData; + protected boolean useExplicitIV; + + protected BlockCipher encryptCipher; + protected BlockCipher decryptCipher; + + protected TlsMac writeMac; + protected TlsMac readMac; + + public TlsMac getWriteMac() + { + return writeMac; + } + + public TlsMac getReadMac() + { + return readMac; + } + + public TlsBlockCipher(TlsContext context, BlockCipher clientWriteCipher, BlockCipher serverWriteCipher, + Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize) throws IOException + { + this.context = context; + + this.randomData = new byte[256]; + context.getSecureRandom().nextBytes(randomData); + + this.useExplicitIV = TlsUtils.isTLSv11(context); + + int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize() + + serverWriteDigest.getDigestSize(); + + // From TLS 1.1 onwards, block ciphers don't need client_write_IV + if (!useExplicitIV) + { + key_block_size += clientWriteCipher.getBlockSize() + serverWriteCipher.getBlockSize(); + } + + byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size); + + int offset = 0; + + TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.getDigestSize()); + offset += clientWriteDigest.getDigestSize(); + TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.getDigestSize()); + offset += serverWriteDigest.getDigestSize(); + + KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + + byte[] client_write_IV, server_write_IV; + if (useExplicitIV) + { + client_write_IV = new byte[clientWriteCipher.getBlockSize()]; + server_write_IV = new byte[serverWriteCipher.getBlockSize()]; + } + else + { + client_write_IV = Arrays.copyOfRange(key_block, offset, offset + clientWriteCipher.getBlockSize()); + offset += clientWriteCipher.getBlockSize(); + server_write_IV = Arrays.copyOfRange(key_block, offset, offset + serverWriteCipher.getBlockSize()); + offset += serverWriteCipher.getBlockSize(); + } + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + CipherParameters encryptParams, decryptParams; + if (context.isServer()) + { + this.writeMac = serverWriteMac; + this.readMac = clientWriteMac; + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + encryptParams = new ParametersWithIV(server_write_key, server_write_IV); + decryptParams = new ParametersWithIV(client_write_key, client_write_IV); + } + else + { + this.writeMac = clientWriteMac; + this.readMac = serverWriteMac; + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + encryptParams = new ParametersWithIV(client_write_key, client_write_IV); + decryptParams = new ParametersWithIV(server_write_key, server_write_IV); + } + + this.encryptCipher.init(true, encryptParams); + this.decryptCipher.init(false, decryptParams); + } + + public int getPlaintextLimit(int ciphertextLimit) + { + int blockSize = encryptCipher.getBlockSize(); + int macSize = writeMac.getSize(); + + int plaintextLimit = ciphertextLimit; + + // An explicit IV consumes 1 block + if (useExplicitIV) + { + plaintextLimit -= blockSize; + } + + // Leave room for the MAC, and require block-alignment + if (encryptThenMAC) + { + plaintextLimit -= macSize; + plaintextLimit -= plaintextLimit % blockSize; + } + else + { + plaintextLimit -= plaintextLimit % blockSize; + plaintextLimit -= macSize; + } + + // Minimum 1 byte of padding + --plaintextLimit; + + return plaintextLimit; + } + + public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) + { + int blockSize = encryptCipher.getBlockSize(); + int macSize = writeMac.getSize(); + + ProtocolVersion version = context.getServerVersion(); + + int enc_input_length = len; + if (!encryptThenMAC) + { + enc_input_length += macSize; + } + + int padding_length = blockSize - 1 - (enc_input_length % blockSize); + + // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though) + if (!version.isDTLS() && !version.isSSL()) + { + // Add a random number of extra blocks worth of padding + int maxExtraPadBlocks = (255 - padding_length) / blockSize; + int actualExtraPadBlocks = chooseExtraPadBlocks(context.getSecureRandom(), maxExtraPadBlocks); + padding_length += actualExtraPadBlocks * blockSize; + } + + int totalSize = len + macSize + padding_length + 1; + if (useExplicitIV) + { + totalSize += blockSize; + } + + byte[] outBuf = new byte[totalSize]; + int outOff = 0; + + if (useExplicitIV) + { + byte[] explicitIV = new byte[blockSize]; + context.getSecureRandom().nextBytes(explicitIV); + + encryptCipher.init(true, new ParametersWithIV(null, explicitIV)); + + System.arraycopy(explicitIV, 0, outBuf, outOff, blockSize); + outOff += blockSize; + } + + int blocks_start = outOff; + + System.arraycopy(plaintext, offset, outBuf, outOff, len); + outOff += len; + + if (!encryptThenMAC) + { + byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len); + System.arraycopy(mac, 0, outBuf, outOff, mac.length); + outOff += mac.length; + } + + for (int i = 0; i <= padding_length; i++) + { + outBuf[outOff++] = (byte)padding_length; + } + + for (int i = blocks_start; i < outOff; i += blockSize) + { + encryptCipher.processBlock(outBuf, i, outBuf, i); + } + + if (encryptThenMAC) + { + byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, outOff); + System.arraycopy(mac, 0, outBuf, outOff, mac.length); + outOff += mac.length; + } + +// assert outBuf.length == outOff; + + return outBuf; + } + + public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len) + throws IOException + { + int blockSize = decryptCipher.getBlockSize(); + int macSize = readMac.getSize(); + + int minLen = blockSize; + if (encryptThenMAC) + { + minLen += macSize; + } + else + { + minLen = Math.max(minLen, macSize + 1); + } + + if (useExplicitIV) + { + minLen += blockSize; + } + + if (len < minLen) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + int blocks_length = len; + if (encryptThenMAC) + { + blocks_length -= macSize; + } + + if (blocks_length % blockSize != 0) + { + throw new TlsFatalAlert(AlertDescription.decryption_failed); + } + + if (encryptThenMAC) + { + int end = offset + len; + byte[] receivedMac = Arrays.copyOfRange(ciphertext, end - macSize, end); + byte[] calculatedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, len - macSize); + + boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac); + + if (badMac) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + } + + if (useExplicitIV) + { + decryptCipher.init(false, new ParametersWithIV(null, ciphertext, offset, blockSize)); + + offset += blockSize; + blocks_length -= blockSize; + } + + for (int i = 0; i < blocks_length; i += blockSize) + { + decryptCipher.processBlock(ciphertext, offset + i, ciphertext, offset + i); + } + + // If there's anything wrong with the padding, this will return zero + int totalPad = checkPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMAC ? 0 : macSize); + + int dec_output_length = blocks_length - totalPad; + + if (!encryptThenMAC) + { + dec_output_length -= macSize; + int macInputLen = dec_output_length; + int macOff = offset + macInputLen; + byte[] receivedMac = Arrays.copyOfRange(ciphertext, macOff, macOff + macSize); + byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, + blocks_length - macSize, randomData); + + boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac); + + if (badMac || totalPad == 0) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + } + + return Arrays.copyOfRange(ciphertext, offset, offset + dec_output_length); + } + + protected int checkPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize) + { + int end = off + len; + byte lastByte = buf[end - 1]; + int padlen = lastByte & 0xff; + int totalPad = padlen + 1; + + int dummyIndex = 0; + byte padDiff = 0; + + if ((TlsUtils.isSSL(context) && totalPad > blockSize) || (macSize + totalPad > len)) + { + totalPad = 0; + } + else + { + int padPos = end - totalPad; + do + { + padDiff |= (buf[padPos++] ^ lastByte); + } + while (padPos < end); + + dummyIndex = totalPad; + + if (padDiff != 0) + { + totalPad = 0; + } + } + + // Run some extra dummy checks so the number of checks is always constant + { + byte[] dummyPad = randomData; + while (dummyIndex < 256) + { + padDiff |= (dummyPad[dummyIndex++] ^ lastByte); + } + // Ensure the above loop is not eliminated + dummyPad[0] ^= padDiff; + } + + return totalPad; + } + + protected int chooseExtraPadBlocks(SecureRandom r, int max) + { + // return r.nextInt(max + 1); + + int x = r.nextInt(); + int n = lowestBitSet(x); + return Math.min(n, max); + } + + protected int lowestBitSet(int x) + { + if (x == 0) + { + return 32; + } + + int n = 0; + while ((x & 1) == 0) + { + ++n; + x >>= 1; + } + return n; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCipher.java new file mode 100644 index 000000000..6a8f6bc0c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCipher.java @@ -0,0 +1,14 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface TlsCipher +{ + int getPlaintextLimit(int ciphertextLimit); + + byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) + throws IOException; + + byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCipherFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCipherFactory.java new file mode 100644 index 000000000..8f0d3fefa --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCipherFactory.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface TlsCipherFactory +{ + + /** + * See enumeration classes EncryptionAlgorithm, MACAlgorithm for appropriate argument values + */ + TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClient.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClient.java new file mode 100644 index 000000000..2b56d4257 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClient.java @@ -0,0 +1,79 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; + +public interface TlsClient + extends TlsPeer +{ + void init(TlsClientContext context); + + /** + * Return the session this client wants to resume, if any. Note that the peer's certificate + * chain for the session (if any) may need to be periodically revalidated. + * + * @return A {@link TlsSession} representing the resumable session to be used for this + * connection, or null to use a new session. + * @see SessionParameters#getPeerCertificate() + */ + TlsSession getSessionToResume(); + + ProtocolVersion getClientHelloRecordLayerVersion(); + + ProtocolVersion getClientVersion(); + + int[] getCipherSuites(); + + short[] getCompressionMethods(); + + // Hashtable is (Integer -> byte[]) + Hashtable getClientExtensions() + throws IOException; + + void notifyServerVersion(ProtocolVersion selectedVersion) + throws IOException; + + /** + * Notifies the client of the session_id sent in the ServerHello. + * + * @param sessionID + * @see {@link TlsContext#getResumableSession()} + */ + void notifySessionID(byte[] sessionID); + + void notifySelectedCipherSuite(int selectedCipherSuite); + + void notifySelectedCompressionMethod(short selectedCompressionMethod); + + // Hashtable is (Integer -> byte[]) + void processServerExtensions(Hashtable serverExtensions) + throws IOException; + + // Vector is (SupplementalDataEntry) + void processServerSupplementalData(Vector serverSupplementalData) + throws IOException; + + TlsKeyExchange getKeyExchange() + throws IOException; + + TlsAuthentication getAuthentication() + throws IOException; + + // Vector is (SupplementalDataEntry) + Vector getClientSupplementalData() + throws IOException; + + /** + * RFC 5077 3.3. NewSessionTicket Handshake Message + * + * This method will be called (only) when a NewSessionTicket handshake message is received. The + * ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption + * that it complies with e.g. RFC 5077 4. Recommended Ticket Construction. + * + * @param newSessionTicket The ticket. + * @throws IOException + */ + void notifyNewSessionTicket(NewSessionTicket newSessionTicket) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientContext.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientContext.java new file mode 100644 index 000000000..245245b2f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientContext.java @@ -0,0 +1,6 @@ +package org.spongycastle.crypto.tls; + +public interface TlsClientContext + extends TlsContext +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientContextImpl.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientContextImpl.java new file mode 100644 index 000000000..ca3929444 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientContextImpl.java @@ -0,0 +1,18 @@ +package org.spongycastle.crypto.tls; + +import java.security.SecureRandom; + +class TlsClientContextImpl + extends AbstractTlsContext + implements TlsClientContext +{ + TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + { + super(secureRandom, securityParameters); + } + + public boolean isServer() + { + return false; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientProtocol.java new file mode 100644 index 000000000..c6b56a9e8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsClientProtocol.java @@ -0,0 +1,884 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.crypto.prng.ThreadedSeedGenerator; +import org.spongycastle.util.Arrays; + +public class TlsClientProtocol + extends TlsProtocol +{ + protected TlsClient tlsClient = null; + protected TlsClientContextImpl tlsClientContext = null; + + protected byte[] selectedSessionID = null; + + protected TlsKeyExchange keyExchange = null; + protected TlsAuthentication authentication = null; + + protected CertificateStatus certificateStatus = null; + protected CertificateRequest certificateRequest = null; + + private static SecureRandom createSecureRandom() + { + /* + * We use our threaded seed generator to generate a good random seed. If the user has a + * better random seed, he should use the constructor with a SecureRandom. + */ + ThreadedSeedGenerator tsg = new ThreadedSeedGenerator(); + SecureRandom random = new SecureRandom(); + + /* + * Hopefully, 20 bytes in fast mode are good enough. + */ + random.setSeed(tsg.generateSeed(20, true)); + + return random; + } + + public TlsClientProtocol(InputStream input, OutputStream output) + { + this(input, output, createSecureRandom()); + } + + public TlsClientProtocol(InputStream input, OutputStream output, SecureRandom secureRandom) + { + super(input, output, secureRandom); + } + + /** + * Initiates a TLS handshake in the role of client + * + * @param tlsClient The {@link TlsClient} to use for the handshake. + * @throws IOException If handshake was not successful. + */ + public void connect(TlsClient tlsClient) throws IOException + { + if (tlsClient == null) + { + throw new IllegalArgumentException("'tlsClient' cannot be null"); + } + if (this.tlsClient != null) + { + throw new IllegalStateException("'connect' can only be called once"); + } + + this.tlsClient = tlsClient; + + this.securityParameters = new SecurityParameters(); + this.securityParameters.entity = ConnectionEnd.client; + this.securityParameters.clientRandom = createRandomBlock(secureRandom); + + this.tlsClientContext = new TlsClientContextImpl(secureRandom, securityParameters); + this.tlsClient.init(tlsClientContext); + this.recordStream.init(tlsClientContext); + + TlsSession sessionToResume = tlsClient.getSessionToResume(); + if (sessionToResume != null) + { + SessionParameters sessionParameters = sessionToResume.exportSessionParameters(); + if (sessionParameters != null) + { + this.tlsSession = sessionToResume; + this.sessionParameters = sessionParameters; + } + } + + sendClientHelloMessage(); + this.connection_state = CS_CLIENT_HELLO; + + completeHandshake(); + } + + protected void cleanupHandshake() + { + super.cleanupHandshake(); + + this.selectedSessionID = null; + this.keyExchange = null; + this.authentication = null; + this.certificateStatus = null; + this.certificateRequest = null; + } + + protected AbstractTlsContext getContext() + { + return tlsClientContext; + } + + protected TlsPeer getPeer() + { + return tlsClient; + } + + protected void handleHandshakeMessage(short type, byte[] data) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(data); + + if (this.resumedSession) + { + if (type != HandshakeType.finished || this.connection_state != CS_SERVER_HELLO) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + processFinishedMessage(buf); + this.connection_state = CS_SERVER_FINISHED; + + sendFinishedMessage(); + this.connection_state = CS_CLIENT_FINISHED; + this.connection_state = CS_END; + + return; + } + + switch (type) + { + case HandshakeType.certificate: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO: + { + handleSupplementalData(null); + // NB: Fall through to next case label + } + case CS_SERVER_SUPPLEMENTAL_DATA: + { + // Parse the Certificate message and send to cipher suite + + this.peerCertificate = Certificate.parse(buf); + + assertEmpty(buf); + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (this.peerCertificate == null || this.peerCertificate.isEmpty()) + { + this.allowCertificateStatus = false; + } + + this.keyExchange.processServerCertificate(this.peerCertificate); + + this.authentication = tlsClient.getAuthentication(); + this.authentication.notifyServerCertificate(this.peerCertificate); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.connection_state = CS_SERVER_CERTIFICATE; + break; + } + case HandshakeType.certificate_status: + { + switch (this.connection_state) + { + case CS_SERVER_CERTIFICATE: + { + if (!this.allowCertificateStatus) + { + /* + * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the + * server MUST have included an extension of type "status_request" with empty + * "extension_data" in the extended server hello.. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.certificateStatus = CertificateStatus.parse(buf); + + assertEmpty(buf); + + // TODO[RFC 3546] Figure out how to provide this to the client/authentication. + + this.connection_state = CS_CERTIFICATE_STATUS; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.finished: + { + switch (this.connection_state) + { + case CS_CLIENT_FINISHED: + { + processFinishedMessage(buf); + this.connection_state = CS_SERVER_FINISHED; + this.connection_state = CS_END; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.server_hello: + { + switch (this.connection_state) + { + case CS_CLIENT_HELLO: + { + receiveServerHelloMessage(buf); + this.connection_state = CS_SERVER_HELLO; + + if (this.securityParameters.maxFragmentLength >= 0) + { + int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength); + recordStream.setPlaintextLimit(plainTextLimit); + } + + this.securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(), + this.securityParameters.getCipherSuite()); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify + * verify_data_length has a verify_data_length equal to 12. This includes all + * existing cipher suites. + */ + this.securityParameters.verifyDataLength = 12; + + this.recordStream.notifyHelloComplete(); + + if (this.resumedSession) + { + this.securityParameters.masterSecret = Arrays.clone(this.sessionParameters.getMasterSecret()); + this.recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); + + sendChangeCipherSpecMessage(); + } + else + { + invalidateSession(); + + if (this.selectedSessionID.length > 0) + { + this.tlsSession = new TlsSessionImpl(this.selectedSessionID, null); + } + } + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.supplemental_data: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO: + { + handleSupplementalData(readSupplementalDataMessage(buf)); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.server_hello_done: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO: + { + handleSupplementalData(null); + // NB: Fall through to next case label + } + case CS_SERVER_SUPPLEMENTAL_DATA: + { + // There was no server certificate message; check it's OK + this.keyExchange.skipServerCredentials(); + this.authentication = null; + + // NB: Fall through to next case label + } + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + { + // There was no server key exchange message; check it's OK + this.keyExchange.skipServerKeyExchange(); + + // NB: Fall through to next case label + } + case CS_SERVER_KEY_EXCHANGE: + case CS_CERTIFICATE_REQUEST: + { + assertEmpty(buf); + + this.connection_state = CS_SERVER_HELLO_DONE; + + this.recordStream.getHandshakeHash().sealHashAlgorithms(); + + Vector clientSupplementalData = tlsClient.getClientSupplementalData(); + if (clientSupplementalData != null) + { + sendSupplementalDataMessage(clientSupplementalData); + } + this.connection_state = CS_CLIENT_SUPPLEMENTAL_DATA; + + TlsCredentials clientCreds = null; + if (certificateRequest == null) + { + this.keyExchange.skipClientCredentials(); + } + else + { + clientCreds = this.authentication.getClientCredentials(certificateRequest); + + if (clientCreds == null) + { + this.keyExchange.skipClientCredentials(); + + /* + * RFC 5246 If no suitable certificate is available, the client MUST send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + sendCertificateMessage(Certificate.EMPTY_CHAIN); + } + else + { + this.keyExchange.processClientCredentials(clientCreds); + + sendCertificateMessage(clientCreds.getCertificate()); + } + } + + this.connection_state = CS_CLIENT_CERTIFICATE; + + /* + * Send the client key exchange message, depending on the key exchange we are using + * in our CipherSuite. + */ + sendClientKeyExchangeMessage(); + this.connection_state = CS_CLIENT_KEY_EXCHANGE; + + establishMasterSecret(getContext(), keyExchange); + recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); + + TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish(); + + if (clientCreds != null && clientCreds instanceof TlsSignerCredentials) + { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds; + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + byte[] hash; + + if (TlsUtils.isTLSv12(getContext())) + { + signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm(); + if (signatureAndHashAlgorithm == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash()); + } + else + { + signatureAndHashAlgorithm = null; + hash = getCurrentPRFHash(getContext(), prepareFinishHash, null); + } + + byte[] signature = signerCredentials.generateCertificateSignature(hash); + DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature); + sendCertificateVerifyMessage(certificateVerify); + + this.connection_state = CS_CERTIFICATE_VERIFY; + } + + sendChangeCipherSpecMessage(); + sendFinishedMessage(); + this.connection_state = CS_CLIENT_FINISHED; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + break; + } + case HandshakeType.server_key_exchange: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO: + { + handleSupplementalData(null); + // NB: Fall through to next case label + } + case CS_SERVER_SUPPLEMENTAL_DATA: + { + // There was no server certificate message; check it's OK + this.keyExchange.skipServerCredentials(); + this.authentication = null; + + // NB: Fall through to next case label + } + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + { + this.keyExchange.processServerKeyExchange(buf); + + assertEmpty(buf); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.connection_state = CS_SERVER_KEY_EXCHANGE; + break; + } + case HandshakeType.certificate_request: + { + switch (this.connection_state) + { + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + { + // There was no server key exchange message; check it's OK + this.keyExchange.skipServerKeyExchange(); + + // NB: Fall through to next case label + } + case CS_SERVER_KEY_EXCHANGE: + { + if (this.authentication == null) + { + /* + * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server + * to request client identification. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + this.certificateRequest = CertificateRequest.parse(getContext(), buf); + + assertEmpty(buf); + + this.keyExchange.validateCertificateRequest(this.certificateRequest); + + /* + * TODO Give the client a chance to immediately select the CertificateVerify hash + * algorithm here to avoid tracking the other hash algorithms unnecessarily? + */ + TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(), + this.certificateRequest.getSupportedSignatureAlgorithms()); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.connection_state = CS_CERTIFICATE_REQUEST; + break; + } + case HandshakeType.session_ticket: + { + switch (this.connection_state) + { + case CS_CLIENT_FINISHED: + { + if (!this.expectSessionTicket) + { + /* + * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a + * SessionTicket extension in the ServerHello. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + /* + * RFC 5077 3.4. If the client receives a session ticket from the server, then it + * discards any Session ID that was sent in the ServerHello. + */ + invalidateSession(); + + receiveNewSessionTicketMessage(buf); + this.connection_state = CS_SERVER_SESSION_TICKET; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + case HandshakeType.hello_request: + { + assertEmpty(buf); + + /* + * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the + * client is currently negotiating a session. This message may be ignored by the client + * if it does not wish to renegotiate a session, or the client may, if it wishes, + * respond with a no_renegotiation alert. + */ + if (this.connection_state == CS_END) + { + /* + * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal + * handshake_failure alert. + */ + if (TlsUtils.isSSL(getContext())) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + String message = "Renegotiation not supported"; + raiseWarning(AlertDescription.no_renegotiation, message); + } + break; + } + case HandshakeType.client_hello: + case HandshakeType.client_key_exchange: + case HandshakeType.certificate_verify: + case HandshakeType.hello_verify_request: + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + protected void handleSupplementalData(Vector serverSupplementalData) + throws IOException + { + this.tlsClient.processServerSupplementalData(serverSupplementalData); + this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA; + + this.keyExchange = tlsClient.getKeyExchange(); + this.keyExchange.init(getContext()); + } + + protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf) + throws IOException + { + NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf); + + TlsProtocol.assertEmpty(buf); + + tlsClient.notifyNewSessionTicket(newSessionTicket); + } + + protected void receiveServerHelloMessage(ByteArrayInputStream buf) + throws IOException + { + ProtocolVersion server_version = TlsUtils.readVersion(buf); + if (server_version.isDTLS()) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + // Check that this matches what the server is sending in the record layer + if (!server_version.equals(this.recordStream.getReadVersion())) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + ProtocolVersion client_version = getContext().getClientVersion(); + if (!server_version.isEqualOrEarlierVersionOf(client_version)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.recordStream.setWriteVersion(server_version); + getContext().setServerVersion(server_version); + this.tlsClient.notifyServerVersion(server_version); + + /* + * Read the server random + */ + this.securityParameters.serverRandom = TlsUtils.readFully(32, buf); + + this.selectedSessionID = TlsUtils.readOpaque8(buf); + if (this.selectedSessionID.length > 32) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.tlsClient.notifySessionID(this.selectedSessionID); + + this.resumedSession = this.selectedSessionID.length > 0 && this.tlsSession != null + && Arrays.areEqual(this.selectedSessionID, this.tlsSession.getSessionID()); + + /* + * Find out which CipherSuite the server has chosen and check that it was one of the offered + * ones. + */ + int selectedCipherSuite = TlsUtils.readUint16(buf); + if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.tlsClient.notifySelectedCipherSuite(selectedCipherSuite); + + /* + * Find out which CompressionMethod the server has chosen and check that it was one of the + * offered ones. + */ + short selectedCompressionMethod = TlsUtils.readUint8(buf); + if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod); + + /* + * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server + * hello message when the client has requested extended functionality via the extended + * client hello message specified in Section 2.1. ... Note that the extended server hello + * message is only sent in response to an extended client hello message. This prevents the + * possibility that the extended server hello message could "break" existing TLS 1.0 + * clients. + */ + this.serverExtensions = readExtensions(buf); + + /* + * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an + * extended client hello message. + * + * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server + * Hello is always allowed. + */ + if (this.serverExtensions != null) + { + Enumeration e = this.serverExtensions.keys(); + while (e.hasMoreElements()) + { + Integer extType = (Integer)e.nextElement(); + + /* + * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a + * ClientHello containing only the SCSV is an explicit exception to the prohibition + * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is + * only allowed because the client is signaling its willingness to receive the + * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + if (extType.equals(EXT_RenegotiationInfo)) + { + continue; + } + + /* + * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions[.] + */ + if (this.resumedSession) + { + // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats + // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats + // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats +// throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + /* + * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the + * same extension type appeared in the corresponding ClientHello. If a client + * receives an extension type in ServerHello that it did not request in the + * associated ClientHello, it MUST abort the handshake with an unsupported_extension + * fatal alert. + */ + if (null == TlsUtils.getExtensionData(this.clientExtensions, extType)) + { + throw new TlsFatalAlert(AlertDescription.unsupported_extension); + } + } + } + + /* + * RFC 5746 3.4. Client Behavior: Initial Handshake + */ + { + /* + * When a ServerHello is received, the client MUST check if it includes the + * "renegotiation_info" extension: + */ + byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo); + if (renegExtData != null) + { + /* + * If the extension is present, set the secure_renegotiation flag to TRUE. The + * client MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake (by sending a fatal + * handshake_failure alert). + */ + this.secure_renegotiation = true; + + if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + } + + // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming + this.tlsClient.notifySecureRenegotiation(this.secure_renegotiation); + + Hashtable sessionClientExtensions = clientExtensions, sessionServerExtensions = serverExtensions; + if (this.resumedSession) + { + if (selectedCipherSuite != this.sessionParameters.getCipherSuite() + || selectedCompressionMethod != this.sessionParameters.getCompressionAlgorithm()) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + sessionClientExtensions = null; + sessionServerExtensions = this.sessionParameters.readServerExtensions(); + } + + this.securityParameters.cipherSuite = selectedCipherSuite; + this.securityParameters.compressionAlgorithm = selectedCompressionMethod; + + if (sessionServerExtensions != null) + { + this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(sessionClientExtensions, + sessionServerExtensions, AlertDescription.illegal_parameter); + + this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(sessionServerExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + this.allowCertificateStatus = !this.resumedSession + && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions, + TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter); + + this.expectSessionTicket = !this.resumedSession + && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions, TlsProtocol.EXT_SessionTicket, + AlertDescription.illegal_parameter); + } + + if (sessionClientExtensions != null) + { + this.tlsClient.processServerExtensions(sessionServerExtensions); + } + } + + protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify) + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify); + + certificateVerify.encode(message); + + message.writeToRecordStream(); + } + + protected void sendClientHelloMessage() + throws IOException + { + this.recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion()); + + ProtocolVersion client_version = this.tlsClient.getClientVersion(); + if (client_version.isDTLS()) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + getContext().setClientVersion(client_version); + + /* + * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a + * Session ID in the TLS ClientHello. + */ + byte[] session_id = TlsUtils.EMPTY_BYTES; + if (this.tlsSession != null) + { + session_id = this.tlsSession.getSessionID(); + if (session_id == null || session_id.length > 32) + { + session_id = TlsUtils.EMPTY_BYTES; + } + } + + this.offeredCipherSuites = this.tlsClient.getCipherSuites(); + + this.offeredCompressionMethods = this.tlsClient.getCompressionMethods(); + + if (session_id.length > 0 && this.sessionParameters != null) + { + if (!Arrays.contains(this.offeredCipherSuites, sessionParameters.getCipherSuite()) + || !Arrays.contains(this.offeredCompressionMethods, sessionParameters.getCompressionAlgorithm())) + { + session_id = TlsUtils.EMPTY_BYTES; + } + } + + this.clientExtensions = this.tlsClient.getClientExtensions(); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); + + TlsUtils.writeVersion(client_version, message); + + message.write(this.securityParameters.getClientRandom()); + + TlsUtils.writeOpaque8(session_id, message); + + // Cipher Suites (and SCSV) + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); + + boolean noSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if (noRenegExt && noSCSV) + { + // TODO Consider whether to default to a client extension instead +// this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.clientExtensions); +// this.clientExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)); + this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + TlsUtils.writeUint16ArrayWithUint16Length(offeredCipherSuites, message); + } + + TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message); + + if (clientExtensions != null) + { + writeExtensions(message, clientExtensions); + } + + message.writeToRecordStream(); + } + + protected void sendClientKeyExchangeMessage() + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange); + + this.keyExchange.generateClientKeyExchange(message); + + message.writeToRecordStream(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCompression.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCompression.java new file mode 100644 index 000000000..e6d475ceb --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCompression.java @@ -0,0 +1,10 @@ +package org.spongycastle.crypto.tls; + +import java.io.OutputStream; + +public interface TlsCompression +{ + OutputStream compress(OutputStream output); + + OutputStream decompress(OutputStream output); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsContext.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsContext.java new file mode 100644 index 000000000..5da0bb645 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsContext.java @@ -0,0 +1,41 @@ +package org.spongycastle.crypto.tls; + +import java.security.SecureRandom; + +public interface TlsContext +{ + SecureRandom getSecureRandom(); + + SecurityParameters getSecurityParameters(); + + boolean isServer(); + + ProtocolVersion getClientVersion(); + + ProtocolVersion getServerVersion(); + + /** + * Used to get the resumable session, if any, used by this connection. Only available after the + * handshake has successfully completed. + * + * @return A {@link TlsSession} representing the resumable session used by this connection, or + * null if no resumable session available. + * @see {@link TlsPeer#notifyHandshakeComplete()} + */ + TlsSession getResumableSession(); + + Object getUserObject(); + + void setUserObject(Object userObject); + + /** + * Export keying material according to RFC 5705: "Keying Material Exporters for TLS". + * + * @param asciiLabel indicates which application will use the exported keys. + * @param context_value allows the application using the exporter to mix its own data with the TLS PRF for + * the exporter output. + * @param length the number of bytes to generate + * @return a pseudorandom bit string of 'length' bytes generated from the master_secret. + */ + byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCredentials.java new file mode 100644 index 000000000..269233949 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsCredentials.java @@ -0,0 +1,6 @@ +package org.spongycastle.crypto.tls; + +public interface TlsCredentials +{ + Certificate getCertificate(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHEKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHEKeyExchange.java new file mode 100644 index 000000000..fa4bd98f1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHEKeyExchange.java @@ -0,0 +1,115 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.util.io.TeeInputStream; + +public class TlsDHEKeyExchange + extends TlsDHKeyExchange +{ + protected TlsSignerCredentials serverCredentials = null; + + public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters) + { + super(keyExchange, supportedSignatureAlgorithms, dhParameters); + } + + public void processServerCredentials(TlsCredentials serverCredentials) + throws IOException + { + if (!(serverCredentials instanceof TlsSignerCredentials)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + processServerCertificate(serverCredentials.getCertificate()); + + this.serverCredentials = (TlsSignerCredentials)serverCredentials; + } + + public byte[] generateServerKeyExchange() + throws IOException + { + if (this.dhParameters == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + DigestInputBuffer buf = new DigestInputBuffer(); + + this.dhAgreeServerPrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(), + this.dhParameters, buf); + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + Digest d; + + if (TlsUtils.isTLSv12(context)) + { + signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm(); + if (signatureAndHashAlgorithm == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash()); + } + else + { + signatureAndHashAlgorithm = null; + d = new CombinedHash(); + } + + SecurityParameters securityParameters = context.getSecurityParameters(); + d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); + d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); + buf.updateDigest(d); + + byte[] hash = new byte[d.getDigestSize()]; + d.doFinal(hash, 0); + + byte[] signature = serverCredentials.generateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.encode(buf); + + return buf.toByteArray(); + } + + public void processServerKeyExchange(InputStream input) + throws IOException + { + SecurityParameters securityParameters = context.getSecurityParameters(); + + SignerInputBuffer buf = new SignerInputBuffer(); + InputStream teeIn = new TeeInputStream(input, buf); + + ServerDHParams params = ServerDHParams.parse(teeIn); + + DigitallySigned signed_params = DigitallySigned.parse(context, input); + + Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters); + buf.updateSigner(signer); + if (!signer.verifySignature(signed_params.getSignature())) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + + this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(params.getPublicKey()); + } + + protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters) + { + Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey); + signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); + signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); + return signer; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHKeyExchange.java new file mode 100644 index 000000000..b4948d9d3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHKeyExchange.java @@ -0,0 +1,208 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Vector; + +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.crypto.util.PublicKeyFactory; + +/** + * TLS 1.0/1.1 DH key exchange. + */ +public class TlsDHKeyExchange + extends AbstractTlsKeyExchange +{ + protected static final BigInteger ONE = BigInteger.valueOf(1); + protected static final BigInteger TWO = BigInteger.valueOf(2); + + protected TlsSigner tlsSigner; + protected DHParameters dhParameters; + + protected AsymmetricKeyParameter serverPublicKey; + protected DHPublicKeyParameters dhAgreeServerPublicKey; + protected TlsAgreementCredentials agreementCredentials; + protected DHPrivateKeyParameters dhAgreeClientPrivateKey; + + protected DHPrivateKeyParameters dhAgreeServerPrivateKey; + protected DHPublicKeyParameters dhAgreeClientPublicKey; + + public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters) + { + super(keyExchange, supportedSignatureAlgorithms); + + switch (keyExchange) + { + case KeyExchangeAlgorithm.DH_RSA: + case KeyExchangeAlgorithm.DH_DSS: + this.tlsSigner = null; + break; + case KeyExchangeAlgorithm.DHE_RSA: + this.tlsSigner = new TlsRSASigner(); + break; + case KeyExchangeAlgorithm.DHE_DSS: + this.tlsSigner = new TlsDSSSigner(); + break; + default: + throw new IllegalArgumentException("unsupported key exchange algorithm"); + } + + this.dhParameters = dhParameters; + } + + public void init(TlsContext context) + { + super.init(context); + + if (this.tlsSigner != null) + { + this.tlsSigner.init(context); + } + } + + public void skipServerCredentials() + throws IOException + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public void processServerCertificate(Certificate serverCertificate) + throws IOException + { + if (serverCertificate.isEmpty()) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + org.spongycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + try + { + this.serverPublicKey = PublicKeyFactory.createKey(keyInfo); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + if (tlsSigner == null) + { + try + { + this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey); + } + catch (ClassCastException e) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement); + } + else + { + if (!tlsSigner.isValidPublicKey(this.serverPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature); + } + + super.processServerCertificate(serverCertificate); + } + + public boolean requiresServerKeyExchange() + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.DH_anon: + return true; + default: + return false; + } + } + + public void validateCertificateRequest(CertificateRequest certificateRequest) + throws IOException + { + short[] types = certificateRequest.getCertificateTypes(); + for (int i = 0; i < types.length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.rsa_fixed_dh: + case ClientCertificateType.dss_fixed_dh: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public void processClientCredentials(TlsCredentials clientCredentials) + throws IOException + { + if (clientCredentials instanceof TlsAgreementCredentials) + { + // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')? + + this.agreementCredentials = (TlsAgreementCredentials)clientCredentials; + } + else if (clientCredentials instanceof TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public void generateClientKeyExchange(OutputStream output) + throws IOException + { + /* + * RFC 2246 7.4.7.2 If the client certificate already contains a suitable Diffie-Hellman + * key, then Yc is implicit and does not need to be sent again. In this case, the Client Key + * Exchange message will be sent, but will be empty. + */ + if (agreementCredentials == null) + { + this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(), + dhAgreeServerPublicKey.getParameters(), output); + } + } + + public byte[] generatePremasterSecret() + throws IOException + { + if (agreementCredentials != null) + { + return agreementCredentials.generateAgreement(dhAgreeServerPublicKey); + } + + if (dhAgreeServerPrivateKey != null) + { + return TlsDHUtils.calculateDHBasicAgreement(dhAgreeClientPublicKey, dhAgreeServerPrivateKey); + } + + if (dhAgreeClientPrivateKey != null) + { + return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHUtils.java new file mode 100644 index 000000000..9a7a22184 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDHUtils.java @@ -0,0 +1,105 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.agreement.DHBasicAgreement; +import org.spongycastle.crypto.generators.DHBasicKeyPairGenerator; +import org.spongycastle.crypto.params.DHKeyGenerationParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.util.BigIntegers; + +public class TlsDHUtils +{ + static final BigInteger ONE = BigInteger.valueOf(1); + static final BigInteger TWO = BigInteger.valueOf(2); + + public static boolean areCompatibleParameters(DHParameters a, DHParameters b) + { + return a.getP().equals(b.getP()) && a.getG().equals(b.getG()); + } + + public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey) + { + DHBasicAgreement basicAgreement = new DHBasicAgreement(); + basicAgreement.init(privateKey); + BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey); + + /* + * RFC 5246 8.1.2. Leading bytes of Z that contain all zero bits are stripped before it is + * used as the pre_master_secret. + */ + return BigIntegers.asUnsignedByteArray(agreementValue); + } + + public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random, DHParameters dhParams) + { + DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator(); + dhGen.init(new DHKeyGenerationParameters(random, dhParams)); + return dhGen.generateKeyPair(); + } + + public static DHPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, DHParameters dhParams, + OutputStream output) throws IOException + { + AsymmetricCipherKeyPair kp = generateDHKeyPair(random, dhParams); + + DHPublicKeyParameters dh_public = (DHPublicKeyParameters) kp.getPublic(); + writeDHParameter(dh_public.getY(), output); + + return (DHPrivateKeyParameters) kp.getPrivate(); + } + + public static DHPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, DHParameters dhParams, + OutputStream output) throws IOException + { + AsymmetricCipherKeyPair kp = TlsDHUtils.generateDHKeyPair(random, dhParams); + + DHPublicKeyParameters dhPublicKey = (DHPublicKeyParameters)kp.getPublic(); + ServerDHParams params = new ServerDHParams(dhPublicKey); + params.encode(output); + + return (DHPrivateKeyParameters)kp.getPrivate(); + } + + public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) throws IOException + { + BigInteger Y = key.getY(); + DHParameters params = key.getParameters(); + BigInteger p = params.getP(); + BigInteger g = params.getG(); + + if (!p.isProbablePrime(2)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + if (g.compareTo(TWO) < 0 || g.compareTo(p.subtract(TWO)) > 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + if (Y.compareTo(TWO) < 0 || Y.compareTo(p.subtract(ONE)) > 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + // TODO See RFC 2631 for more discussion of Diffie-Hellman validation + + return key; + } + + public static BigInteger readDHParameter(InputStream input) throws IOException + { + return new BigInteger(1, TlsUtils.readOpaque16(input)); + } + + public static void writeDHParameter(BigInteger x, OutputStream output) throws IOException + { + TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDSASigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDSASigner.java new file mode 100644 index 000000000..a2899fe06 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDSASigner.java @@ -0,0 +1,85 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.digests.NullDigest; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.DSADigestSigner; + +public abstract class TlsDSASigner + extends AbstractTlsSigner +{ + public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) + throws CryptoException + { + Signer signer = makeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.update(hash, 16, 20); + } + else + { + signer.update(hash, 0, hash.length); + } + return signer.generateSignature(); + } + + public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) + throws CryptoException + { + Signer signer = makeSigner(algorithm, true, false, publicKey); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.update(hash, 16, 20); + } + else + { + signer.update(hash, 0, hash.length); + } + return signer.verifySignature(sigBytes); + } + + public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) + { + return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + } + + public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) + { + return makeSigner(algorithm, false, false, publicKey); + } + + protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning, + CipherParameters cp) + { + if ((algorithm != null) != TlsUtils.isTLSv12(context)) + { + throw new IllegalStateException(); + } + + if (algorithm != null + && (algorithm.getHash() != HashAlgorithm.sha1 || algorithm.getSignature() != getSignatureAlgorithm())) + { + throw new IllegalStateException(); + } + + Digest d = raw ? new NullDigest() : TlsUtils.createHash(HashAlgorithm.sha1); + + Signer s = new DSADigestSigner(createDSAImpl(), d); + s.init(forSigning, cp); + return s; + } + + protected abstract short getSignatureAlgorithm(); + + protected abstract DSA createDSAImpl(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDSSSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDSSSigner.java new file mode 100644 index 000000000..d4309c137 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsDSSSigner.java @@ -0,0 +1,25 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.crypto.signers.DSASigner; + +public class TlsDSSSigner + extends TlsDSASigner +{ + public boolean isValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey instanceof DSAPublicKeyParameters; + } + + protected DSA createDSAImpl() + { + return new DSASigner(); + } + + protected short getSignatureAlgorithm() + { + return SignatureAlgorithm.dsa; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECCUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECCUtils.java new file mode 100644 index 000000000..52e836fcf --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECCUtils.java @@ -0,0 +1,627 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Hashtable; + +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.agreement.ECDHBasicAgreement; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECFieldElement; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.Integers; + +public class TlsECCUtils +{ + public static final Integer EXT_elliptic_curves = Integers.valueOf(ExtensionType.elliptic_curves); + public static final Integer EXT_ec_point_formats = Integers.valueOf(ExtensionType.ec_point_formats); + + private static final String[] curveNames = new String[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1", + "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", + "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1", + "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1", + "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"}; + + public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves) throws IOException + { + extensions.put(EXT_elliptic_curves, createSupportedEllipticCurvesExtension(namedCurves)); + } + + public static void addSupportedPointFormatsExtension(Hashtable extensions, short[] ecPointFormats) + throws IOException + { + extensions.put(EXT_ec_point_formats, createSupportedPointFormatsExtension(ecPointFormats)); + } + + public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions) throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_elliptic_curves); + return extensionData == null ? null : readSupportedEllipticCurvesExtension(extensionData); + } + + public static short[] getSupportedPointFormatsExtension(Hashtable extensions) throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_ec_point_formats); + return extensionData == null ? null : readSupportedPointFormatsExtension(extensionData); + } + + public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves) throws IOException + { + if (namedCurves == null || namedCurves.length < 1) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return TlsUtils.encodeUint16ArrayWithUint16Length(namedCurves); + } + + public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException + { + if (ecPointFormats == null) + { + ecPointFormats = new short[] { ECPointFormat.uncompressed }; + } + else if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + + // NOTE: We add it at the end (lowest preference) + short[] tmp = new short[ecPointFormats.length + 1]; + System.arraycopy(ecPointFormats, 0, tmp, 0, ecPointFormats.length); + tmp[ecPointFormats.length] = ECPointFormat.uncompressed; + + ecPointFormats = tmp; + } + + return TlsUtils.encodeUint8ArrayWithUint8Length(ecPointFormats); + } + + public static int[] readSupportedEllipticCurvesExtension(byte[] extensionData) throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + int length = TlsUtils.readUint16(buf); + if (length < 2 || (length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + int[] namedCurves = TlsUtils.readUint16Array(length / 2, buf); + + TlsProtocol.assertEmpty(buf); + + return namedCurves; + } + + public static short[] readSupportedPointFormatsExtension(byte[] extensionData) throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + short length = TlsUtils.readUint8(buf); + if (length < 1) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + short[] ecPointFormats = TlsUtils.readUint8Array(length, buf); + + TlsProtocol.assertEmpty(buf); + + if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return ecPointFormats; + } + + public static String getNameOfNamedCurve(int namedCurve) + { + return isSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null; + } + + public static ECDomainParameters getParametersForNamedCurve(int namedCurve) + { + String curveName = getNameOfNamedCurve(namedCurve); + if (curveName == null) + { + return null; + } + + // Lazily created the first time a particular curve is accessed + X9ECParameters ecP = ECNamedCurveTable.getByName(curveName); + + if (ecP == null) + { + return null; + } + + // It's a bit inefficient to do this conversion every time + return new ECDomainParameters(ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed()); + } + + public static boolean hasAnySupportedNamedCurves() + { + return curveNames.length > 0; + } + + public static boolean containsECCCipherSuites(int[] cipherSuites) + { + for (int i = 0; i < cipherSuites.length; ++i) + { + if (isECCCipherSuite(cipherSuites[i])) + { + return true; + } + } + return false; + } + + public static boolean isECCCipherSuite(int cipherSuite) + { + switch (cipherSuite) + { + /* + * RFC 4492 + */ + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA: + + /* + * RFC 5289 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + + /* + * RFC 5489 + */ + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + + /* + * draft-josefsson-salsa20-tls-02 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96: + + return true; + + default: + return false; + } + } + + public static boolean areOnSameCurve(ECDomainParameters a, ECDomainParameters b) + { + // TODO Move to ECDomainParameters.equals() or other utility method? + return a.getCurve().equals(b.getCurve()) && a.getG().equals(b.getG()) && a.getN().equals(b.getN()) + && a.getH().equals(b.getH()); + } + + public static boolean isSupportedNamedCurve(int namedCurve) + { + return (namedCurve > 0 && namedCurve <= curveNames.length); + } + + public static boolean isCompressionPreferred(short[] ecPointFormats, short compressionFormat) + { + if (ecPointFormats == null) + { + return false; + } + for (int i = 0; i < ecPointFormats.length; ++i) + { + short ecPointFormat = ecPointFormats[i]; + if (ecPointFormat == ECPointFormat.uncompressed) + { + return false; + } + if (ecPointFormat == compressionFormat) + { + return true; + } + } + return false; + } + + public static byte[] serializeECFieldElement(int fieldSize, BigInteger x) throws IOException + { + return BigIntegers.asUnsignedByteArray((fieldSize + 7) / 8, x); + } + + public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point) throws IOException + { + ECCurve curve = point.getCurve(); + + /* + * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the + * format MUST conform to what the server has requested through a Supported Point Formats + * Extension if this extension was used, and MUST be uncompressed if this extension was not + * used. + */ + boolean compressed = false; + if (curve instanceof ECCurve.F2m) + { + compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2); + } + else if (curve instanceof ECCurve.Fp) + { + compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime); + } + return point.getEncoded(compressed); + } + + public static byte[] serializeECPublicKey(short[] ecPointFormats, ECPublicKeyParameters keyParameters) + throws IOException + { + return serializeECPoint(ecPointFormats, keyParameters.getQ()); + } + + public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding) throws IOException + { + int requiredLength = (fieldSize + 7) / 8; + if (encoding.length != requiredLength) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + return new BigInteger(1, encoding); + } + + public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding) throws IOException + { + /* + * NOTE: Here we implicitly decode compressed or uncompressed encodings. DefaultTlsClient by + * default is set up to advertise that we can parse any encoding so this works fine, but + * extra checks might be needed here if that were changed. + */ + // TODO Review handling of infinity and hybrid encodings + return curve.decodePoint(encoding); + } + + public static ECPublicKeyParameters deserializeECPublicKey(short[] ecPointFormats, ECDomainParameters curve_params, + byte[] encoding) throws IOException + { + try + { + ECPoint Y = deserializeECPoint(ecPointFormats, curve_params.getCurve(), encoding); + return new ECPublicKeyParameters(Y, curve_params); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public static byte[] calculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) + { + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.init(privateKey); + BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey); + + /* + * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by + * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for + * any given field; leading zeros found in this octet string MUST NOT be truncated. + */ + return BigIntegers.asUnsignedByteArray(basicAgreement.getFieldSize(), agreementValue); + } + + public static AsymmetricCipherKeyPair generateECKeyPair(SecureRandom random, ECDomainParameters ecParams) + { + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + keyPairGenerator.init(new ECKeyGenerationParameters(ecParams, random)); + return keyPairGenerator.generateKeyPair(); + } + + public static ECPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, short[] ecPointFormats, + ECDomainParameters ecParams, OutputStream output) throws IOException + { + AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(random, ecParams); + + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic(); + writeECPoint(ecPointFormats, ecPublicKey.getQ(), output); + + return (ECPrivateKeyParameters) kp.getPrivate(); + } + + public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key) throws IOException + { + // TODO Check RFC 4492 for validation + return key; + } + + public static int readECExponent(int fieldSize, InputStream input) throws IOException + { + BigInteger K = readECParameter(input); + if (K.bitLength() < 32) + { + int k = K.intValue(); + if (k > 0 && k < fieldSize) + { + return k; + } + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public static BigInteger readECFieldElement(int fieldSize, InputStream input) throws IOException + { + return deserializeECFieldElement(fieldSize, TlsUtils.readOpaque8(input)); + } + + public static BigInteger readECParameter(InputStream input) throws IOException + { + // TODO Are leading zeroes okay here? + return new BigInteger(1, TlsUtils.readOpaque8(input)); + } + + public static ECDomainParameters readECParameters(int[] namedCurves, short[] ecPointFormats, InputStream input) + throws IOException + { + try + { + short curveType = TlsUtils.readUint8(input); + + switch (curveType) + { + case ECCurveType.explicit_prime: + { + checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves); + + BigInteger prime_p = readECParameter(input); + BigInteger a = readECFieldElement(prime_p.bitLength(), input); + BigInteger b = readECFieldElement(prime_p.bitLength(), input); + ECCurve curve = new ECCurve.Fp(prime_p, a, b); + ECPoint base = deserializeECPoint(ecPointFormats, curve, TlsUtils.readOpaque8(input)); + BigInteger order = readECParameter(input); + BigInteger cofactor = readECParameter(input); + return new ECDomainParameters(curve, base, order, cofactor); + } + case ECCurveType.explicit_char2: + { + checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves); + + int m = TlsUtils.readUint16(input); + short basis = TlsUtils.readUint8(input); + ECCurve curve; + switch (basis) { + case ECBasisType.ec_basis_trinomial: + { + int k = readECExponent(m, input); + BigInteger a = readECFieldElement(m, input); + BigInteger b = readECFieldElement(m, input); + curve = new ECCurve.F2m(m, k, a, b); + break; + } + case ECBasisType.ec_basis_pentanomial: + { + int k1 = readECExponent(m, input); + int k2 = readECExponent(m, input); + int k3 = readECExponent(m, input); + BigInteger a = readECFieldElement(m, input); + BigInteger b = readECFieldElement(m, input); + curve = new ECCurve.F2m(m, k1, k2, k3, a, b); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + ECPoint base = deserializeECPoint(ecPointFormats, curve, TlsUtils.readOpaque8(input)); + BigInteger order = readECParameter(input); + BigInteger cofactor = readECParameter(input); + return new ECDomainParameters(curve, base, order, cofactor); + } + case ECCurveType.named_curve: + { + int namedCurve = TlsUtils.readUint16(input); + if (!NamedCurve.refersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a + * specific curve. Values of NamedCurve that indicate support for a class of + * explicitly defined curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + checkNamedCurve(namedCurves, namedCurve); + + return TlsECCUtils.getParametersForNamedCurve(namedCurve); + } + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + private static void checkNamedCurve(int[] namedCurves, int namedCurve) throws IOException + { + if (namedCurves != null && !Arrays.contains(namedCurves, namedCurve)) + { + /* + * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite + * unless they can complete the handshake while respecting the choice of curves + * and compression techniques specified by the client. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public static void writeECExponent(int k, OutputStream output) throws IOException + { + BigInteger K = BigInteger.valueOf(k); + writeECParameter(K, output); + } + + public static void writeECFieldElement(ECFieldElement x, OutputStream output) throws IOException + { + TlsUtils.writeOpaque8(x.getEncoded(), output); + } + + public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output) throws IOException + { + TlsUtils.writeOpaque8(serializeECFieldElement(fieldSize, x), output); + } + + public static void writeECParameter(BigInteger x, OutputStream output) throws IOException + { + TlsUtils.writeOpaque8(BigIntegers.asUnsignedByteArray(x), output); + } + + public static void writeExplicitECParameters(short[] ecPointFormats, ECDomainParameters ecParameters, + OutputStream output) throws IOException + { + ECCurve curve = ecParameters.getCurve(); + if (curve instanceof ECCurve.Fp) + { + TlsUtils.writeUint8(ECCurveType.explicit_prime, output); + + ECCurve.Fp fp = (ECCurve.Fp) curve; + writeECParameter(fp.getQ(), output); + } + else if (curve instanceof ECCurve.F2m) + { + TlsUtils.writeUint8(ECCurveType.explicit_char2, output); + + ECCurve.F2m f2m = (ECCurve.F2m) curve; + int m = f2m.getM(); + TlsUtils.checkUint16(m); + TlsUtils.writeUint16(m, output); + + if (f2m.isTrinomial()) + { + TlsUtils.writeUint8(ECBasisType.ec_basis_trinomial, output); + writeECExponent(f2m.getK1(), output); + } + else + { + TlsUtils.writeUint8(ECBasisType.ec_basis_pentanomial, output); + writeECExponent(f2m.getK1(), output); + writeECExponent(f2m.getK2(), output); + writeECExponent(f2m.getK3(), output); + } + + } + else + { + throw new IllegalArgumentException("'ecParameters' not a known curve type"); + } + + writeECFieldElement(curve.getA(), output); + writeECFieldElement(curve.getB(), output); + TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, ecParameters.getG()), output); + writeECParameter(ecParameters.getN(), output); + writeECParameter(ecParameters.getH(), output); + } + + public static void writeECPoint(short[] ecPointFormats, ECPoint point, OutputStream output) throws IOException + { + TlsUtils.writeOpaque8(TlsECCUtils.serializeECPoint(ecPointFormats, point), output); + } + + public static void writeNamedECParameters(int namedCurve, OutputStream output) throws IOException + { + if (!NamedCurve.refersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific + * curve. Values of NamedCurve that indicate support for a class of explicitly defined + * curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + TlsUtils.writeUint8(ECCurveType.named_curve, output); + TlsUtils.checkUint16(namedCurve); + TlsUtils.writeUint16(namedCurve, output); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDHEKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDHEKeyExchange.java new file mode 100644 index 000000000..c1a1021a8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDHEKeyExchange.java @@ -0,0 +1,221 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.io.TeeInputStream; + +/** + * ECDHE key exchange (see RFC 4492) + */ +public class TlsECDHEKeyExchange + extends TlsECDHKeyExchange +{ + protected TlsSignerCredentials serverCredentials = null; + + public TlsECDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves, + short[] clientECPointFormats, short[] serverECPointFormats) + { + super(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats); + } + + public void processServerCredentials(TlsCredentials serverCredentials) + throws IOException + { + if (!(serverCredentials instanceof TlsSignerCredentials)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + processServerCertificate(serverCredentials.getCertificate()); + + this.serverCredentials = (TlsSignerCredentials)serverCredentials; + } + + public byte[] generateServerKeyExchange() + throws IOException + { + /* + * First we try to find a supported named curve from the client's list. + */ + int namedCurve = -1; + if (namedCurves == null) + { + // TODO Let the peer choose the default named curve + namedCurve = NamedCurve.secp256r1; + } + else + { + for (int i = 0; i < namedCurves.length; ++i) + { + int entry = namedCurves[i]; + if (TlsECCUtils.isSupportedNamedCurve(entry)) + { + namedCurve = entry; + break; + } + } + } + + ECDomainParameters curve_params = null; + if (namedCurve >= 0) + { + curve_params = TlsECCUtils.getParametersForNamedCurve(namedCurve); + } + else + { + /* + * If no named curves are suitable, check if the client supports explicit curves. + */ + if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves)) + { + curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.secp256r1); + } + else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves)) + { + curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect283r1); + } + } + + if (curve_params == null) + { + /* + * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find + * a suitable curve. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), curve_params); + this.ecAgreePrivateKey = (ECPrivateKeyParameters)kp.getPrivate(); + + DigestInputBuffer buf = new DigestInputBuffer(); + + if (namedCurve < 0) + { + TlsECCUtils.writeExplicitECParameters(clientECPointFormats, curve_params, buf); + } + else + { + TlsECCUtils.writeNamedECParameters(namedCurve, buf); + } + + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic(); + TlsECCUtils.writeECPoint(clientECPointFormats, ecPublicKey.getQ(), buf); + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + Digest d; + + if (TlsUtils.isTLSv12(context)) + { + signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm(); + if (signatureAndHashAlgorithm == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash()); + } + else + { + signatureAndHashAlgorithm = null; + d = new CombinedHash(); + } + + SecurityParameters securityParameters = context.getSecurityParameters(); + d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); + d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); + buf.updateDigest(d); + + byte[] hash = new byte[d.getDigestSize()]; + d.doFinal(hash, 0); + + byte[] signature = serverCredentials.generateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.encode(buf); + + return buf.toByteArray(); + } + + public void processServerKeyExchange(InputStream input) + throws IOException + { + SecurityParameters securityParameters = context.getSecurityParameters(); + + SignerInputBuffer buf = new SignerInputBuffer(); + InputStream teeIn = new TeeInputStream(input, buf); + + ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, teeIn); + + byte[] point = TlsUtils.readOpaque8(teeIn); + + DigitallySigned signed_params = DigitallySigned.parse(context, input); + + Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters); + buf.updateSigner(signer); + if (!signer.verifySignature(signed_params.getSignature())) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + + this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey( + clientECPointFormats, curve_params, point)); + } + + public void validateCertificateRequest(CertificateRequest certificateRequest) + throws IOException + { + /* + * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with + * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because + * the use of a long-term ECDH client key would jeopardize the forward secrecy property of + * these algorithms. + */ + short[] types = certificateRequest.getCertificateTypes(); + for (int i = 0; i < types.length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public void processClientCredentials(TlsCredentials clientCredentials) + throws IOException + { + if (clientCredentials instanceof TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters) + { + Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey); + signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); + signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); + return signer; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDHKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDHKeyExchange.java new file mode 100644 index 000000000..87ecaedba --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDHKeyExchange.java @@ -0,0 +1,219 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.util.PublicKeyFactory; + +/** + * ECDH key exchange (see RFC 4492) + */ +public class TlsECDHKeyExchange extends AbstractTlsKeyExchange +{ + protected TlsSigner tlsSigner; + protected int[] namedCurves; + protected short[] clientECPointFormats, serverECPointFormats; + + protected AsymmetricKeyParameter serverPublicKey; + protected TlsAgreementCredentials agreementCredentials; + + protected ECPrivateKeyParameters ecAgreePrivateKey; + protected ECPublicKeyParameters ecAgreePublicKey; + + public TlsECDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves, + short[] clientECPointFormats, short[] serverECPointFormats) + { + super(keyExchange, supportedSignatureAlgorithms); + + switch (keyExchange) + { + case KeyExchangeAlgorithm.ECDHE_RSA: + this.tlsSigner = new TlsRSASigner(); + break; + case KeyExchangeAlgorithm.ECDHE_ECDSA: + this.tlsSigner = new TlsECDSASigner(); + break; + case KeyExchangeAlgorithm.ECDH_RSA: + case KeyExchangeAlgorithm.ECDH_ECDSA: + this.tlsSigner = null; + break; + default: + throw new IllegalArgumentException("unsupported key exchange algorithm"); + } + + this.keyExchange = keyExchange; + this.namedCurves = namedCurves; + this.clientECPointFormats = clientECPointFormats; + this.serverECPointFormats = serverECPointFormats; + } + + public void init(TlsContext context) + { + super.init(context); + + if (this.tlsSigner != null) + { + this.tlsSigner.init(context); + } + } + + public void skipServerCredentials() throws IOException + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public void processServerCertificate(Certificate serverCertificate) throws IOException + { + if (serverCertificate.isEmpty()) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + org.spongycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + try + { + this.serverPublicKey = PublicKeyFactory.createKey(keyInfo); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + if (tlsSigner == null) + { + try + { + this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey((ECPublicKeyParameters) this.serverPublicKey); + } + catch (ClassCastException e) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement); + } + else + { + if (!tlsSigner.isValidPublicKey(this.serverPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature); + } + + super.processServerCertificate(serverCertificate); + } + + public boolean requiresServerKeyExchange() + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.ECDHE_ECDSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + case KeyExchangeAlgorithm.ECDH_anon: + return true; + default: + return false; + } + } + + public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException + { + /* + * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with + * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because + * the use of a long-term ECDH client key would jeopardize the forward secrecy property of + * these algorithms. + */ + short[] types = certificateRequest.getCertificateTypes(); + for (int i = 0; i < types.length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + case ClientCertificateType.rsa_fixed_ecdh: + case ClientCertificateType.ecdsa_fixed_ecdh: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public void processClientCredentials(TlsCredentials clientCredentials) throws IOException + { + if (clientCredentials instanceof TlsAgreementCredentials) + { + // TODO Validate client cert has matching parameters (see 'TlsECCUtils.areOnSameCurve')? + + this.agreementCredentials = (TlsAgreementCredentials) clientCredentials; + } + else if (clientCredentials instanceof TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public void generateClientKeyExchange(OutputStream output) throws IOException + { + if (agreementCredentials == null) + { + this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(), + serverECPointFormats, ecAgreePublicKey.getParameters(), output); + } + } + + public void processClientCertificate(Certificate clientCertificate) throws IOException + { + // TODO Extract the public key + // TODO If the certificate is 'fixed', take the public key as ecAgreeClientPublicKey + } + + public void processClientKeyExchange(InputStream input) throws IOException + { + if (ecAgreePublicKey != null) + { + // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate + return; + } + + byte[] point = TlsUtils.readOpaque8(input); + + ECDomainParameters curve_params = this.ecAgreePrivateKey.getParameters(); + + this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey( + serverECPointFormats, curve_params, point)); + } + + public byte[] generatePremasterSecret() throws IOException + { + if (agreementCredentials != null) + { + return agreementCredentials.generateAgreement(ecAgreePublicKey); + } + + if (ecAgreePrivateKey != null) + { + return TlsECCUtils.calculateECDHBasicAgreement(ecAgreePublicKey, ecAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDSASigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDSASigner.java new file mode 100644 index 000000000..c3bd67c98 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsECDSASigner.java @@ -0,0 +1,25 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.signers.ECDSASigner; + +public class TlsECDSASigner + extends TlsDSASigner +{ + public boolean isValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey instanceof ECPublicKeyParameters; + } + + protected DSA createDSAImpl() + { + return new ECDSASigner(); + } + + protected short getSignatureAlgorithm() + { + return SignatureAlgorithm.ecdsa; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsEncryptionCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsEncryptionCredentials.java new file mode 100644 index 000000000..a8e7d8882 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsEncryptionCredentials.java @@ -0,0 +1,10 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface TlsEncryptionCredentials + extends TlsCredentials +{ + byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret) + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsExtensionsUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsExtensionsUtils.java new file mode 100644 index 000000000..07ccb199e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsExtensionsUtils.java @@ -0,0 +1,240 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.util.Integers; + +public class TlsExtensionsUtils +{ + public static final Integer EXT_heartbeat = Integers.valueOf(ExtensionType.heartbeat); + public static final Integer EXT_max_fragment_length = Integers.valueOf(ExtensionType.max_fragment_length); + public static final Integer EXT_server_name = Integers.valueOf(ExtensionType.server_name); + public static final Integer EXT_status_request = Integers.valueOf(ExtensionType.status_request); + public static final Integer EXT_truncated_hmac = Integers.valueOf(ExtensionType.truncated_hmac); + + public static Hashtable ensureExtensionsInitialised(Hashtable extensions) + { + return extensions == null ? new Hashtable() : extensions; + } + + public static void addHeartbeatExtension(Hashtable extensions, HeartbeatExtension heartbeatExtension) + throws IOException + { + extensions.put(EXT_heartbeat, createHeartbeatExtension(heartbeatExtension)); + } + + public static void addMaxFragmentLengthExtension(Hashtable extensions, short maxFragmentLength) + throws IOException + { + extensions.put(EXT_max_fragment_length, createMaxFragmentLengthExtension(maxFragmentLength)); + } + + public static void addServerNameExtension(Hashtable extensions, ServerNameList serverNameList) + throws IOException + { + extensions.put(EXT_server_name, createServerNameExtension(serverNameList)); + } + + public static void addStatusRequestExtension(Hashtable extensions, CertificateStatusRequest statusRequest) + throws IOException + { + extensions.put(EXT_status_request, createStatusRequestExtension(statusRequest)); + } + + public static void addTruncatedHMacExtension(Hashtable extensions) + { + extensions.put(EXT_truncated_hmac, createTruncatedHMacExtension()); + } + + public static HeartbeatExtension getHeartbeatExtension(Hashtable extensions) + throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_heartbeat); + return extensionData == null ? null : readHeartbeatExtension(extensionData); + } + + public static short getMaxFragmentLengthExtension(Hashtable extensions) + throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_max_fragment_length); + return extensionData == null ? -1 : readMaxFragmentLengthExtension(extensionData); + } + + public static ServerNameList getServerNameExtension(Hashtable extensions) + throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_server_name); + return extensionData == null ? null : readServerNameExtension(extensionData); + } + + public static CertificateStatusRequest getStatusRequestExtension(Hashtable extensions) + throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_status_request); + return extensionData == null ? null : readStatusRequestExtension(extensionData); + } + + public static boolean hasTruncatedHMacExtension(Hashtable extensions) throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_truncated_hmac); + return extensionData == null ? false : readTruncatedHMacExtension(extensionData); + } + + public static byte[] createEmptyExtensionData() + { + return TlsUtils.EMPTY_BYTES; + } + + public static byte[] createHeartbeatExtension(HeartbeatExtension heartbeatExtension) + throws IOException + { + if (heartbeatExtension == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + heartbeatExtension.encode(buf); + + return buf.toByteArray(); + } + + public static byte[] createMaxFragmentLengthExtension(short maxFragmentLength) + throws IOException + { + if (!MaxFragmentLength.isValid(maxFragmentLength)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return new byte[]{ (byte)maxFragmentLength }; + } + + public static byte[] createServerNameExtension(ServerNameList serverNameList) + throws IOException + { + if (serverNameList == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + serverNameList.encode(buf); + + return buf.toByteArray(); + } + + public static byte[] createStatusRequestExtension(CertificateStatusRequest statusRequest) + throws IOException + { + if (statusRequest == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + statusRequest.encode(buf); + + return buf.toByteArray(); + } + + public static byte[] createTruncatedHMacExtension() + { + return createEmptyExtensionData(); + } + + public static HeartbeatExtension readHeartbeatExtension(byte[] extensionData) + throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + HeartbeatExtension heartbeatExtension = HeartbeatExtension.parse(buf); + + TlsProtocol.assertEmpty(buf); + + return heartbeatExtension; + } + + public static short readMaxFragmentLengthExtension(byte[] extensionData) + throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + if (extensionData.length != 1) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + short maxFragmentLength = (short)extensionData[0]; + + if (!MaxFragmentLength.isValid(maxFragmentLength)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return maxFragmentLength; + } + + public static ServerNameList readServerNameExtension(byte[] extensionData) + throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + ServerNameList serverNameList = ServerNameList.parse(buf); + + TlsProtocol.assertEmpty(buf); + + return serverNameList; + } + + public static CertificateStatusRequest readStatusRequestExtension(byte[] extensionData) + throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + CertificateStatusRequest statusRequest = CertificateStatusRequest.parse(buf); + + TlsProtocol.assertEmpty(buf); + + return statusRequest; + } + + private static boolean readTruncatedHMacExtension(byte[] extensionData) throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + if (extensionData.length != 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsFatalAlert.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsFatalAlert.java new file mode 100644 index 000000000..7e633809c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsFatalAlert.java @@ -0,0 +1,21 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public class TlsFatalAlert + extends IOException +{ + private static final long serialVersionUID = 3584313123679111168L; + + private short alertDescription; + + public TlsFatalAlert(short alertDescription) + { + this.alertDescription = alertDescription; + } + + public short getAlertDescription() + { + return alertDescription; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsHandshakeHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsHandshakeHash.java new file mode 100644 index 000000000..1fb28d3c8 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsHandshakeHash.java @@ -0,0 +1,21 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.Digest; + +interface TlsHandshakeHash + extends Digest +{ + void init(TlsContext context); + + TlsHandshakeHash notifyPRFDetermined(); + + void trackHashAlgorithm(short hashAlgorithm); + + void sealHashAlgorithms(); + + TlsHandshakeHash stopTracking(); + + Digest forkPRFHash(); + + byte[] getFinalHash(short hashAlgorithm); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsInputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsInputStream.java new file mode 100644 index 000000000..7f7de6977 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsInputStream.java @@ -0,0 +1,41 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An InputStream for an TLS 1.0 connection. + */ +class TlsInputStream + extends InputStream +{ + private byte[] buf = new byte[1]; + private TlsProtocol handler = null; + + TlsInputStream(TlsProtocol handler) + { + this.handler = handler; + } + + public int read(byte[] buf, int offset, int len) + throws IOException + { + return this.handler.readApplicationData(buf, offset, len); + } + + public int read() + throws IOException + { + if (this.read(buf) < 0) + { + return -1; + } + return buf[0] & 0xff; + } + + public void close() + throws IOException + { + handler.close(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsKeyExchange.java new file mode 100644 index 000000000..d0f2370a2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsKeyExchange.java @@ -0,0 +1,55 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A generic interface for key exchange implementations in TLS 1.0/1.1. + */ +public interface TlsKeyExchange +{ + + void init(TlsContext context); + + void skipServerCredentials() + throws IOException; + + void processServerCredentials(TlsCredentials serverCredentials) + throws IOException; + + void processServerCertificate(Certificate serverCertificate) + throws IOException; + + boolean requiresServerKeyExchange(); + + byte[] generateServerKeyExchange() + throws IOException; + + void skipServerKeyExchange() + throws IOException; + + void processServerKeyExchange(InputStream input) + throws IOException; + + void validateCertificateRequest(CertificateRequest certificateRequest) + throws IOException; + + void skipClientCredentials() + throws IOException; + + void processClientCredentials(TlsCredentials clientCredentials) + throws IOException; + + void processClientCertificate(Certificate clientCertificate) + throws IOException; + + void generateClientKeyExchange(OutputStream output) + throws IOException; + + void processClientKeyExchange(InputStream input) + throws IOException; + + byte[] generatePremasterSecret() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java new file mode 100644 index 000000000..33eb7f7b3 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java @@ -0,0 +1,181 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.digests.LongDigest; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.util.Arrays; + +/** + * A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest. + */ +public class TlsMac +{ + protected TlsContext context; + protected byte[] secret; + protected Mac mac; + protected int digestBlockSize; + protected int digestOverhead; + protected int macLength; + + /** + * Generate a new instance of an TlsMac. + * + * @param context the TLS client context + * @param digest The digest to use. + * @param key A byte-array where the key for this MAC is located. + * @param keyOff The number of bytes to skip, before the key starts in the buffer. + * @param len The length of the key. + */ + public TlsMac(TlsContext context, Digest digest, byte[] key, int keyOff, int keyLen) + { + this.context = context; + + KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen); + + this.secret = Arrays.clone(keyParameter.getKey()); + + // TODO This should check the actual algorithm, not rely on the engine type + if (digest instanceof LongDigest) + { + this.digestBlockSize = 128; + this.digestOverhead = 16; + } + else + { + this.digestBlockSize = 64; + this.digestOverhead = 8; + } + + if (TlsUtils.isSSL(context)) + { + this.mac = new SSL3Mac(digest); + + // TODO This should check the actual algorithm, not assume based on the digest size + if (digest.getDigestSize() == 20) + { + /* + * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not + * digest block-aligned. + */ + this.digestOverhead = 4; + } + } + else + { + this.mac = new HMac(digest); + + // NOTE: The input pad for HMAC is always a full digest block + } + + this.mac.init(keyParameter); + + this.macLength = mac.getMacSize(); + if (context.getSecurityParameters().truncatedHMac) + { + this.macLength = Math.min(this.macLength, 10); + } + } + + /** + * @return the MAC write secret + */ + public byte[] getMACSecret() + { + return this.secret; + } + + /** + * @return The output length of this MAC. + */ + public int getSize() + { + return macLength; + } + + /** + * Calculate the MAC for some given data. + * + * @param type The message type of the message. + * @param message A byte-buffer containing the message. + * @param offset The number of bytes to skip, before the message starts. + * @param length The length of the message. + * @return A new byte-buffer containing the MAC value. + */ + public byte[] calculateMac(long seqNo, short type, byte[] message, int offset, int length) + { + /* + * TODO[draft-josefsson-salsa20-tls-02] 3. Moreover, in order to accommodate MAC algorithms + * like UMAC that require a nonce as part of their operation, the document extends the MAC + * algorithm as specified in the TLS protocol. The extended MAC includes a nonce as a second + * parameter. MAC algorithms that do not require a nonce, such as HMAC, are assumed to + * ignore the nonce input value. The MAC in a GenericStreamCipher is then calculated as + * follows. + */ + + ProtocolVersion serverVersion = context.getServerVersion(); + boolean isSSL = serverVersion.isSSL(); + + byte[] macHeader = new byte[isSSL ? 11 : 13]; + TlsUtils.writeUint64(seqNo, macHeader, 0); + TlsUtils.writeUint8(type, macHeader, 8); + if (!isSSL) + { + TlsUtils.writeVersion(serverVersion, macHeader, 9); + } + TlsUtils.writeUint16(length, macHeader, macHeader.length - 2); + + mac.update(macHeader, 0, macHeader.length); + mac.update(message, offset, length); + + byte[] result = new byte[mac.getMacSize()]; + mac.doFinal(result, 0); + return truncate(result); + } + + public byte[] calculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length, + int fullLength, byte[] dummyData) + { + /* + * Actual MAC only calculated on 'length' bytes... + */ + byte[] result = calculateMac(seqNo, type, message, offset, length); + + /* + * ...but ensure a constant number of complete digest blocks are processed (as many as would + * be needed for 'fullLength' bytes of input). + */ + int headerLength = TlsUtils.isSSL(context) ? 11 : 13; + + // How many extra full blocks do we need to calculate? + int extra = getDigestBlockCount(headerLength + fullLength) - getDigestBlockCount(headerLength + length); + + while (--extra >= 0) + { + mac.update(dummyData, 0, digestBlockSize); + } + + // One more byte in case the implementation is "lazy" about processing blocks + mac.update(dummyData[0]); + mac.reset(); + + return result; + } + + protected int getDigestBlockCount(int inputLength) + { + // NOTE: This calculation assumes a minimum of 1 pad byte + return (inputLength + digestOverhead) / digestBlockSize; + } + + protected byte[] truncate(byte[] bs) + { + if (bs.length <= macLength) + { + return bs; + } + + return Arrays.copyOf(bs, macLength); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsNullCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsNullCipher.java new file mode 100644 index 000000000..6972cea68 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsNullCipher.java @@ -0,0 +1,123 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.util.Arrays; + +/** + * A NULL CipherSuite with optional MAC + */ +public class TlsNullCipher + implements TlsCipher +{ + protected TlsContext context; + + protected TlsMac writeMac; + protected TlsMac readMac; + + public TlsNullCipher(TlsContext context) + { + this.context = context; + this.writeMac = null; + this.readMac = null; + } + + public TlsNullCipher(TlsContext context, Digest clientWriteDigest, Digest serverWriteDigest) + throws IOException + { + if ((clientWriteDigest == null) != (serverWriteDigest == null)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + this.context = context; + + TlsMac clientWriteMac = null, serverWriteMac = null; + + if (clientWriteDigest != null) + { + int key_block_size = clientWriteDigest.getDigestSize() + + serverWriteDigest.getDigestSize(); + byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size); + + int offset = 0; + + clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.getDigestSize()); + offset += clientWriteDigest.getDigestSize(); + + serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.getDigestSize()); + offset += serverWriteDigest.getDigestSize(); + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + if (context.isServer()) + { + writeMac = serverWriteMac; + readMac = clientWriteMac; + } + else + { + writeMac = clientWriteMac; + readMac = serverWriteMac; + } + } + + public int getPlaintextLimit(int ciphertextLimit) + { + int result = ciphertextLimit; + if (writeMac != null) + { + result -= writeMac.getSize(); + } + return result; + } + + public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) + throws IOException + { + if (writeMac == null) + { + return Arrays.copyOfRange(plaintext, offset, offset + len); + } + + byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len); + byte[] ciphertext = new byte[len + mac.length]; + System.arraycopy(plaintext, offset, ciphertext, 0, len); + System.arraycopy(mac, 0, ciphertext, len, mac.length); + return ciphertext; + } + + public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len) + throws IOException + { + if (readMac == null) + { + return Arrays.copyOfRange(ciphertext, offset, offset + len); + } + + int macSize = readMac.getSize(); + if (len < macSize) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + int macInputLen = len - macSize; + + byte[] receivedMac = Arrays.copyOfRange(ciphertext, offset + macInputLen, offset + len); + byte[] computedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, macInputLen); + + if (!Arrays.constantTimeAreEqual(receivedMac, computedMac)) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + + return Arrays.copyOfRange(ciphertext, offset, offset + macInputLen); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsNullCompression.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsNullCompression.java new file mode 100644 index 000000000..724d93b60 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsNullCompression.java @@ -0,0 +1,17 @@ +package org.spongycastle.crypto.tls; + +import java.io.OutputStream; + +public class TlsNullCompression + implements TlsCompression +{ + public OutputStream compress(OutputStream output) + { + return output; + } + + public OutputStream decompress(OutputStream output) + { + return output; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsOutputStream.java new file mode 100644 index 000000000..d4b6de66b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsOutputStream.java @@ -0,0 +1,44 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An OutputStream for an TLS connection. + */ +class TlsOutputStream + extends OutputStream +{ + private byte[] buf = new byte[1]; + private TlsProtocol handler; + + TlsOutputStream(TlsProtocol handler) + { + this.handler = handler; + } + + public void write(byte buf[], int offset, int len) + throws IOException + { + this.handler.writeData(buf, offset, len); + } + + public void write(int arg0) + throws IOException + { + buf[0] = (byte)arg0; + this.write(buf, 0, 1); + } + + public void close() + throws IOException + { + handler.close(); + } + + public void flush() + throws IOException + { + handler.flush(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPSKIdentity.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPSKIdentity.java new file mode 100644 index 000000000..a271ddced --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPSKIdentity.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +public interface TlsPSKIdentity +{ + void skipIdentityHint(); + + void notifyIdentityHint(byte[] psk_identity_hint); + + byte[] getPSKIdentity(); + + byte[] getPSK(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPSKKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPSKKeyExchange.java new file mode 100644 index 000000000..c3431bb44 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPSKKeyExchange.java @@ -0,0 +1,285 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.util.PublicKeyFactory; + +/** + * TLS 1.0 PSK key exchange (RFC 4279). + */ +public class TlsPSKKeyExchange + extends AbstractTlsKeyExchange +{ + protected TlsPSKIdentity pskIdentity; + protected DHParameters dhParameters; + protected int[] namedCurves; + protected short[] clientECPointFormats, serverECPointFormats; + + protected byte[] psk_identity_hint = null; + + protected DHPrivateKeyParameters dhAgreePrivateKey = null; + protected DHPublicKeyParameters dhAgreePublicKey = null; + + protected AsymmetricKeyParameter serverPublicKey = null; + protected RSAKeyParameters rsaServerPublicKey = null; + protected TlsEncryptionCredentials serverCredentials = null; + protected byte[] premasterSecret; + + public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity, + DHParameters dhParameters, int[] namedCurves, short[] clientECPointFormats, short[] serverECPointFormats) + { + super(keyExchange, supportedSignatureAlgorithms); + + switch (keyExchange) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.RSA_PSK: + break; + default: + throw new IllegalArgumentException("unsupported key exchange algorithm"); + } + + this.pskIdentity = pskIdentity; + this.dhParameters = dhParameters; + this.namedCurves = namedCurves; + this.clientECPointFormats = clientECPointFormats; + this.serverECPointFormats = serverECPointFormats; + } + + public void skipServerCredentials() + throws IOException + { + if (keyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public void processServerCredentials(TlsCredentials serverCredentials) + throws IOException + { + if (!(serverCredentials instanceof TlsEncryptionCredentials)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + processServerCertificate(serverCredentials.getCertificate()); + + this.serverCredentials = (TlsEncryptionCredentials)serverCredentials; + } + + public byte[] generateServerKeyExchange() throws IOException + { + // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys + this.psk_identity_hint = null; + + if (this.psk_identity_hint == null && !requiresServerKeyExchange()) + { + return null; + } + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + if (this.psk_identity_hint == null) + { + TlsUtils.writeOpaque16(TlsUtils.EMPTY_BYTES, buf); + } + else + { + TlsUtils.writeOpaque16(this.psk_identity_hint, buf); + } + + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + if (this.dhParameters == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(), + this.dhParameters, buf); + } + else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + } + + return buf.toByteArray(); + } + + public void processServerCertificate(Certificate serverCertificate) + throws IOException + { + if (keyExchange != KeyExchangeAlgorithm.RSA_PSK) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + if (serverCertificate.isEmpty()) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + org.spongycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + try + { + this.serverPublicKey = PublicKeyFactory.createKey(keyInfo); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + // Sanity check the PublicKeyFactory + if (this.serverPublicKey.isPrivate()) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + this.rsaServerPublicKey = validateRSAPublicKey((RSAKeyParameters)this.serverPublicKey); + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyEncipherment); + + super.processServerCertificate(serverCertificate); + } + + public boolean requiresServerKeyExchange() + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + return true; + default: + return false; + } + } + + public void processServerKeyExchange(InputStream input) + throws IOException + { + this.psk_identity_hint = TlsUtils.readOpaque16(input); + + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + ServerDHParams serverDHParams = ServerDHParams.parse(input); + + this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(serverDHParams.getPublicKey()); + } + else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + } + } + + public void validateCertificateRequest(CertificateRequest certificateRequest) + throws IOException + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public void processClientCredentials(TlsCredentials clientCredentials) + throws IOException + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public void generateClientKeyExchange(OutputStream output) + throws IOException + { + if (psk_identity_hint == null) + { + pskIdentity.skipIdentityHint(); + } + else + { + pskIdentity.notifyIdentityHint(psk_identity_hint); + } + + byte[] psk_identity = pskIdentity.getPSKIdentity(); + + TlsUtils.writeOpaque16(psk_identity, output); + + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(), + dhAgreePublicKey.getParameters(), output); + } + else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + throw new TlsFatalAlert(AlertDescription.internal_error); + } + else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, + output); + } + } + + public byte[] generatePremasterSecret() + throws IOException + { + byte[] psk = pskIdentity.getPSK(); + byte[] other_secret = generateOtherSecret(psk.length); + + ByteArrayOutputStream buf = new ByteArrayOutputStream(4 + other_secret.length + psk.length); + TlsUtils.writeOpaque16(other_secret, buf); + TlsUtils.writeOpaque16(psk, buf); + return buf.toByteArray(); + } + + protected byte[] generateOtherSecret(int pskLength) throws IOException + { + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + if (dhAgreePrivateKey != null) + { + return TlsDHUtils.calculateDHBasicAgreement(dhAgreePublicKey, dhAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + return this.premasterSecret; + } + + return new byte[pskLength]; + } + + protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key) + throws IOException + { + // TODO What is the minimum bit length required? + // key.getModulus().bitLength(); + + if (!key.getExponent().isProbablePrime(2)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return key; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPeer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPeer.java new file mode 100644 index 000000000..8dbc26c8f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsPeer.java @@ -0,0 +1,35 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface TlsPeer +{ + void notifySecureRenegotiation(boolean secureNegotiation) throws IOException; + + TlsCompression getCompression() throws IOException; + + TlsCipher getCipher() throws IOException; + + /** + * This method will be called when an alert is raised by the protocol. + * + * @param alertLevel {@link AlertLevel} + * @param alertDescription {@link AlertDescription} + * @param message A human-readable message explaining what caused this alert. May be null. + * @param cause The exception that caused this alert to be raised. May be null. + */ + void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause); + + /** + * This method will be called when an alert is received from the remote peer. + * + * @param alertLevel {@link AlertLevel} + * @param alertDescription {@link AlertDescription} + */ + void notifyAlertReceived(short alertLevel, short alertDescription); + + /** + * Notifies the peer that the handshake has been successfully completed. + */ + void notifyHandshakeComplete() throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsProtocol.java new file mode 100644 index 000000000..1120279b0 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsProtocol.java @@ -0,0 +1,1117 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Integers; + +/** + * An implementation of all high level protocols in TLS 1.0/1.1. + */ +public abstract class TlsProtocol +{ + protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info); + protected static final Integer EXT_SessionTicket = Integers.valueOf(ExtensionType.session_ticket); + + private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack"; + + /* + * Our Connection states + */ + protected static final short CS_START = 0; + protected static final short CS_CLIENT_HELLO = 1; + protected static final short CS_SERVER_HELLO = 2; + protected static final short CS_SERVER_SUPPLEMENTAL_DATA = 3; + protected static final short CS_SERVER_CERTIFICATE = 4; + protected static final short CS_CERTIFICATE_STATUS = 5; + protected static final short CS_SERVER_KEY_EXCHANGE = 6; + protected static final short CS_CERTIFICATE_REQUEST = 7; + protected static final short CS_SERVER_HELLO_DONE = 8; + protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 9; + protected static final short CS_CLIENT_CERTIFICATE = 10; + protected static final short CS_CLIENT_KEY_EXCHANGE = 11; + protected static final short CS_CERTIFICATE_VERIFY = 12; + protected static final short CS_CLIENT_FINISHED = 13; + protected static final short CS_SERVER_SESSION_TICKET = 14; + protected static final short CS_SERVER_FINISHED = 15; + protected static final short CS_END = 16; + + /* + * Queues for data from some protocols. + */ + private ByteQueue applicationDataQueue = new ByteQueue(); + private ByteQueue alertQueue = new ByteQueue(2); + private ByteQueue handshakeQueue = new ByteQueue(); + + /* + * The Record Stream we use + */ + protected RecordStream recordStream; + protected SecureRandom secureRandom; + + private TlsInputStream tlsInputStream = null; + private TlsOutputStream tlsOutputStream = null; + + private volatile boolean closed = false; + private volatile boolean failedWithError = false; + private volatile boolean appDataReady = false; + private volatile boolean splitApplicationDataRecords = true; + private byte[] expected_verify_data = null; + + protected TlsSession tlsSession = null; + protected SessionParameters sessionParameters = null; + protected SecurityParameters securityParameters = null; + protected Certificate peerCertificate = null; + + protected int[] offeredCipherSuites = null; + protected short[] offeredCompressionMethods = null; + protected Hashtable clientExtensions = null; + protected Hashtable serverExtensions = null; + + protected short connection_state = CS_START; + protected boolean resumedSession = false; + protected boolean receivedChangeCipherSpec = false; + protected boolean secure_renegotiation = false; + protected boolean allowCertificateStatus = false; + protected boolean expectSessionTicket = false; + + public TlsProtocol(InputStream input, OutputStream output, SecureRandom secureRandom) + { + this.recordStream = new RecordStream(this, input, output); + this.secureRandom = secureRandom; + } + + protected abstract AbstractTlsContext getContext(); + + protected abstract TlsPeer getPeer(); + + protected void handleChangeCipherSpecMessage() throws IOException + { + } + + protected abstract void handleHandshakeMessage(short type, byte[] buf) + throws IOException; + + protected void handleWarningMessage(short description) + throws IOException + { + } + + protected void cleanupHandshake() + { + if (this.expected_verify_data != null) + { + Arrays.fill(this.expected_verify_data, (byte)0); + this.expected_verify_data = null; + } + + this.securityParameters.clear(); + this.peerCertificate = null; + + this.offeredCipherSuites = null; + this.offeredCompressionMethods = null; + this.clientExtensions = null; + this.serverExtensions = null; + + this.resumedSession = false; + this.receivedChangeCipherSpec = false; + this.secure_renegotiation = false; + this.allowCertificateStatus = false; + this.expectSessionTicket = false; + } + + protected void completeHandshake() + throws IOException + { + try + { + /* + * We will now read data, until we have completed the handshake. + */ + while (this.connection_state != CS_END) + { + if (this.closed) + { + // TODO What kind of exception/alert? + } + + safeReadRecord(); + } + + this.recordStream.finaliseHandshake(); + + this.splitApplicationDataRecords = !TlsUtils.isTLSv11(getContext()); + + /* + * If this was an initial handshake, we are now ready to send and receive application data. + */ + if (!appDataReady) + { + this.appDataReady = true; + + this.tlsInputStream = new TlsInputStream(this); + this.tlsOutputStream = new TlsOutputStream(this); + } + + if (this.tlsSession != null) + { + if (this.sessionParameters == null) + { + this.sessionParameters = new SessionParameters.Builder() + .setCipherSuite(this.securityParameters.cipherSuite) + .setCompressionAlgorithm(this.securityParameters.compressionAlgorithm) + .setMasterSecret(this.securityParameters.masterSecret) + .setPeerCertificate(this.peerCertificate) + // TODO Consider filtering extensions that aren't relevant to resumed sessions + .setServerExtensions(this.serverExtensions) + .build(); + + this.tlsSession = new TlsSessionImpl(this.tlsSession.getSessionID(), this.sessionParameters); + } + + getContext().setResumableSession(this.tlsSession); + } + + getPeer().notifyHandshakeComplete(); + } + finally + { + cleanupHandshake(); + } + } + + protected void processRecord(short protocol, byte[] buf, int offset, int len) + throws IOException + { + /* + * Have a look at the protocol type, and add it to the correct queue. + */ + switch (protocol) + { + case ContentType.alert: + { + alertQueue.addData(buf, offset, len); + processAlert(); + break; + } + case ContentType.application_data: + { + if (!appDataReady) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + applicationDataQueue.addData(buf, offset, len); + processApplicationData(); + break; + } + case ContentType.change_cipher_spec: + { + processChangeCipherSpec(buf, offset, len); + break; + } + case ContentType.handshake: + { + handshakeQueue.addData(buf, offset, len); + processHandshake(); + break; + } + case ContentType.heartbeat: + { + // TODO[RFC 6520] + } + default: + /* + * Uh, we don't know this protocol. + * + * RFC2246 defines on page 13, that we should ignore this. + */ + } + } + + private void processHandshake() + throws IOException + { + boolean read; + do + { + read = false; + /* + * We need the first 4 bytes, they contain type and length of the message. + */ + if (handshakeQueue.size() >= 4) + { + byte[] beginning = new byte[4]; + handshakeQueue.read(beginning, 0, 4, 0); + ByteArrayInputStream bis = new ByteArrayInputStream(beginning); + short type = TlsUtils.readUint8(bis); + int len = TlsUtils.readUint24(bis); + + /* + * Check if we have enough bytes in the buffer to read the full message. + */ + if (handshakeQueue.size() >= (len + 4)) + { + /* + * Read the message. + */ + byte[] buf = handshakeQueue.removeData(len, 4); + + /* + * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages + * starting at client hello up to, but not including, this finished message. + * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes. + */ + switch (type) + { + case HandshakeType.hello_request: + break; + case HandshakeType.finished: + { + if (this.expected_verify_data == null) + { + this.expected_verify_data = createVerifyData(!getContext().isServer()); + } + + // NB: Fall through to next case label + } + default: + recordStream.updateHandshakeData(beginning, 0, 4); + recordStream.updateHandshakeData(buf, 0, len); + break; + } + + /* + * Now, parse the message. + */ + handleHandshakeMessage(type, buf); + read = true; + } + } + } + while (read); + } + + private void processApplicationData() + { + /* + * There is nothing we need to do here. + * + * This function could be used for callbacks when application data arrives in the future. + */ + } + + private void processAlert() + throws IOException + { + while (alertQueue.size() >= 2) + { + /* + * An alert is always 2 bytes. Read the alert. + */ + byte[] tmp = alertQueue.removeData(2, 0); + short level = tmp[0]; + short description = tmp[1]; + + getPeer().notifyAlertReceived(level, description); + + if (level == AlertLevel.fatal) + { + /* + * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated + * without proper close_notify messages with level equal to warning. + */ + invalidateSession(); + + this.failedWithError = true; + this.closed = true; + + recordStream.safeClose(); + + throw new IOException(TLS_ERROR_MESSAGE); + } + else + { + + /* + * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own + * and close down the connection immediately, discarding any pending writes. + */ + // TODO Can close_notify be a fatal alert? + if (description == AlertDescription.close_notify) + { + handleClose(false); + } + + /* + * If it is just a warning, we continue. + */ + handleWarningMessage(description); + } + } + } + + /** + * This method is called, when a change cipher spec message is received. + * + * @throws IOException If the message has an invalid content or the handshake is not in the correct + * state. + */ + private void processChangeCipherSpec(byte[] buf, int off, int len) + throws IOException + { + for (int i = 0; i < len; ++i) + { + short message = TlsUtils.readUint8(buf, off + i); + + if (message != ChangeCipherSpec.change_cipher_spec) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + if (this.receivedChangeCipherSpec) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.receivedChangeCipherSpec = true; + + recordStream.receivedReadCipherSpec(); + + handleChangeCipherSpecMessage(); + } + } + + /** + * Read data from the network. The method will return immediately, if there is still some data + * left in the buffer, or block until some application data has been read from the network. + * + * @param buf The buffer where the data will be copied to. + * @param offset The position where the data will be placed in the buffer. + * @param len The maximum number of bytes to read. + * @return The number of bytes read. + * @throws IOException If something goes wrong during reading data. + */ + protected int readApplicationData(byte[] buf, int offset, int len) + throws IOException + { + if (len < 1) + { + return 0; + } + + while (applicationDataQueue.size() == 0) + { + /* + * We need to read some data. + */ + if (this.closed) + { + if (this.failedWithError) + { + /* + * Something went terribly wrong, we should throw an IOException + */ + throw new IOException(TLS_ERROR_MESSAGE); + } + + /* + * Connection has been closed, there is no more data to read. + */ + return -1; + } + + safeReadRecord(); + } + + len = Math.min(len, applicationDataQueue.size()); + applicationDataQueue.removeData(buf, offset, len, 0); + return len; + } + + protected void safeReadRecord() + throws IOException + { + try + { + if (!recordStream.readRecord()) + { + // TODO It would be nicer to allow graceful connection close if between records +// this.failWithError(AlertLevel.warning, AlertDescription.close_notify); + throw new EOFException(); + } + } + catch (TlsFatalAlert e) + { + if (!this.closed) + { + this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e); + } + throw e; + } + catch (IOException e) + { + if (!this.closed) + { + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); + } + throw e; + } + catch (RuntimeException e) + { + if (!this.closed) + { + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); + } + throw e; + } + } + + protected void safeWriteRecord(short type, byte[] buf, int offset, int len) + throws IOException + { + try + { + recordStream.writeRecord(type, buf, offset, len); + } + catch (TlsFatalAlert e) + { + if (!this.closed) + { + this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to write record", e); + } + throw e; + } + catch (IOException e) + { + if (!closed) + { + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e); + } + throw e; + } + catch (RuntimeException e) + { + if (!closed) + { + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e); + } + throw e; + } + } + + /** + * Send some application data to the remote system. + * + * The method will handle fragmentation internally. + * + * @param buf The buffer with the data. + * @param offset The position in the buffer where the data is placed. + * @param len The length of the data. + * @throws IOException If something goes wrong during sending. + */ + protected void writeData(byte[] buf, int offset, int len) + throws IOException + { + if (this.closed) + { + if (this.failedWithError) + { + throw new IOException(TLS_ERROR_MESSAGE); + } + + throw new IOException("Sorry, connection has been closed, you cannot write more data"); + } + + while (len > 0) + { + /* + * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are + * potentially useful as a traffic analysis countermeasure. + * + * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting. + */ + + if (this.splitApplicationDataRecords) + { + /* + * Protect against known IV attack! + * + * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. + */ + safeWriteRecord(ContentType.application_data, buf, offset, 1); + ++offset; + --len; + } + + if (len > 0) + { + // Fragment data according to the current fragment limit. + int toWrite = Math.min(len, recordStream.getPlaintextLimit()); + safeWriteRecord(ContentType.application_data, buf, offset, toWrite); + offset += toWrite; + len -= toWrite; + } + } + } + + protected void writeHandshakeMessage(byte[] buf, int off, int len) throws IOException + { + while (len > 0) + { + // Fragment data according to the current fragment limit. + int toWrite = Math.min(len, recordStream.getPlaintextLimit()); + safeWriteRecord(ContentType.handshake, buf, off, toWrite); + off += toWrite; + len -= toWrite; + } + } + + /** + * @return An OutputStream which can be used to send data. + */ + public OutputStream getOutputStream() + { + return this.tlsOutputStream; + } + + /** + * @return An InputStream which can be used to read data. + */ + public InputStream getInputStream() + { + return this.tlsInputStream; + } + + /** + * Terminate this connection with an alert. Can be used for normal closure too. + * + * @param alertLevel + * See {@link AlertLevel} for values. + * @param alertDescription + * See {@link AlertDescription} for values. + * @throws IOException + * If alert was fatal. + */ + protected void failWithError(short alertLevel, short alertDescription, String message, Exception cause) + throws IOException + { + /* + * Check if the connection is still open. + */ + if (!closed) + { + /* + * Prepare the message + */ + this.closed = true; + + if (alertLevel == AlertLevel.fatal) + { + /* + * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated + * without proper close_notify messages with level equal to warning. + */ + // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete. + invalidateSession(); + + this.failedWithError = true; + } + raiseAlert(alertLevel, alertDescription, message, cause); + recordStream.safeClose(); + if (alertLevel != AlertLevel.fatal) + { + return; + } + } + + throw new IOException(TLS_ERROR_MESSAGE); + } + + protected void invalidateSession() + { + if (this.sessionParameters != null) + { + this.sessionParameters.clear(); + this.sessionParameters = null; + } + + if (this.tlsSession != null) + { + this.tlsSession.invalidate(); + this.tlsSession = null; + } + } + + protected void processFinishedMessage(ByteArrayInputStream buf) + throws IOException + { + byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf); + + assertEmpty(buf); + + /* + * Compare both checksums. + */ + if (!Arrays.constantTimeAreEqual(expected_verify_data, verify_data)) + { + /* + * Wrong checksum in the finished message. + */ + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + + protected void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause) + throws IOException + { + getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause); + + byte[] error = new byte[2]; + error[0] = (byte)alertLevel; + error[1] = (byte)alertDescription; + + safeWriteRecord(ContentType.alert, error, 0, 2); + } + + protected void raiseWarning(short alertDescription, String message) + throws IOException + { + raiseAlert(AlertLevel.warning, alertDescription, message, null); + } + + protected void sendCertificateMessage(Certificate certificate) + throws IOException + { + if (certificate == null) + { + certificate = Certificate.EMPTY_CHAIN; + } + + if (certificate.getLength() == 0) + { + TlsContext context = getContext(); + if (!context.isServer()) + { + ProtocolVersion serverVersion = getContext().getServerVersion(); + if (serverVersion.isSSL()) + { + String message = serverVersion.toString() + " client didn't provide credentials"; + raiseWarning(AlertDescription.no_certificate, message); + return; + } + } + } + + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate); + + certificate.encode(message); + + message.writeToRecordStream(); + } + + protected void sendChangeCipherSpecMessage() + throws IOException + { + byte[] message = new byte[]{ 1 }; + safeWriteRecord(ContentType.change_cipher_spec, message, 0, message.length); + recordStream.sentWriteCipherSpec(); + } + + protected void sendFinishedMessage() + throws IOException + { + byte[] verify_data = createVerifyData(getContext().isServer()); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.length); + + message.write(verify_data); + + message.writeToRecordStream(); + } + + protected void sendSupplementalDataMessage(Vector supplementalData) + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data); + + writeSupplementalData(message, supplementalData); + + message.writeToRecordStream(); + } + + protected byte[] createVerifyData(boolean isServer) + { + TlsContext context = getContext(); + + if (isServer) + { + return TlsUtils.calculateVerifyData(context, ExporterLabel.server_finished, + getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_SERVER)); + } + + return TlsUtils.calculateVerifyData(context, ExporterLabel.client_finished, + getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_CLIENT)); + } + + /** + * Closes this connection. + * + * @throws IOException If something goes wrong during closing. + */ + public void close() + throws IOException + { + handleClose(true); + } + + protected void handleClose(boolean user_canceled) + throws IOException + { + if (!closed) + { + if (user_canceled && !appDataReady) + { + raiseWarning(AlertDescription.user_canceled, "User canceled handshake"); + } + this.failWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null); + } + } + + protected void flush() + throws IOException + { + recordStream.flush(); + } + + protected short processMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions, short alertDescription) + throws IOException + { + short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions); + if (maxFragmentLength >= 0 && !this.resumedSession) + { + if (maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions)) + { + throw new TlsFatalAlert(alertDescription); + } + } + return maxFragmentLength; + } + + /** + * Make sure the InputStream 'buf' now empty. Fail otherwise. + * + * @param buf The InputStream to check. + * @throws IOException If 'buf' is not empty. + */ + protected static void assertEmpty(ByteArrayInputStream buf) + throws IOException + { + if (buf.available() > 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + } + + protected static byte[] createRandomBlock(SecureRandom random) + { + random.setSeed(System.currentTimeMillis()); + + byte[] result = new byte[32]; + random.nextBytes(result); + /* + * The consensus seems to be that using the time here is neither all that useful, nor + * secure. Perhaps there could be an option to (re-)enable it. Instead, we seed the random + * source with the current time to retain it's main benefit. + */ +// TlsUtils.writeGMTUnixTime(result, 0); + return result; + } + + protected static byte[] createRenegotiationInfo(byte[] renegotiated_connection) + throws IOException + { + return TlsUtils.encodeOpaque8(renegotiated_connection); + } + + protected static void establishMasterSecret(TlsContext context, TlsKeyExchange keyExchange) + throws IOException + { + byte[] pre_master_secret = keyExchange.generatePremasterSecret(); + + try + { + context.getSecurityParameters().masterSecret = TlsUtils.calculateMasterSecret(context, pre_master_secret); + } + finally + { + // TODO Is there a way to ensure the data is really overwritten? + /* + * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the + * master_secret has been computed. + */ + if (pre_master_secret != null) + { + Arrays.fill(pre_master_secret, (byte)0); + } + } + } + + /** + * 'sender' only relevant to SSLv3 + */ + protected static byte[] getCurrentPRFHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender) + { + Digest d = handshakeHash.forkPRFHash(); + + if (sslSender != null && TlsUtils.isSSL(context)) + { + d.update(sslSender, 0, sslSender.length); + } + + byte[] bs = new byte[d.getDigestSize()]; + d.doFinal(bs, 0); + return bs; + } + + protected static Hashtable readExtensions(ByteArrayInputStream input) + throws IOException + { + if (input.available() < 1) + { + return null; + } + + byte[] extBytes = TlsUtils.readOpaque16(input); + + assertEmpty(input); + + ByteArrayInputStream buf = new ByteArrayInputStream(extBytes); + + // Integer -> byte[] + Hashtable extensions = new Hashtable(); + + while (buf.available() > 0) + { + Integer extension_type = Integers.valueOf(TlsUtils.readUint16(buf)); + byte[] extension_data = TlsUtils.readOpaque16(buf); + + /* + * RFC 3546 2.3 There MUST NOT be more than one extension of the same type. + */ + if (null != extensions.put(extension_type, extension_data)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + return extensions; + } + + protected static Vector readSupplementalDataMessage(ByteArrayInputStream input) + throws IOException + { + byte[] supp_data = TlsUtils.readOpaque24(input); + + assertEmpty(input); + + ByteArrayInputStream buf = new ByteArrayInputStream(supp_data); + + Vector supplementalData = new Vector(); + + while (buf.available() > 0) + { + int supp_data_type = TlsUtils.readUint16(buf); + byte[] data = TlsUtils.readOpaque16(buf); + + supplementalData.addElement(new SupplementalDataEntry(supp_data_type, data)); + } + + return supplementalData; + } + + protected static void writeExtensions(OutputStream output, Hashtable extensions) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + Enumeration keys = extensions.keys(); + while (keys.hasMoreElements()) + { + Integer key = (Integer)keys.nextElement(); + int extension_type = key.intValue(); + byte[] extension_data = (byte[])extensions.get(key); + + TlsUtils.checkUint16(extension_type); + TlsUtils.writeUint16(extension_type, buf); + TlsUtils.writeOpaque16(extension_data, buf); + } + + byte[] extBytes = buf.toByteArray(); + + TlsUtils.writeOpaque16(extBytes, output); + } + + protected static void writeSupplementalData(OutputStream output, Vector supplementalData) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + for (int i = 0; i < supplementalData.size(); ++i) + { + SupplementalDataEntry entry = (SupplementalDataEntry)supplementalData.elementAt(i); + + int supp_data_type = entry.getDataType(); + TlsUtils.checkUint16(supp_data_type); + TlsUtils.writeUint16(supp_data_type, buf); + TlsUtils.writeOpaque16(entry.getData(), buf); + } + + byte[] supp_data = buf.toByteArray(); + + TlsUtils.writeOpaque24(supp_data, output); + } + + protected static int getPRFAlgorithm(TlsContext context, int ciphersuite) throws IOException + { + boolean isTLSv12 = TlsUtils.isTLSv12(context); + + switch (ciphersuite) + { + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + { + if (isTLSv12) + { + return PRFAlgorithm.tls_prf_sha256; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + { + if (isTLSv12) + { + return PRFAlgorithm.tls_prf_sha384; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + { + if (isTLSv12) + { + return PRFAlgorithm.tls_prf_sha384; + } + return PRFAlgorithm.tls_prf_legacy; + } + + default: + { + if (isTLSv12) + { + return PRFAlgorithm.tls_prf_sha256; + } + return PRFAlgorithm.tls_prf_legacy; + } + } + } + + class HandshakeMessage extends ByteArrayOutputStream + { + HandshakeMessage(short handshakeType) throws IOException + { + this(handshakeType, 60); + } + + HandshakeMessage(short handshakeType, int length) throws IOException + { + super(length + 4); + TlsUtils.writeUint8(handshakeType, this); + // Reserve space for length + count += 3; + } + + void writeToRecordStream() throws IOException + { + // Patch actual length back in + int length = count - 4; + TlsUtils.checkUint24(length); + TlsUtils.writeUint24(length, buf, 1); + writeHandshakeMessage(buf, 0, count); + buf = null; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsProtocolHandler.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsProtocolHandler.java new file mode 100644 index 000000000..bd12e4dff --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsProtocolHandler.java @@ -0,0 +1,23 @@ +package org.spongycastle.crypto.tls; + +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; + +/** + * @deprecated use TlsClientProtocol instead + */ +public class TlsProtocolHandler + extends TlsClientProtocol +{ + + public TlsProtocolHandler(InputStream is, OutputStream os) + { + super(is, os); + } + + public TlsProtocolHandler(InputStream is, OutputStream os, SecureRandom sr) + { + super(is, os, sr); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSAKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSAKeyExchange.java new file mode 100644 index 000000000..b9ba5443c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSAKeyExchange.java @@ -0,0 +1,191 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.util.io.Streams; + +/** + * TLS 1.0/1.1 and SSLv3 RSA key exchange. + */ +public class TlsRSAKeyExchange + extends AbstractTlsKeyExchange +{ + protected AsymmetricKeyParameter serverPublicKey = null; + + protected RSAKeyParameters rsaServerPublicKey = null; + + protected TlsEncryptionCredentials serverCredentials = null; + + protected byte[] premasterSecret; + + public TlsRSAKeyExchange(Vector supportedSignatureAlgorithms) + { + super(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms); + } + + public void skipServerCredentials() + throws IOException + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public void processServerCredentials(TlsCredentials serverCredentials) + throws IOException + { + if (!(serverCredentials instanceof TlsEncryptionCredentials)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + processServerCertificate(serverCredentials.getCertificate()); + + this.serverCredentials = (TlsEncryptionCredentials)serverCredentials; + } + + public void processServerCertificate(Certificate serverCertificate) + throws IOException + { + if (serverCertificate.isEmpty()) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + org.spongycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + try + { + this.serverPublicKey = PublicKeyFactory.createKey(keyInfo); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + // Sanity check the PublicKeyFactory + if (this.serverPublicKey.isPrivate()) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + this.rsaServerPublicKey = validateRSAPublicKey((RSAKeyParameters)this.serverPublicKey); + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyEncipherment); + + super.processServerCertificate(serverCertificate); + } + + public void validateCertificateRequest(CertificateRequest certificateRequest) + throws IOException + { + short[] types = certificateRequest.getCertificateTypes(); + for (int i = 0; i < types.length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public void processClientCredentials(TlsCredentials clientCredentials) + throws IOException + { + if (!(clientCredentials instanceof TlsSignerCredentials)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public void generateClientKeyExchange(OutputStream output) + throws IOException + { + this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, rsaServerPublicKey, output); + } + + public void processClientKeyExchange(InputStream input) + throws IOException + { + byte[] encryptedPreMasterSecret; + if (TlsUtils.isSSL(context)) + { + // TODO Do any SSLv3 clients actually include the length? + encryptedPreMasterSecret = Streams.readAll(input); + } + else + { + encryptedPreMasterSecret = TlsUtils.readOpaque16(input); + } + + this.premasterSecret = TlsRSAUtils.safeDecryptPreMasterSecret(context, serverCredentials, encryptedPreMasterSecret); + } + + public byte[] generatePremasterSecret() + throws IOException + { + if (this.premasterSecret == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + byte[] tmp = this.premasterSecret; + this.premasterSecret = null; + return tmp; + } + + // Would be needed to process RSA_EXPORT server key exchange + // protected void processRSAServerKeyExchange(InputStream is, Signer signer) throws IOException + // { + // InputStream sigIn = is; + // if (signer != null) + // { + // sigIn = new SignerInputStream(is, signer); + // } + // + // byte[] modulusBytes = TlsUtils.readOpaque16(sigIn); + // byte[] exponentBytes = TlsUtils.readOpaque16(sigIn); + // + // if (signer != null) + // { + // byte[] sigByte = TlsUtils.readOpaque16(is); + // + // if (!signer.verifySignature(sigByte)) + // { + // handler.failWithError(AlertLevel.fatal, AlertDescription.bad_certificate); + // } + // } + // + // BigInteger modulus = new BigInteger(1, modulusBytes); + // BigInteger exponent = new BigInteger(1, exponentBytes); + // + // this.rsaServerPublicKey = validateRSAPublicKey(new RSAKeyParameters(false, modulus, + // exponent)); + // } + + protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key) + throws IOException + { + // TODO What is the minimum bit length required? + // key.getModulus().bitLength(); + + if (!key.getExponent().isProbablePrime(2)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return key; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSASigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSASigner.java new file mode 100644 index 000000000..9511b0784 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSASigner.java @@ -0,0 +1,112 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.digests.NullDigest; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.signers.GenericSigner; +import org.spongycastle.crypto.signers.RSADigestSigner; + +public class TlsRSASigner + extends AbstractTlsSigner +{ + public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) + throws CryptoException + { + Signer signer = makeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + signer.update(hash, 0, hash.length); + return signer.generateSignature(); + } + + public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) + throws CryptoException + { + Signer signer = makeSigner(algorithm, true, false, publicKey); + signer.update(hash, 0, hash.length); + return signer.verifySignature(sigBytes); + } + + public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) + { + return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + } + + public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) + { + return makeSigner(algorithm, false, false, publicKey); + } + + public boolean isValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey instanceof RSAKeyParameters && !publicKey.isPrivate(); + } + + protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning, + CipherParameters cp) + { + if ((algorithm != null) != TlsUtils.isTLSv12(context)) + { + throw new IllegalStateException(); + } + + if (algorithm != null && algorithm.getSignature() != SignatureAlgorithm.rsa) + { + throw new IllegalStateException(); + } + + Digest d; + if (raw) + { + d = new NullDigest(); + } + else if (algorithm == null) + { + d = new CombinedHash(); + } + else + { + d = TlsUtils.createHash(algorithm.getHash()); + } + + Signer s; + if (algorithm != null) + { + /* + * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated + * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1]. + */ + s = new RSADigestSigner(d, TlsUtils.getOIDForHashAlgorithm(algorithm.getHash())); + } + else + { + /* + * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme + * that did not include a DigestInfo encoding. + */ + s = new GenericSigner(createRSAImpl(), d); + } + s.init(forSigning, cp); + return s; + } + + protected AsymmetricBlockCipher createRSAImpl() + { + /* + * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks + * on TLS are possible, at least when the client and server are on the same LAN. + * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other + * anti-timing technique, as described in [TIMING]. + */ + return new PKCS1Encoding(new RSABlindedEngine()); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSAUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSAUtils.java new file mode 100644 index 000000000..70ef035d7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRSAUtils.java @@ -0,0 +1,116 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.RSAKeyParameters; + +public class TlsRSAUtils +{ + public static byte[] generateEncryptedPreMasterSecret(TlsContext context, RSAKeyParameters rsaServerPublicKey, + OutputStream output) throws IOException + { + /* + * Choose a PremasterSecret and send it encrypted to the server + */ + byte[] premasterSecret = new byte[48]; + context.getSecureRandom().nextBytes(premasterSecret); + TlsUtils.writeVersion(context.getClientVersion(), premasterSecret, 0); + + PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine()); + encoding.init(true, new ParametersWithRandom(rsaServerPublicKey, context.getSecureRandom())); + + try + { + byte[] encryptedPreMasterSecret = encoding.processBlock(premasterSecret, 0, premasterSecret.length); + + if (TlsUtils.isSSL(context)) + { + // TODO Do any SSLv3 servers actually expect the length? + output.write(encryptedPreMasterSecret); + } + else + { + TlsUtils.writeOpaque16(encryptedPreMasterSecret, output); + } + } + catch (InvalidCipherTextException e) + { + /* + * This should never happen, only during decryption. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return premasterSecret; + } + + public static byte[] safeDecryptPreMasterSecret(TlsContext context, TlsEncryptionCredentials encryptionCredentials, + byte[] encryptedPreMasterSecret) + { + /* + * RFC 5246 7.4.7.1. + */ + + ProtocolVersion clientVersion = context.getClientVersion(); + + // TODO Provide as configuration option? + boolean versionNumberCheckDisabled = false; + + /* + * See notes regarding Bleichenbacher/Klima attack. The code here implements the first + * construction proposed there, which is RECOMMENDED. + */ + byte[] R = new byte[48]; + context.getSecureRandom().nextBytes(R); + + byte[] M = TlsUtils.EMPTY_BYTES; + try + { + M = encryptionCredentials.decryptPreMasterSecret(encryptedPreMasterSecret); + } + catch (Exception e) + { + /* + * In any case, a TLS server MUST NOT generate an alert if processing an + * RSA-encrypted premaster secret message fails, or the version number is not as + * expected. Instead, it MUST continue the handshake with a randomly generated + * premaster secret. + */ + } + + if (M.length != 48) + { + TlsUtils.writeVersion(clientVersion, R, 0); + return R; + } + + /* + * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST + * check the version number [..]. + */ + if (versionNumberCheckDisabled && clientVersion.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10)) + { + /* + * If the version number is TLS 1.0 or earlier, server implementations SHOULD + * check the version number, but MAY have a configuration option to disable the + * check. + */ + } + else + { + /* + * Note that explicitly constructing the pre_master_secret with the + * ClientHello.client_version produces an invalid master_secret if the client + * has sent the wrong version in the original pre_master_secret. + */ + TlsUtils.writeVersion(clientVersion, M, 0); + } + + return M; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRuntimeException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRuntimeException.java new file mode 100644 index 000000000..97d30b9aa --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsRuntimeException.java @@ -0,0 +1,26 @@ +package org.spongycastle.crypto.tls; + +public class TlsRuntimeException + extends RuntimeException +{ + private static final long serialVersionUID = 1928023487348344086L; + + Throwable e; + + public TlsRuntimeException(String message, Throwable e) + { + super(message); + + this.e = e; + } + + public TlsRuntimeException(String message) + { + super(message); + } + + public Throwable getCause() + { + return e; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRPKeyExchange.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRPKeyExchange.java new file mode 100644 index 000000000..1b171ff4b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRPKeyExchange.java @@ -0,0 +1,205 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Vector; + +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.agreement.srp.SRP6Client; +import org.spongycastle.crypto.agreement.srp.SRP6Util; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.io.TeeInputStream; + +/** + * TLS 1.1 SRP key exchange (RFC 5054). + */ +public class TlsSRPKeyExchange extends AbstractTlsKeyExchange +{ + protected TlsSigner tlsSigner; + protected byte[] identity; + protected byte[] password; + + protected AsymmetricKeyParameter serverPublicKey = null; + + protected byte[] s = null; + protected BigInteger B = null; + protected SRP6Client srpClient = new SRP6Client(); + + public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password) + { + super(keyExchange, supportedSignatureAlgorithms); + + switch (keyExchange) + { + case KeyExchangeAlgorithm.SRP: + this.tlsSigner = null; + break; + case KeyExchangeAlgorithm.SRP_RSA: + this.tlsSigner = new TlsRSASigner(); + break; + case KeyExchangeAlgorithm.SRP_DSS: + this.tlsSigner = new TlsDSSSigner(); + break; + default: + throw new IllegalArgumentException("unsupported key exchange algorithm"); + } + + this.keyExchange = keyExchange; + this.identity = identity; + this.password = password; + } + + public void init(TlsContext context) + { + super.init(context); + + if (this.tlsSigner != null) { + this.tlsSigner.init(context); + } + } + + public void skipServerCredentials() throws IOException + { + if (tlsSigner != null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public void processServerCertificate(Certificate serverCertificate) throws IOException + { + if (tlsSigner == null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + if (serverCertificate.isEmpty()) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + org.spongycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + try + { + this.serverPublicKey = PublicKeyFactory.createKey(keyInfo); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + if (!tlsSigner.isValidPublicKey(this.serverPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature); + + super.processServerCertificate(serverCertificate); + } + + public boolean requiresServerKeyExchange() + { + return true; + } + + public void processServerKeyExchange(InputStream input) throws IOException + { + SecurityParameters securityParameters = context.getSecurityParameters(); + + SignerInputBuffer buf = null; + InputStream teeIn = input; + + if (tlsSigner != null) + { + buf = new SignerInputBuffer(); + teeIn = new TeeInputStream(input, buf); + } + + byte[] NBytes = TlsUtils.readOpaque16(teeIn); + byte[] gBytes = TlsUtils.readOpaque16(teeIn); + byte[] sBytes = TlsUtils.readOpaque8(teeIn); + byte[] BBytes = TlsUtils.readOpaque16(teeIn); + + if (buf != null) + { + DigitallySigned signed_params = DigitallySigned.parse(context, input); + + Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters); + buf.updateSigner(signer); + if (!signer.verifySignature(signed_params.getSignature())) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + + BigInteger N = new BigInteger(1, NBytes); + BigInteger g = new BigInteger(1, gBytes); + + // TODO Validate group parameters (see RFC 5054) +// throw new TlsFatalAlert(AlertDescription.insufficient_security); + + this.s = sBytes; + + /* + * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if + * B % N = 0. + */ + try + { + this.B = SRP6Util.validatePublicValue(N, new BigInteger(1, BBytes)); + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.srpClient.init(N, g, new SHA1Digest(), context.getSecureRandom()); + } + + public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public void processClientCredentials(TlsCredentials clientCredentials) throws IOException + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public void generateClientKeyExchange(OutputStream output) throws IOException + { + BigInteger A = srpClient.generateClientCredentials(s, this.identity, this.password); + TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(A), output); + } + + public byte[] generatePremasterSecret() throws IOException + { + try + { + // TODO Check if this needs to be a fixed size + return BigIntegers.asUnsignedByteArray(srpClient.calculateSecret(B)); + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters) + { + Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey); + signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); + signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); + return signer; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRPUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRPUtils.java new file mode 100644 index 000000000..74fac926f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRPUtils.java @@ -0,0 +1,49 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.util.Integers; + +public class TlsSRPUtils +{ + public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp); + + public static void addSRPExtension(Hashtable extensions, byte[] identity) throws IOException + { + extensions.put(EXT_SRP, createSRPExtension(identity)); + } + + public static byte[] getSRPExtension(Hashtable extensions) throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_SRP); + return extensionData == null ? null : readSRPExtension(extensionData); + } + + public static byte[] createSRPExtension(byte[] identity) throws IOException + { + if (identity == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return TlsUtils.encodeOpaque8(identity); + } + + public static byte[] readSRPExtension(byte[] extensionData) throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + byte[] identity = TlsUtils.readOpaque8(buf); + + TlsProtocol.assertEmpty(buf); + + return identity; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRTPUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRTPUtils.java new file mode 100644 index 000000000..edc48795a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSRTPUtils.java @@ -0,0 +1,74 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.spongycastle.util.Integers; + +/** + * RFC 5764 DTLS Extension to Establish Keys for SRTP. + */ +public class TlsSRTPUtils +{ + public static final Integer EXT_use_srtp = Integers.valueOf(ExtensionType.use_srtp); + + public static void addUseSRTPExtension(Hashtable extensions, UseSRTPData useSRTPData) + throws IOException + { + extensions.put(EXT_use_srtp, createUseSRTPExtension(useSRTPData)); + } + + public static UseSRTPData getUseSRTPExtension(Hashtable extensions) + throws IOException + { + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_use_srtp); + return extensionData == null ? null : readUseSRTPExtension(extensionData); + } + + public static byte[] createUseSRTPExtension(UseSRTPData useSRTPData) + throws IOException + { + if (useSRTPData == null) + { + throw new IllegalArgumentException("'useSRTPData' cannot be null"); + } + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + // SRTPProtectionProfiles + TlsUtils.writeUint16ArrayWithUint16Length(useSRTPData.getProtectionProfiles(), buf); + + // srtp_mki + TlsUtils.writeOpaque8(useSRTPData.getMki(), buf); + + return buf.toByteArray(); + } + + public static UseSRTPData readUseSRTPExtension(byte[] extensionData) + throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + // SRTPProtectionProfiles + int length = TlsUtils.readUint16(buf); + if (length < 2 || (length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + int[] protectionProfiles = TlsUtils.readUint16Array(length / 2, buf); + + // srtp_mki + byte[] mki = TlsUtils.readOpaque8(buf); + + TlsProtocol.assertEmpty(buf); + + return new UseSRTPData(protectionProfiles, mki); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServer.java new file mode 100644 index 000000000..3dcd14ff4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServer.java @@ -0,0 +1,90 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; + +public interface TlsServer + extends TlsPeer +{ + void init(TlsServerContext context); + + void notifyClientVersion(ProtocolVersion clientVersion) throws IOException; + + void notifyOfferedCipherSuites(int[] offeredCipherSuites) + throws IOException; + + void notifyOfferedCompressionMethods(short[] offeredCompressionMethods) + throws IOException; + + // Hashtable is (Integer -> byte[]) + void processClientExtensions(Hashtable clientExtensions) + throws IOException; + + ProtocolVersion getServerVersion() + throws IOException; + + int getSelectedCipherSuite() + throws IOException; + + short getSelectedCompressionMethod() + throws IOException; + + // Hashtable is (Integer -> byte[]) + Hashtable getServerExtensions() + throws IOException; + + // Vector is (SupplementalDataEntry) + Vector getServerSupplementalData() + throws IOException; + + TlsCredentials getCredentials() + throws IOException; + + /** + * This method will be called (only) if the server included an extension of type + * "status_request" with empty "extension_data" in the extended server hello. See RFC 3546 + * 3.6. Certificate Status Request. If a non-null {@link CertificateStatus} is returned, it + * is sent to the client as a handshake message of type "certificate_status". + * + * @return A {@link CertificateStatus} to be sent to the client (or null for none). + * @throws IOException + */ + CertificateStatus getCertificateStatus() + throws IOException; + + TlsKeyExchange getKeyExchange() + throws IOException; + + CertificateRequest getCertificateRequest() + throws IOException; + + // Vector is (SupplementalDataEntry) + void processClientSupplementalData(Vector clientSupplementalData) + throws IOException; + + /** + * Called by the protocol handler to report the client certificate, only if + * {@link #getCertificateRequest()} returned non-null. + * + * Note: this method is responsible for certificate verification and validation. + * + * @param clientCertificate + * the effective client certificate (may be an empty chain). + * @throws IOException + */ + void notifyClientCertificate(Certificate clientCertificate) + throws IOException; + + /** + * RFC 5077 3.3. NewSessionTicket Handshake Message. + * + * This method will be called (only) if a NewSessionTicket extension was sent by the server. See + * RFC 5077 4. Recommended Ticket Construction for recommended format and protection. + * + * @return The ticket. + * @throws IOException + */ + NewSessionTicket getNewSessionTicket() + throws IOException; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerContext.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerContext.java new file mode 100644 index 000000000..48480f364 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerContext.java @@ -0,0 +1,6 @@ +package org.spongycastle.crypto.tls; + +public interface TlsServerContext + extends TlsContext +{ +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerContextImpl.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerContextImpl.java new file mode 100644 index 000000000..270633506 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerContextImpl.java @@ -0,0 +1,18 @@ +package org.spongycastle.crypto.tls; + +import java.security.SecureRandom; + +class TlsServerContextImpl + extends AbstractTlsContext + implements TlsServerContext +{ + TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + { + super(secureRandom, securityParameters); + } + + public boolean isServer() + { + return true; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerProtocol.java new file mode 100644 index 000000000..3411086e2 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsServerProtocol.java @@ -0,0 +1,760 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Vector; + +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.util.Arrays; + +public class TlsServerProtocol + extends TlsProtocol +{ + protected TlsServer tlsServer = null; + protected TlsServerContextImpl tlsServerContext = null; + + protected TlsKeyExchange keyExchange = null; + protected TlsCredentials serverCredentials = null; + protected CertificateRequest certificateRequest = null; + + protected short clientCertificateType = -1; + protected TlsHandshakeHash prepareFinishHash = null; + + public TlsServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom) + { + super(input, output, secureRandom); + } + + /** + * Receives a TLS handshake in the role of server + * + * @param tlsServer + * @throws IOException If handshake was not successful. + */ + public void accept(TlsServer tlsServer) + throws IOException + { + if (tlsServer == null) + { + throw new IllegalArgumentException("'tlsServer' cannot be null"); + } + if (this.tlsServer != null) + { + throw new IllegalStateException("'accept' can only be called once"); + } + + this.tlsServer = tlsServer; + + this.securityParameters = new SecurityParameters(); + this.securityParameters.entity = ConnectionEnd.server; + this.securityParameters.serverRandom = createRandomBlock(secureRandom); + + this.tlsServerContext = new TlsServerContextImpl(secureRandom, securityParameters); + this.tlsServer.init(tlsServerContext); + this.recordStream.init(tlsServerContext); + + this.recordStream.setRestrictReadVersion(false); + + completeHandshake(); + } + + protected void cleanupHandshake() + { + super.cleanupHandshake(); + + this.keyExchange = null; + this.serverCredentials = null; + this.certificateRequest = null; + this.prepareFinishHash = null; + } + + protected AbstractTlsContext getContext() + { + return tlsServerContext; + } + + protected TlsPeer getPeer() + { + return tlsServer; + } + + protected void handleHandshakeMessage(short type, byte[] data) + throws IOException + { + ByteArrayInputStream buf = new ByteArrayInputStream(data); + + switch (type) + { + case HandshakeType.client_hello: + { + switch (this.connection_state) + { + case CS_START: + { + receiveClientHelloMessage(buf); + this.connection_state = CS_CLIENT_HELLO; + + sendServerHelloMessage(); + this.connection_state = CS_SERVER_HELLO; + + Vector serverSupplementalData = tlsServer.getServerSupplementalData(); + if (serverSupplementalData != null) + { + sendSupplementalDataMessage(serverSupplementalData); + } + this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA; + + this.keyExchange = tlsServer.getKeyExchange(); + this.keyExchange.init(getContext()); + + this.serverCredentials = tlsServer.getCredentials(); + + Certificate serverCertificate = null; + + if (this.serverCredentials == null) + { + this.keyExchange.skipServerCredentials(); + } + else + { + this.keyExchange.processServerCredentials(this.serverCredentials); + + serverCertificate = this.serverCredentials.getCertificate(); + sendCertificateMessage(serverCertificate); + } + this.connection_state = CS_SERVER_CERTIFICATE; + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.isEmpty()) + { + this.allowCertificateStatus = false; + } + + if (this.allowCertificateStatus) + { + CertificateStatus certificateStatus = tlsServer.getCertificateStatus(); + if (certificateStatus != null) + { + sendCertificateStatusMessage(certificateStatus); + } + } + + this.connection_state = CS_CERTIFICATE_STATUS; + + byte[] serverKeyExchange = this.keyExchange.generateServerKeyExchange(); + if (serverKeyExchange != null) + { + sendServerKeyExchangeMessage(serverKeyExchange); + } + this.connection_state = CS_SERVER_KEY_EXCHANGE; + + if (this.serverCredentials != null) + { + this.certificateRequest = tlsServer.getCertificateRequest(); + if (this.certificateRequest != null) + { + this.keyExchange.validateCertificateRequest(certificateRequest); + + sendCertificateRequestMessage(certificateRequest); + + TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(), + this.certificateRequest.getSupportedSignatureAlgorithms()); + } + } + this.connection_state = CS_CERTIFICATE_REQUEST; + + sendServerHelloDoneMessage(); + this.connection_state = CS_SERVER_HELLO_DONE; + + this.recordStream.getHandshakeHash().sealHashAlgorithms(); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.supplemental_data: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO_DONE: + { + tlsServer.processClientSupplementalData(readSupplementalDataMessage(buf)); + this.connection_state = CS_CLIENT_SUPPLEMENTAL_DATA; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.certificate: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO_DONE: + { + tlsServer.processClientSupplementalData(null); + // NB: Fall through to next case label + } + case CS_CLIENT_SUPPLEMENTAL_DATA: + { + if (this.certificateRequest == null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + receiveCertificateMessage(buf); + this.connection_state = CS_CLIENT_CERTIFICATE; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.client_key_exchange: + { + switch (this.connection_state) + { + case CS_SERVER_HELLO_DONE: + { + tlsServer.processClientSupplementalData(null); + // NB: Fall through to next case label + } + case CS_CLIENT_SUPPLEMENTAL_DATA: + { + if (this.certificateRequest == null) + { + this.keyExchange.skipClientCredentials(); + } + else + { + if (TlsUtils.isTLSv12(getContext())) + { + /* + * RFC 5246 If no suitable certificate is available, the client MUST send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + else if (TlsUtils.isSSL(getContext())) + { + if (this.peerCertificate == null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + else + { + notifyClientCertificate(Certificate.EMPTY_CHAIN); + } + } + // NB: Fall through to next case label + } + case CS_CLIENT_CERTIFICATE: + { + receiveClientKeyExchangeMessage(buf); + this.connection_state = CS_CLIENT_KEY_EXCHANGE; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.certificate_verify: + { + switch (this.connection_state) + { + case CS_CLIENT_KEY_EXCHANGE: + { + /* + * RFC 5246 7.4.8 This message is only sent following a client certificate that has + * signing capability (i.e., all certificates except those containing fixed + * Diffie-Hellman parameters). + */ + if (!expectCertificateVerifyMessage()) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + receiveCertificateVerifyMessage(buf); + this.connection_state = CS_CERTIFICATE_VERIFY; + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.finished: + { + switch (this.connection_state) + { + case CS_CLIENT_KEY_EXCHANGE: + { + if (expectCertificateVerifyMessage()) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + // NB: Fall through to next case label + } + case CS_CERTIFICATE_VERIFY: + { + processFinishedMessage(buf); + this.connection_state = CS_CLIENT_FINISHED; + + if (this.expectSessionTicket) + { + sendNewSessionTicketMessage(tlsServer.getNewSessionTicket()); + sendChangeCipherSpecMessage(); + } + this.connection_state = CS_SERVER_SESSION_TICKET; + + sendFinishedMessage(); + this.connection_state = CS_SERVER_FINISHED; + this.connection_state = CS_END; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.hello_request: + case HandshakeType.hello_verify_request: + case HandshakeType.server_hello: + case HandshakeType.server_key_exchange: + case HandshakeType.certificate_request: + case HandshakeType.server_hello_done: + case HandshakeType.session_ticket: + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + protected void handleWarningMessage(short description) + throws IOException + { + switch (description) + { + case AlertDescription.no_certificate: + { + /* + * SSL 3.0 If the server has sent a certificate request Message, the client must send + * either the certificate message or a no_certificate alert. + */ + if (TlsUtils.isSSL(getContext()) && certificateRequest != null) + { + notifyClientCertificate(Certificate.EMPTY_CHAIN); + } + break; + } + default: + { + super.handleWarningMessage(description); + } + } + } + + protected void notifyClientCertificate(Certificate clientCertificate) + throws IOException + { + if (certificateRequest == null) + { + throw new IllegalStateException(); + } + + if (this.peerCertificate != null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.peerCertificate = clientCertificate; + + if (clientCertificate.isEmpty()) + { + this.keyExchange.skipClientCredentials(); + } + else + { + + /* + * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request + * message was non-empty, one of the certificates in the certificate chain SHOULD be + * issued by one of the listed CAs. + */ + + this.clientCertificateType = TlsUtils.getClientCertificateType(clientCertificate, + this.serverCredentials.getCertificate()); + + this.keyExchange.processClientCertificate(clientCertificate); + } + + /* + * RFC 5246 7.4.6. If the client does not send any certificates, the server MAY at its + * discretion either continue the handshake without client authentication, or respond with a + * fatal handshake_failure alert. Also, if some aspect of the certificate chain was + * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its + * discretion either continue the handshake (considering the client unauthenticated) or send + * a fatal alert. + */ + this.tlsServer.notifyClientCertificate(clientCertificate); + } + + protected void receiveCertificateMessage(ByteArrayInputStream buf) + throws IOException + { + Certificate clientCertificate = Certificate.parse(buf); + + assertEmpty(buf); + + notifyClientCertificate(clientCertificate); + } + + protected void receiveCertificateVerifyMessage(ByteArrayInputStream buf) + throws IOException + { + DigitallySigned clientCertificateVerify = DigitallySigned.parse(getContext(), buf); + + assertEmpty(buf); + + // Verify the CertificateVerify message contains a correct signature. + try + { + // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned + byte[] certificateVerifyHash = getCurrentPRFHash(getContext(), prepareFinishHash, null); + + org.spongycastle.asn1.x509.Certificate x509Cert = this.peerCertificate.getCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); + + TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType); + tlsSigner.init(getContext()); + tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(), + clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + + protected void receiveClientHelloMessage(ByteArrayInputStream buf) + throws IOException + { + ProtocolVersion client_version = TlsUtils.readVersion(buf); + if (client_version.isDTLS()) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + byte[] client_random = TlsUtils.readFully(32, buf); + + /* + * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to + * use the Session ID in the ClientHello for stateful session resumption. + */ + byte[] sessionID = TlsUtils.readOpaque8(buf); + if (sessionID.length > 32) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + /* + * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session + * resumption request), this vector MUST include at least the cipher_suite from that + * session. + */ + int cipher_suites_length = TlsUtils.readUint16(buf); + if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf); + + /* + * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session + * resumption request), it MUST include the compression_method from that session. + */ + int compression_methods_length = TlsUtils.readUint8(buf); + if (compression_methods_length < 1) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + this.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf); + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + this.clientExtensions = readExtensions(buf); + + getContext().setClientVersion(client_version); + + tlsServer.notifyClientVersion(client_version); + + securityParameters.clientRandom = client_random; + + tlsServer.notifyOfferedCipherSuites(offeredCipherSuites); + tlsServer.notifyOfferedCompressionMethods(offeredCompressionMethods); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + + /* + * When a ClientHello is received, the server MUST check if it includes the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag + * to TRUE. + */ + if (Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + { + this.secure_renegotiation = true; + } + + /* + * The server MUST check if the "renegotiation_info" extension is included in the + * ClientHello. + */ + byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo); + if (renegExtData != null) + { + /* + * If the extension is present, set secure_renegotiation flag to TRUE. The + * server MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake. + */ + this.secure_renegotiation = true; + + if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + } + + tlsServer.notifySecureRenegotiation(this.secure_renegotiation); + + if (clientExtensions != null) + { + tlsServer.processClientExtensions(clientExtensions); + } + } + + protected void receiveClientKeyExchangeMessage(ByteArrayInputStream buf) + throws IOException + { + this.keyExchange.processClientKeyExchange(buf); + + assertEmpty(buf); + + establishMasterSecret(getContext(), keyExchange); + recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); + + this.prepareFinishHash = recordStream.prepareToFinish(); + + if (!expectSessionTicket) + { + sendChangeCipherSpecMessage(); + } + } + + protected void sendCertificateRequestMessage(CertificateRequest certificateRequest) + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request); + + certificateRequest.encode(message); + + message.writeToRecordStream(); + } + + protected void sendCertificateStatusMessage(CertificateStatus certificateStatus) + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status); + + certificateStatus.encode(message); + + message.writeToRecordStream(); + } + + protected void sendNewSessionTicketMessage(NewSessionTicket newSessionTicket) + throws IOException + { + if (newSessionTicket == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket); + + newSessionTicket.encode(message); + + message.writeToRecordStream(); + } + + protected void sendServerHelloMessage() + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello); + + ProtocolVersion server_version = tlsServer.getServerVersion(); + if (!server_version.isEqualOrEarlierVersionOf(getContext().getClientVersion())) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + recordStream.setReadVersion(server_version); + recordStream.setWriteVersion(server_version); + recordStream.setRestrictReadVersion(true); + getContext().setServerVersion(server_version); + + TlsUtils.writeVersion(server_version, message); + + message.write(this.securityParameters.serverRandom); + + /* + * The server may return an empty session_id to indicate that the session will not be cached + * and therefore cannot be resumed. + */ + TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, message); + + int selectedCipherSuite = tlsServer.getSelectedCipherSuite(); + if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + securityParameters.cipherSuite = selectedCipherSuite; + + short selectedCompressionMethod = tlsServer.getSelectedCompressionMethod(); + if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + securityParameters.compressionAlgorithm = selectedCompressionMethod; + + TlsUtils.writeUint16(selectedCipherSuite, message); + TlsUtils.writeUint8(selectedCompressionMethod, message); + + this.serverExtensions = tlsServer.getServerExtensions(); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + if (this.secure_renegotiation) + { + byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); + + if (noRenegExt) + { + /* + * Note that sending a "renegotiation_info" extension in response to a ClientHello + * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, + * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed + * because the client is signaling its willingness to receive the extension via the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + + /* + * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty + * "renegotiation_info" extension in the ServerHello message. + */ + this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.serverExtensions); + this.serverExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)); + } + } + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and send a server hello containing no + * extensions. + */ + + if (this.serverExtensions != null) + { + this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(clientExtensions, + this.serverExtensions, AlertDescription.internal_error); + + this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(this.serverExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + this.allowCertificateStatus = !this.resumedSession + && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsExtensionsUtils.EXT_status_request, + AlertDescription.internal_error); + + this.expectSessionTicket = !this.resumedSession + && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsProtocol.EXT_SessionTicket, + AlertDescription.internal_error); + + writeExtensions(message, this.serverExtensions); + } + + if (this.securityParameters.maxFragmentLength >= 0) + { + int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength); + recordStream.setPlaintextLimit(plainTextLimit); + } + + securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(), securityParameters.getCipherSuite()); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has + * a verify_data_length equal to 12. This includes all existing cipher suites. + */ + securityParameters.verifyDataLength = 12; + + message.writeToRecordStream(); + + this.recordStream.notifyHelloComplete(); + } + + protected void sendServerHelloDoneMessage() + throws IOException + { + byte[] message = new byte[4]; + TlsUtils.writeUint8(HandshakeType.server_hello_done, message, 0); + TlsUtils.writeUint24(0, message, 1); + + writeHandshakeMessage(message, 0, message.length); + } + + protected void sendServerKeyExchangeMessage(byte[] serverKeyExchange) + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.length); + + message.write(serverKeyExchange); + + message.writeToRecordStream(); + } + + protected boolean expectCertificateVerifyMessage() + { + return this.clientCertificateType >= 0 && TlsUtils.hasSigningCapability(this.clientCertificateType); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSession.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSession.java new file mode 100644 index 000000000..6d30e4426 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSession.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +public interface TlsSession +{ + SessionParameters exportSessionParameters(); + + byte[] getSessionID(); + + void invalidate(); + + boolean isResumable(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSessionImpl.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSessionImpl.java new file mode 100644 index 000000000..af11bf08a --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSessionImpl.java @@ -0,0 +1,48 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.util.Arrays; + +class TlsSessionImpl implements TlsSession +{ + final byte[] sessionID; + SessionParameters sessionParameters; + + TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters) + { + if (sessionID == null) + { + throw new IllegalArgumentException("'sessionID' cannot be null"); + } + if (sessionID.length < 1 || sessionID.length > 32) + { + throw new IllegalArgumentException("'sessionID' must have length between 1 and 32 bytes, inclusive"); + } + + this.sessionID = Arrays.clone(sessionID); + this.sessionParameters = sessionParameters; + } + + public synchronized SessionParameters exportSessionParameters() + { + return this.sessionParameters == null ? null : this.sessionParameters.copy(); + } + + public synchronized byte[] getSessionID() + { + return sessionID; + } + + public synchronized void invalidate() + { + if (this.sessionParameters != null) + { + this.sessionParameters.clear(); + this.sessionParameters = null; + } + } + + public synchronized boolean isResumable() + { + return this.sessionParameters != null; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSigner.java new file mode 100644 index 000000000..021155e38 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSigner.java @@ -0,0 +1,34 @@ +package org.spongycastle.crypto.tls; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +public interface TlsSigner +{ + void init(TlsContext context); + + byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + throws CryptoException; + + byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) + throws CryptoException; + + boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + throws CryptoException; + + boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) + throws CryptoException; + + Signer createSigner(AsymmetricKeyParameter privateKey); + + Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); + + Signer createVerifyer(AsymmetricKeyParameter publicKey); + + Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); + + boolean isValidPublicKey(AsymmetricKeyParameter publicKey); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSignerCredentials.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSignerCredentials.java new file mode 100644 index 000000000..da1a91cd7 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsSignerCredentials.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +public interface TlsSignerCredentials + extends TlsCredentials +{ + byte[] generateCertificateSignature(byte[] hash) + throws IOException; + + SignatureAndHashAlgorithm getSignatureAndHashAlgorithm(); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsStreamCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsStreamCipher.java new file mode 100644 index 000000000..ff065ee88 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsStreamCipher.java @@ -0,0 +1,163 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.util.Arrays; + +public class TlsStreamCipher + implements TlsCipher +{ + private static boolean encryptThenMAC = false; + + protected TlsContext context; + + protected StreamCipher encryptCipher; + protected StreamCipher decryptCipher; + + protected TlsMac writeMac; + protected TlsMac readMac; + + public TlsStreamCipher(TlsContext context, StreamCipher clientWriteCipher, + StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest, + int cipherKeySize) throws IOException + { + boolean isServer = context.isServer(); + + this.context = context; + + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + + int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize() + + serverWriteDigest.getDigestSize(); + + byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size); + + int offset = 0; + + // Init MACs + TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.getDigestSize()); + offset += clientWriteDigest.getDigestSize(); + TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.getDigestSize()); + offset += serverWriteDigest.getDigestSize(); + + // Build keys + KeyParameter clientWriteKey = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter serverWriteKey = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + CipherParameters encryptParams, decryptParams; + if (isServer) + { + this.writeMac = serverWriteMac; + this.readMac = clientWriteMac; + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + encryptParams = serverWriteKey; + decryptParams = clientWriteKey; + } + else + { + this.writeMac = clientWriteMac; + this.readMac = serverWriteMac; + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + encryptParams = clientWriteKey; + decryptParams = serverWriteKey; + } + + this.encryptCipher.init(true, encryptParams); + this.decryptCipher.init(false, decryptParams); + } + + public int getPlaintextLimit(int ciphertextLimit) + { + return ciphertextLimit - writeMac.getSize(); + } + + public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) + { + /* + * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That + * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS + * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation + * of the 16-bit epoch with the 48-bit sequence number. + */ + + byte[] outBuf = new byte[len + writeMac.getSize()]; + + encryptCipher.processBytes(plaintext, offset, len, outBuf, 0); + + if (encryptThenMAC) + { + byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, len); + System.arraycopy(mac, 0, outBuf, len, mac.length); + } + else + { + byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len); + encryptCipher.processBytes(mac, 0, mac.length, outBuf, len); + } + + return outBuf; + } + + public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len) + throws IOException + { + /* + * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That + * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS + * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation + * of the 16-bit epoch with the 48-bit sequence number. + */ + + int macSize = readMac.getSize(); + if (len < macSize) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + int plaintextLength = len - macSize; + + if (encryptThenMAC) + { + int ciphertextEnd = offset + len; + checkMAC(seqNo, type, ciphertext, ciphertextEnd - macSize, ciphertextEnd, ciphertext, offset, plaintextLength); + byte[] deciphered = new byte[plaintextLength]; + decryptCipher.processBytes(ciphertext, offset, plaintextLength, deciphered, 0); + return deciphered; + } + else + { + byte[] deciphered = new byte[len]; + decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0); + checkMAC(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength); + return Arrays.copyOfRange(deciphered, 0, plaintextLength); + } + } + + private void checkMAC(long seqNo, short type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen) + throws IOException + { + byte[] receivedMac = Arrays.copyOfRange(recBuf, recStart, recEnd); + byte[] computedMac = readMac.calculateMac(seqNo, type, calcBuf, calcOff, calcLen); + + if (!Arrays.constantTimeAreEqual(receivedMac, computedMac)) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsUtils.java new file mode 100644 index 000000000..802db189d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsUtils.java @@ -0,0 +1,1261 @@ +package org.spongycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Integers; +import org.spongycastle.util.Strings; +import org.spongycastle.util.io.Streams; + +/** + * Some helper functions for MicroTLS. + */ +public class TlsUtils +{ + public static byte[] EMPTY_BYTES = new byte[0]; + + public static final Integer EXT_signature_algorithms = Integers.valueOf(ExtensionType.signature_algorithms); + + public static void checkUint8(short i) throws IOException + { + if (!isValidUint8(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static void checkUint8(int i) throws IOException + { + if (!isValidUint8(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static void checkUint16(int i) throws IOException + { + if (!isValidUint16(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static void checkUint24(int i) throws IOException + { + if (!isValidUint24(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static void checkUint32(long i) throws IOException + { + if (!isValidUint32(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static void checkUint48(long i) throws IOException + { + if (!isValidUint48(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static void checkUint64(long i) throws IOException + { + if (!isValidUint64(i)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static boolean isValidUint8(short i) + { + return (i & 0xFF) == i; + } + + public static boolean isValidUint8(int i) + { + return (i & 0xFF) == i; + } + + public static boolean isValidUint16(int i) + { + return (i & 0xFFFF) == i; + } + + public static boolean isValidUint24(int i) + { + return (i & 0xFFFFFF) == i; + } + + public static boolean isValidUint32(long i) + { + return (i & 0xFFFFFFFFL) == i; + } + + public static boolean isValidUint48(long i) + { + return (i & 0xFFFFFFFFFFFFL) == i; + } + + public static boolean isValidUint64(long i) + { + return true; + } + + public static boolean isSSL(TlsContext context) + { + return context.getServerVersion().isSSL(); + } + + public static boolean isTLSv11(TlsContext context) + { + return ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()); + } + + public static boolean isTLSv12(TlsContext context) + { + return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()); + } + + public static void writeUint8(short i, OutputStream output) + throws IOException + { + output.write(i); + } + + public static void writeUint8(int i, OutputStream output) + throws IOException + { + output.write(i); + } + + public static void writeUint8(short i, byte[] buf, int offset) + { + buf[offset] = (byte)i; + } + + public static void writeUint8(int i, byte[] buf, int offset) + { + buf[offset] = (byte)i; + } + + public static void writeUint16(int i, OutputStream output) + throws IOException + { + output.write(i >> 8); + output.write(i); + } + + public static void writeUint16(int i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 8); + buf[offset + 1] = (byte)i; + } + + public static void writeUint24(int i, OutputStream output) + throws IOException + { + output.write(i >> 16); + output.write(i >> 8); + output.write(i); + } + + public static void writeUint24(int i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 16); + buf[offset + 1] = (byte)(i >> 8); + buf[offset + 2] = (byte)(i); + } + + public static void writeUint32(long i, OutputStream output) + throws IOException + { + output.write((int)(i >> 24)); + output.write((int)(i >> 16)); + output.write((int)(i >> 8)); + output.write((int)(i)); + } + + public static void writeUint32(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 24); + buf[offset + 1] = (byte)(i >> 16); + buf[offset + 2] = (byte)(i >> 8); + buf[offset + 3] = (byte)(i); + } + + public static void writeUint48(long i, OutputStream output) + throws IOException + { + output.write((byte)(i >> 40)); + output.write((byte)(i >> 32)); + output.write((byte)(i >> 24)); + output.write((byte)(i >> 16)); + output.write((byte)(i >> 8)); + output.write((byte)(i)); + } + + public static void writeUint48(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 40); + buf[offset + 1] = (byte)(i >> 32); + buf[offset + 2] = (byte)(i >> 24); + buf[offset + 3] = (byte)(i >> 16); + buf[offset + 4] = (byte)(i >> 8); + buf[offset + 5] = (byte)(i); + } + + public static void writeUint64(long i, OutputStream output) + throws IOException + { + output.write((byte)(i >> 56)); + output.write((byte)(i >> 48)); + output.write((byte)(i >> 40)); + output.write((byte)(i >> 32)); + output.write((byte)(i >> 24)); + output.write((byte)(i >> 16)); + output.write((byte)(i >> 8)); + output.write((byte)(i)); + } + + public static void writeUint64(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 56); + buf[offset + 1] = (byte)(i >> 48); + buf[offset + 2] = (byte)(i >> 40); + buf[offset + 3] = (byte)(i >> 32); + buf[offset + 4] = (byte)(i >> 24); + buf[offset + 5] = (byte)(i >> 16); + buf[offset + 6] = (byte)(i >> 8); + buf[offset + 7] = (byte)(i); + } + + public static void writeOpaque8(byte[] buf, OutputStream output) + throws IOException + { + checkUint8(buf.length); + writeUint8(buf.length, output); + output.write(buf); + } + + public static void writeOpaque16(byte[] buf, OutputStream output) + throws IOException + { + checkUint16(buf.length); + writeUint16(buf.length, output); + output.write(buf); + } + + public static void writeOpaque24(byte[] buf, OutputStream output) + throws IOException + { + checkUint24(buf.length); + writeUint24(buf.length, output); + output.write(buf); + } + + public static void writeUint8Array(short[] uints, OutputStream output) + throws IOException + { + for (int i = 0; i < uints.length; ++i) + { + writeUint8(uints[i], output); + } + } + + public static void writeUint8Array(short[] uints, byte[] buf, int offset) + throws IOException + { + for (int i = 0; i < uints.length; ++i) + { + writeUint8(uints[i], buf, offset); + ++offset; + } + } + + public static void writeUint8ArrayWithUint8Length(short[] uints, OutputStream output) + throws IOException + { + checkUint8(uints.length); + writeUint8(uints.length, output); + writeUint8Array(uints, output); + } + + public static void writeUint8ArrayWithUint8Length(short[] uints, byte[] buf, int offset) + throws IOException + { + checkUint8(uints.length); + writeUint8(uints.length, buf, offset); + writeUint8Array(uints, buf, offset + 1); + } + + public static void writeUint16Array(int[] uints, OutputStream output) + throws IOException + { + for (int i = 0; i < uints.length; ++i) + { + writeUint16(uints[i], output); + } + } + + public static void writeUint16Array(int[] uints, byte[] buf, int offset) + throws IOException + { + for (int i = 0; i < uints.length; ++i) + { + writeUint16(uints[i], buf, offset); + offset += 2; + } + } + + public static void writeUint16ArrayWithUint16Length(int[] uints, OutputStream output) + throws IOException + { + int length = 2 * uints.length; + checkUint16(length); + writeUint16(length, output); + writeUint16Array(uints, output); + } + + public static void writeUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset) + throws IOException + { + int length = 2 * uints.length; + checkUint16(length); + writeUint16(length, buf, offset); + writeUint16Array(uints, buf, offset + 2); + } + + public static byte[] encodeOpaque8(byte[] buf) + throws IOException + { + checkUint8(buf.length); + return Arrays.prepend(buf, (byte)buf.length); + } + + public static byte[] encodeUint8ArrayWithUint8Length(short[] uints) throws IOException + { + byte[] result = new byte[1 + uints.length]; + writeUint8ArrayWithUint8Length(uints, result, 0); + return result; + } + + public static byte[] encodeUint16ArrayWithUint16Length(int[] uints) throws IOException + { + int length = 2 * uints.length; + byte[] result = new byte[2 + length]; + writeUint16ArrayWithUint16Length(uints, result, 0); + return result; + } + + public static short readUint8(InputStream input) + throws IOException + { + int i = input.read(); + if (i < 0) + { + throw new EOFException(); + } + return (short)i; + } + + public static short readUint8(byte[] buf, int offset) + { + return (short)buf[offset]; + } + + public static int readUint16(InputStream input) + throws IOException + { + int i1 = input.read(); + int i2 = input.read(); + if (i2 < 0) + { + throw new EOFException(); + } + return i1 << 8 | i2; + } + + public static int readUint16(byte[] buf, int offset) + { + int n = (buf[offset] & 0xff) << 8; + n |= (buf[++offset] & 0xff); + return n; + } + + public static int readUint24(InputStream input) + throws IOException + { + int i1 = input.read(); + int i2 = input.read(); + int i3 = input.read(); + if (i3 < 0) + { + throw new EOFException(); + } + return (i1 << 16) | (i2 << 8) | i3; + } + + public static int readUint24(byte[] buf, int offset) + { + int n = (buf[offset] & 0xff) << 16; + n |= (buf[++offset] & 0xff) << 8; + n |= (buf[++offset] & 0xff); + return n; + } + + public static long readUint32(InputStream input) + throws IOException + { + int i1 = input.read(); + int i2 = input.read(); + int i3 = input.read(); + int i4 = input.read(); + if (i4 < 0) + { + throw new EOFException(); + } + return (((long)i1) << 24) | (((long)i2) << 16) | (((long)i3) << 8) | ((long)i4); + } + + public static long readUint48(InputStream input) + throws IOException + { + int i1 = input.read(); + int i2 = input.read(); + int i3 = input.read(); + int i4 = input.read(); + int i5 = input.read(); + int i6 = input.read(); + if (i6 < 0) + { + throw new EOFException(); + } + return (((long)i1) << 40) | (((long)i2) << 32) | (((long)i3) << 24) | (((long)i4) << 16) | (((long)i5) << 8) | ((long)i6); + } + + public static long readUint48(byte[] buf, int offset) + { + int hi = readUint24(buf, offset); + int lo = readUint24(buf, offset + 3); + return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL); + } + + public static byte[] readAllOrNothing(int length, InputStream input) + throws IOException + { + if (length < 1) + { + return EMPTY_BYTES; + } + byte[] buf = new byte[length]; + int read = Streams.readFully(input, buf); + if (read == 0) + { + return null; + } + if (read != length) + { + throw new EOFException(); + } + return buf; + } + + public static byte[] readFully(int length, InputStream input) + throws IOException + { + if (length < 1) + { + return EMPTY_BYTES; + } + byte[] buf = new byte[length]; + if (length != Streams.readFully(input, buf)) + { + throw new EOFException(); + } + return buf; + } + + public static void readFully(byte[] buf, InputStream input) + throws IOException + { + int length = buf.length; + if (length > 0 && length != Streams.readFully(input, buf)) + { + throw new EOFException(); + } + } + + public static byte[] readOpaque8(InputStream input) + throws IOException + { + short length = readUint8(input); + return readFully(length, input); + } + + public static byte[] readOpaque16(InputStream input) + throws IOException + { + int length = readUint16(input); + return readFully(length, input); + } + + public static byte[] readOpaque24(InputStream input) + throws IOException + { + int length = readUint24(input); + return readFully(length, input); + } + + public static short[] readUint8Array(int count, InputStream input) + throws IOException + { + short[] uints = new short[count]; + for (int i = 0; i < count; ++i) + { + uints[i] = readUint8(input); + } + return uints; + } + + public static int[] readUint16Array(int count, InputStream input) + throws IOException + { + int[] uints = new int[count]; + for (int i = 0; i < count; ++i) + { + uints[i] = readUint16(input); + } + return uints; + } + + public static ProtocolVersion readVersion(byte[] buf, int offset) + throws IOException + { + return ProtocolVersion.get(buf[offset] & 0xFF, buf[offset + 1] & 0xFF); + } + + public static ProtocolVersion readVersion(InputStream input) + throws IOException + { + int i1 = input.read(); + int i2 = input.read(); + if (i2 < 0) + { + throw new EOFException(); + } + return ProtocolVersion.get(i1, i2); + } + + public static int readVersionRaw(byte[] buf, int offset) + throws IOException + { + return (buf[offset] << 8) | buf[offset + 1]; + } + + public static int readVersionRaw(InputStream input) + throws IOException + { + int i1 = input.read(); + int i2 = input.read(); + if (i2 < 0) + { + throw new EOFException(); + } + return (i1 << 8) | i2; + } + + public static ASN1Primitive readASN1Object(byte[] encoding) throws IOException + { + ASN1InputStream asn1 = new ASN1InputStream(encoding); + ASN1Primitive result = asn1.readObject(); + if (null == result) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + if (null != asn1.readObject()) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + return result; + } + + public static ASN1Primitive readDERObject(byte[] encoding) throws IOException + { + /* + * NOTE: The current ASN.1 parsing code can't enforce DER-only parsing, but since DER is + * canonical, we can check it by re-encoding the result and comparing to the original. + */ + ASN1Primitive result = readASN1Object(encoding); + byte[] check = result.getEncoded(ASN1Encoding.DER); + if (!Arrays.areEqual(check, encoding)) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + return result; + } + + public static void writeGMTUnixTime(byte[] buf, int offset) + { + int t = (int)(System.currentTimeMillis() / 1000L); + buf[offset] = (byte)(t >> 24); + buf[offset + 1] = (byte)(t >> 16); + buf[offset + 2] = (byte)(t >> 8); + buf[offset + 3] = (byte)t; + } + + public static void writeVersion(ProtocolVersion version, OutputStream output) + throws IOException + { + output.write(version.getMajorVersion()); + output.write(version.getMinorVersion()); + } + + public static void writeVersion(ProtocolVersion version, byte[] buf, int offset) + { + buf[offset] = (byte)version.getMajorVersion(); + buf[offset + 1] = (byte)version.getMinorVersion(); + } + + public static Vector getDefaultDSSSignatureAlgorithms() + { + return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa)); + } + + public static Vector getDefaultECDSASignatureAlgorithms() + { + return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)); + } + + public static Vector getDefaultRSASignatureAlgorithms() + { + return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa)); + } + + public static byte[] getExtensionData(Hashtable extensions, Integer extensionType) + { + return extensions == null ? null : (byte[])extensions.get(extensionType); + } + + public static boolean hasExpectedEmptyExtensionData(Hashtable extensions, Integer extensionType, + short alertDescription) throws IOException + { + byte[] extension_data = getExtensionData(extensions, extensionType); + if (extension_data == null) + { + return false; + } + if (extension_data.length != 0) + { + throw new TlsFatalAlert(alertDescription); + } + return true; + } + + public static TlsSession importSession(byte[] sessionID, SessionParameters sessionParameters) + { + return new TlsSessionImpl(sessionID, sessionParameters); + } + + public static boolean isSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion) + { + return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(clientVersion.getEquivalentTLSVersion()); + } + + /** + * Add a 'signature_algorithms' extension to existing extensions. + * + * @param extensions A {@link Hashtable} to add the extension to. + * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @throws IOException + */ + public static void addSignatureAlgorithmsExtension(Hashtable extensions, Vector supportedSignatureAlgorithms) + throws IOException + { + extensions.put(EXT_signature_algorithms, createSignatureAlgorithmsExtension(supportedSignatureAlgorithms)); + } + + /** + * Get a 'signature_algorithms' extension from extensions. + * + * @param extensions A {@link Hashtable} to get the extension from, if it is present. + * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null. + * @throws IOException + */ + public static Vector getSignatureAlgorithmsExtension(Hashtable extensions) + throws IOException + { + byte[] extensionData = getExtensionData(extensions, EXT_signature_algorithms); + return extensionData == null ? null : readSignatureAlgorithmsExtension(extensionData); + } + + /** + * Create a 'signature_algorithms' extension value. + * + * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @return A byte array suitable for use as an extension value. + * @throws IOException + */ + public static byte[] createSignatureAlgorithmsExtension(Vector supportedSignatureAlgorithms) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + // supported_signature_algorithms + encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf); + + return buf.toByteArray(); + } + + /** + * Read 'signature_algorithms' extension data. + * + * @param extensionData The extension data. + * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @throws IOException + */ + public static Vector readSignatureAlgorithmsExtension(byte[] extensionData) + throws IOException + { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); + + // supported_signature_algorithms + Vector supported_signature_algorithms = parseSupportedSignatureAlgorithms(false, buf); + + TlsProtocol.assertEmpty(buf); + + return supported_signature_algorithms; + } + + public static void encodeSupportedSignatureAlgorithms(Vector supportedSignatureAlgorithms, boolean allowAnonymous, + OutputStream output) throws IOException + { + if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.size() < 1 + || supportedSignatureAlgorithms.size() >= (1 << 15)) + { + throw new IllegalArgumentException( + "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)"); + } + + // supported_signature_algorithms + int length = 2 * supportedSignatureAlgorithms.size(); + TlsUtils.checkUint16(length); + TlsUtils.writeUint16(length, output); + for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i) + { + SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i); + if (!allowAnonymous && entry.getSignature() == SignatureAlgorithm.anonymous) + { + /* + * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used + * in Section 7.4.3. It MUST NOT appear in this extension. + */ + throw new IllegalArgumentException( + "SignatureAlgorithm.anonymous MUST NOT appear in the signature_algorithms extension"); + } + entry.encode(output); + } + } + + public static Vector parseSupportedSignatureAlgorithms(boolean allowAnonymous, InputStream input) + throws IOException + { + // supported_signature_algorithms + int length = TlsUtils.readUint16(input); + if (length < 2 || (length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + int count = length / 2; + Vector supportedSignatureAlgorithms = new Vector(count); + for (int i = 0; i < count; ++i) + { + SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(input); + if (!allowAnonymous && entry.getSignature() == SignatureAlgorithm.anonymous) + { + /* + * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used + * in Section 7.4.3. It MUST NOT appear in this extension. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + supportedSignatureAlgorithms.addElement(entry); + } + return supportedSignatureAlgorithms; + } + + public static byte[] PRF(TlsContext context, byte[] secret, String asciiLabel, byte[] seed, int size) + { + ProtocolVersion version = context.getServerVersion(); + + if (version.isSSL()) + { + throw new IllegalStateException("No PRF available for SSLv3 session"); + } + + byte[] label = Strings.toByteArray(asciiLabel); + byte[] labelSeed = concat(label, seed); + + int prfAlgorithm = context.getSecurityParameters().getPrfAlgorithm(); + + if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy) + { + return PRF_legacy(secret, label, labelSeed, size); + } + + Digest prfDigest = createPRFHash(prfAlgorithm); + byte[] buf = new byte[size]; + hmac_hash(prfDigest, secret, labelSeed, buf); + return buf; + } + + static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size) + { + int s_half = (secret.length + 1) / 2; + byte[] s1 = new byte[s_half]; + byte[] s2 = new byte[s_half]; + System.arraycopy(secret, 0, s1, 0, s_half); + System.arraycopy(secret, secret.length - s_half, s2, 0, s_half); + + byte[] b1 = new byte[size]; + byte[] b2 = new byte[size]; + hmac_hash(new MD5Digest(), s1, labelSeed, b1); + hmac_hash(new SHA1Digest(), s2, labelSeed, b2); + for (int i = 0; i < size; i++) + { + b1[i] ^= b2[i]; + } + return b1; + } + + static byte[] concat(byte[] a, byte[] b) + { + byte[] c = new byte[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + static void hmac_hash(Digest digest, byte[] secret, byte[] seed, byte[] out) + { + HMac mac = new HMac(digest); + KeyParameter param = new KeyParameter(secret); + byte[] a = seed; + int size = digest.getDigestSize(); + int iterations = (out.length + size - 1) / size; + byte[] buf = new byte[mac.getMacSize()]; + byte[] buf2 = new byte[mac.getMacSize()]; + for (int i = 0; i < iterations; i++) + { + mac.init(param); + mac.update(a, 0, a.length); + mac.doFinal(buf, 0); + a = buf; + mac.init(param); + mac.update(a, 0, a.length); + mac.update(seed, 0, seed.length); + mac.doFinal(buf2, 0); + System.arraycopy(buf2, 0, out, (size * i), Math.min(size, out.length - (size * i))); + } + } + + static void validateKeyUsage(org.spongycastle.asn1.x509.Certificate c, int keyUsageBits) + throws IOException + { + Extensions exts = c.getTBSCertificate().getExtensions(); + if (exts != null) + { + KeyUsage ku = KeyUsage.fromExtensions(exts); + if (ku != null) + { + int bits = ku.getBytes()[0] & 0xff; + if ((bits & keyUsageBits) != keyUsageBits) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + } + } + } + + static byte[] calculateKeyBlock(TlsContext context, int size) + { + SecurityParameters securityParameters = context.getSecurityParameters(); + byte[] master_secret = securityParameters.getMasterSecret(); + byte[] seed = concat(securityParameters.getServerRandom(), + securityParameters.getClientRandom()); + + if (isSSL(context)) + { + return calculateKeyBlock_SSL(master_secret, seed, size); + } + + return PRF(context, master_secret, ExporterLabel.key_expansion, seed, size); + } + + static byte[] calculateKeyBlock_SSL(byte[] master_secret, byte[] random, int size) + { + Digest md5 = new MD5Digest(); + Digest sha1 = new SHA1Digest(); + int md5Size = md5.getDigestSize(); + byte[] shatmp = new byte[sha1.getDigestSize()]; + byte[] tmp = new byte[size + md5Size]; + + int i = 0, pos = 0; + while (pos < size) + { + byte[] ssl3Const = SSL3_CONST[i]; + + sha1.update(ssl3Const, 0, ssl3Const.length); + sha1.update(master_secret, 0, master_secret.length); + sha1.update(random, 0, random.length); + sha1.doFinal(shatmp, 0); + + md5.update(master_secret, 0, master_secret.length); + md5.update(shatmp, 0, shatmp.length); + md5.doFinal(tmp, pos); + + pos += md5Size; + ++i; + } + + byte rval[] = new byte[size]; + System.arraycopy(tmp, 0, rval, 0, size); + return rval; + } + + static byte[] calculateMasterSecret(TlsContext context, byte[] pre_master_secret) + { + SecurityParameters securityParameters = context.getSecurityParameters(); + byte[] seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom()); + + if (isSSL(context)) + { + return calculateMasterSecret_SSL(pre_master_secret, seed); + } + + return PRF(context, pre_master_secret, ExporterLabel.master_secret, seed, 48); + } + + static byte[] calculateMasterSecret_SSL(byte[] pre_master_secret, byte[] random) + { + Digest md5 = new MD5Digest(); + Digest sha1 = new SHA1Digest(); + int md5Size = md5.getDigestSize(); + byte[] shatmp = new byte[sha1.getDigestSize()]; + + byte[] rval = new byte[md5Size * 3]; + int pos = 0; + + for (int i = 0; i < 3; ++i) + { + byte[] ssl3Const = SSL3_CONST[i]; + + sha1.update(ssl3Const, 0, ssl3Const.length); + sha1.update(pre_master_secret, 0, pre_master_secret.length); + sha1.update(random, 0, random.length); + sha1.doFinal(shatmp, 0); + + md5.update(pre_master_secret, 0, pre_master_secret.length); + md5.update(shatmp, 0, shatmp.length); + md5.doFinal(rval, pos); + + pos += md5Size; + } + + return rval; + } + + static byte[] calculateVerifyData(TlsContext context, String asciiLabel, byte[] handshakeHash) + { + if (isSSL(context)) + { + return handshakeHash; + } + + SecurityParameters securityParameters = context.getSecurityParameters(); + byte[] master_secret = securityParameters.getMasterSecret(); + int verify_data_length = securityParameters.getVerifyDataLength(); + + return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length); + } + + public static final Digest createHash(short hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return new MD5Digest(); + case HashAlgorithm.sha1: + return new SHA1Digest(); + case HashAlgorithm.sha224: + return new SHA224Digest(); + case HashAlgorithm.sha256: + return new SHA256Digest(); + case HashAlgorithm.sha384: + return new SHA384Digest(); + case HashAlgorithm.sha512: + return new SHA512Digest(); + default: + throw new IllegalArgumentException("unknown HashAlgorithm"); + } + } + + public static final Digest cloneHash(short hashAlgorithm, Digest hash) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return new MD5Digest((MD5Digest)hash); + case HashAlgorithm.sha1: + return new SHA1Digest((SHA1Digest)hash); + case HashAlgorithm.sha224: + return new SHA224Digest((SHA224Digest)hash); + case HashAlgorithm.sha256: + return new SHA256Digest((SHA256Digest)hash); + case HashAlgorithm.sha384: + return new SHA384Digest((SHA384Digest)hash); + case HashAlgorithm.sha512: + return new SHA512Digest((SHA512Digest)hash); + default: + throw new IllegalArgumentException("unknown HashAlgorithm"); + } + } + + public static final Digest createPRFHash(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PRFAlgorithm.tls_prf_legacy: + return new CombinedHash(); + default: + return createHash(getHashAlgorithmForPRFAlgorithm(prfAlgorithm)); + } + } + + public static final Digest clonePRFHash(int prfAlgorithm, Digest hash) + { + switch (prfAlgorithm) + { + case PRFAlgorithm.tls_prf_legacy: + return new CombinedHash((CombinedHash)hash); + default: + return cloneHash(getHashAlgorithmForPRFAlgorithm(prfAlgorithm), hash); + } + } + + public static final short getHashAlgorithmForPRFAlgorithm(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PRFAlgorithm.tls_prf_legacy: + throw new IllegalArgumentException("legacy PRF not a valid algorithm"); + case PRFAlgorithm.tls_prf_sha256: + return HashAlgorithm.sha256; + case PRFAlgorithm.tls_prf_sha384: + return HashAlgorithm.sha384; + default: + throw new IllegalArgumentException("unknown PRFAlgorithm"); + } + } + + public static ASN1ObjectIdentifier getOIDForHashAlgorithm(short hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return PKCSObjectIdentifiers.md5; + case HashAlgorithm.sha1: + return X509ObjectIdentifiers.id_SHA1; + case HashAlgorithm.sha224: + return NISTObjectIdentifiers.id_sha224; + case HashAlgorithm.sha256: + return NISTObjectIdentifiers.id_sha256; + case HashAlgorithm.sha384: + return NISTObjectIdentifiers.id_sha384; + case HashAlgorithm.sha512: + return NISTObjectIdentifiers.id_sha512; + default: + throw new IllegalArgumentException("unknown HashAlgorithm"); + } + } + + static short getClientCertificateType(Certificate clientCertificate, Certificate serverCertificate) + throws IOException + { + if (clientCertificate.isEmpty()) + { + return -1; + } + + org.spongycastle.asn1.x509.Certificate x509Cert = clientCertificate.getCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); + try + { + AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); + if (publicKey.isPrivate()) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + /* + * TODO RFC 5246 7.4.6. The certificates MUST be signed using an acceptable hash/ + * signature algorithm pair, as described in Section 7.4.4. Note that this relaxes the + * constraints on certificate-signing algorithms found in prior versions of TLS. + */ + + /* + * RFC 5246 7.4.6. Client Certificate + */ + + /* + * RSA public key; the certificate MUST allow the key to be used for signing with the + * signature scheme and hash algorithm that will be employed in the certificate verify + * message. + */ + if (publicKey instanceof RSAKeyParameters) + { + validateKeyUsage(x509Cert, KeyUsage.digitalSignature); + return ClientCertificateType.rsa_sign; + } + + /* + * DSA public key; the certificate MUST allow the key to be used for signing with the + * hash algorithm that will be employed in the certificate verify message. + */ + if (publicKey instanceof DSAPublicKeyParameters) + { + validateKeyUsage(x509Cert, KeyUsage.digitalSignature); + return ClientCertificateType.dss_sign; + } + + /* + * ECDSA-capable public key; the certificate MUST allow the key to be used for signing + * with the hash algorithm that will be employed in the certificate verify message; the + * public key MUST use a curve and point format supported by the server. + */ + if (publicKey instanceof ECPublicKeyParameters) + { + validateKeyUsage(x509Cert, KeyUsage.digitalSignature); + // TODO Check the curve and point format + return ClientCertificateType.ecdsa_sign; + } + + // TODO Add support for ClientCertificateType.*_fixed_* + + } + catch (Exception e) + { + } + + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + static void trackHashAlgorithms(TlsHandshakeHash handshakeHash, Vector supportedSignatureAlgorithms) + { + if (supportedSignatureAlgorithms != null) + { + for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i) + { + SignatureAndHashAlgorithm signatureAndHashAlgorithm = (SignatureAndHashAlgorithm) + supportedSignatureAlgorithms.elementAt(i); + short hashAlgorithm = signatureAndHashAlgorithm.getHash(); + handshakeHash.trackHashAlgorithm(hashAlgorithm); + } + } + } + + public static boolean hasSigningCapability(short clientCertificateType) + { + switch (clientCertificateType) + { + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + case ClientCertificateType.rsa_sign: + return true; + default: + return false; + } + } + + public static TlsSigner createTlsSigner(short clientCertificateType) + { + switch (clientCertificateType) + { + case ClientCertificateType.dss_sign: + return new TlsDSSSigner(); + case ClientCertificateType.ecdsa_sign: + return new TlsECDSASigner(); + case ClientCertificateType.rsa_sign: + return new TlsRSASigner(); + default: + throw new IllegalArgumentException("'clientCertificateType' is not a type with signing capability"); + } + } + + static final byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54}; + static final byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52}; + + // SSL3 magic mix constants ("A", "BB", "CCC", ...) + static final byte[][] SSL3_CONST = genConst(); + + private static byte[][] genConst() + { + int n = 10; + byte[][] arr = new byte[n][]; + for (int i = 0; i < n; i++) + { + byte[] b = new byte[i + 1]; + Arrays.fill(b, (byte)('A' + i)); + arr[i] = b; + } + return arr; + } + + private static Vector vectorOfOne(Object obj) + { + Vector v = new Vector(1); + v.addElement(obj); + return v; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UDPTransport.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UDPTransport.java new file mode 100644 index 000000000..63aca8520 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UDPTransport.java @@ -0,0 +1,75 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; + +public class UDPTransport + implements DatagramTransport +{ + protected final static int MIN_IP_OVERHEAD = 20; + protected final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64; + protected final static int UDP_OVERHEAD = 8; + + protected final DatagramSocket socket; + protected final int receiveLimit, sendLimit; + + public UDPTransport(DatagramSocket socket, int mtu) + throws IOException + { + if (!socket.isBound() || !socket.isConnected()) + { + throw new IllegalArgumentException("'socket' must be bound and connected"); + } + + this.socket = socket; + + // NOTE: As of JDK 1.6, can use NetworkInterface.getMTU + + this.receiveLimit = mtu - MIN_IP_OVERHEAD - UDP_OVERHEAD; + this.sendLimit = mtu - MAX_IP_OVERHEAD - UDP_OVERHEAD; + } + + public int getReceiveLimit() + { + return receiveLimit; + } + + public int getSendLimit() + { + // TODO[DTLS] Implement Path-MTU discovery? + return sendLimit; + } + + public int receive(byte[] buf, int off, int len, int waitMillis) + throws IOException + { + socket.setSoTimeout(waitMillis); + DatagramPacket packet = new DatagramPacket(buf, off, len); + socket.receive(packet); + return packet.getLength(); + } + + public void send(byte[] buf, int off, int len) + throws IOException + { + if (len > getSendLimit()) + { + /* + * RFC 4347 4.1.1. "If the application attempts to send a record larger than the MTU, + * the DTLS implementation SHOULD generate an error, thus avoiding sending a packet + * which will be fragmented." + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + DatagramPacket packet = new DatagramPacket(buf, off, len); + socket.send(packet); + } + + public void close() + throws IOException + { + socket.close(); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/URLAndHash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/URLAndHash.java new file mode 100644 index 000000000..05407842e --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/URLAndHash.java @@ -0,0 +1,104 @@ +package org.spongycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.util.Strings; + +/** + * RFC 6066 5. + */ +public class URLAndHash +{ + protected String url; + protected byte[] sha1Hash; + + public URLAndHash(String url, byte[] sha1Hash) + { + if (url == null || url.length() < 1 || url.length() >= (1 << 16)) + { + throw new IllegalArgumentException("'url' must have length from 1 to (2^16 - 1)"); + } + if (sha1Hash != null && sha1Hash.length != 20) + { + throw new IllegalArgumentException("'sha1Hash' must have length == 20, if present"); + } + + this.url = url; + this.sha1Hash = sha1Hash; + } + + public String getURL() + { + return url; + } + + public byte[] getSHA1Hash() + { + return sha1Hash; + } + + /** + * Encode this {@link URLAndHash} to an {@link OutputStream}. + * + * @param output the {@link OutputStream} to encode to. + * @throws IOException + */ + public void encode(OutputStream output) + throws IOException + { + byte[] urlEncoding = Strings.toByteArray(this.url); + TlsUtils.writeOpaque16(urlEncoding, output); + + if (this.sha1Hash == null) + { + TlsUtils.writeUint8(0, output); + } + else + { + TlsUtils.writeUint8(1, output); + output.write(this.sha1Hash); + } + } + + /** + * Parse a {@link URLAndHash} from an {@link InputStream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link InputStream} to parse from. + * @return a {@link URLAndHash} object. + * @throws IOException + */ + public static URLAndHash parse(TlsContext context, InputStream input) + throws IOException + { + byte[] urlEncoding = TlsUtils.readOpaque16(input); + if (urlEncoding.length < 1) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + String url = Strings.fromByteArray(urlEncoding); + + byte[] sha1Hash = null; + short padding = TlsUtils.readUint8(input); + switch (padding) + { + case 0: + if (TlsUtils.isTLSv12(context)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + break; + case 1: + sha1Hash = TlsUtils.readFully(20, input); + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return new URLAndHash(url, sha1Hash); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UseSRTPData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UseSRTPData.java new file mode 100644 index 000000000..4c1c33e9c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UseSRTPData.java @@ -0,0 +1,54 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 5764 4.1.1 + */ +public class UseSRTPData +{ + + private int[] protectionProfiles; + private byte[] mki; + + /** + * @param protectionProfiles see {@link SRTPProtectionProfile} for valid constants. + * @param mki valid lengths from 0 to 255. + */ + public UseSRTPData(int[] protectionProfiles, byte[] mki) + { + + if (protectionProfiles == null || protectionProfiles.length < 1 + || protectionProfiles.length >= (1 << 15)) + { + throw new IllegalArgumentException( + "'protectionProfiles' must have length from 1 to (2^15 - 1)"); + } + + if (mki == null) + { + mki = TlsUtils.EMPTY_BYTES; + } + else if (mki.length > 255) + { + throw new IllegalArgumentException("'mki' cannot be longer than 255 bytes"); + } + + this.protectionProfiles = protectionProfiles; + this.mki = mki; + } + + /** + * @return see {@link SRTPProtectionProfile} for valid constants. + */ + public int[] getProtectionProfiles() + { + return protectionProfiles; + } + + /** + * @return valid lengths from 0 to 255. + */ + public byte[] getMki() + { + return mki; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UserMappingType.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UserMappingType.java new file mode 100644 index 000000000..f2fc21f20 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/UserMappingType.java @@ -0,0 +1,12 @@ +package org.spongycastle.crypto.tls; + +/** + * RFC 4681 + */ +public class UserMappingType +{ + /* + * RFC 4681 + */ + public static final short upn_domain_hint = 64; +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/Pack.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/Pack.java new file mode 100644 index 000000000..7b12eb4a5 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/Pack.java @@ -0,0 +1,192 @@ +package org.spongycastle.crypto.util; + +public abstract class Pack +{ + public static int bigEndianToInt(byte[] bs, int off) + { + int n = bs[ off] << 24; + n |= (bs[++off] & 0xff) << 16; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return n; + } + + public static void bigEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToInt(bs, off); + off += 4; + } + } + + public static byte[] intToBigEndian(int n) + { + byte[] bs = new byte[4]; + intToBigEndian(n, bs, 0); + return bs; + } + + public static void intToBigEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n >>> 24); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n ); + } + + public static byte[] intToBigEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToBigEndian(ns, bs, 0); + return bs; + } + + public static void intToBigEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToBigEndian(ns[i], bs, off); + off += 4; + } + } + + public static long bigEndianToLong(byte[] bs, int off) + { + int hi = bigEndianToInt(bs, off); + int lo = bigEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void bigEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToLong(bs, off); + off += 8; + } + } + + public static byte[] longToBigEndian(long n) + { + byte[] bs = new byte[8]; + longToBigEndian(n, bs, 0); + return bs; + } + + public static void longToBigEndian(long n, byte[] bs, int off) + { + intToBigEndian((int)(n >>> 32), bs, off); + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4); + } + + public static byte[] longToBigEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToBigEndian(ns, bs, 0); + return bs; + } + + public static void longToBigEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToBigEndian(ns[i], bs, off); + off += 8; + } + } + + public static int littleEndianToInt(byte[] bs, int off) + { + int n = bs[ off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff) << 16; + n |= bs[++off] << 24; + return n; + } + + public static void littleEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToInt(bs, off); + off += 4; + } + } + + public static byte[] intToLittleEndian(int n) + { + byte[] bs = new byte[4]; + intToLittleEndian(n, bs, 0); + return bs; + } + + public static void intToLittleEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 24); + } + + public static byte[] intToLittleEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToLittleEndian(ns, bs, 0); + return bs; + } + + public static void intToLittleEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToLittleEndian(ns[i], bs, off); + off += 4; + } + } + + public static long littleEndianToLong(byte[] bs, int off) + { + int lo = littleEndianToInt(bs, off); + int hi = littleEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void littleEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToLong(bs, off); + off += 8; + } + } + + public static byte[] longToLittleEndian(long n) + { + byte[] bs = new byte[8]; + longToLittleEndian(n, bs, 0); + return bs; + } + + public static void longToLittleEndian(long n, byte[] bs, int off) + { + intToLittleEndian((int)(n & 0xffffffffL), bs, off); + intToLittleEndian((int)(n >>> 32), bs, off + 4); + } + + public static byte[] longToLittleEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToLittleEndian(ns, bs, 0); + return bs; + } + + public static void longToLittleEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToLittleEndian(ns[i], bs, off); + off += 8; + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PrivateKeyFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PrivateKeyFactory.java new file mode 100644 index 000000000..750c6ac55 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PrivateKeyFactory.java @@ -0,0 +1,152 @@ +package org.spongycastle.crypto.util; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.oiw.ElGamalParameter; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.DHParameter; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.pkcs.RSAPrivateKey; +import org.spongycastle.asn1.sec.ECPrivateKey; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; + +/** + * Factory for creating private key objects from PKCS8 PrivateKeyInfo objects. + */ +public class PrivateKeyFactory +{ + /** + * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding. + * + * @param privateKeyInfoData the PrivateKeyInfo encoding + * @return a suitable private key parameter + * @throws IOException on an error decoding the key + */ + public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) throws IOException + { + return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData))); + } + + /** + * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding read from a + * stream. + * + * @param inStr the stream to read the PrivateKeyInfo encoding from + * @return a suitable private key parameter + * @throws IOException on an error decoding the key + */ + public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException + { + return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); + } + + /** + * Create a private key parameter from the passed in PKCS8 PrivateKeyInfo object. + * + * @param keyInfo the PrivateKeyInfo object containing the key material + * @return a suitable private key parameter + * @throws IOException on an error decoding the key + */ + public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) throws IOException + { + AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm(); + + if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption)) + { + RSAPrivateKey keyStructure = RSAPrivateKey.getInstance(keyInfo.parsePrivateKey()); + + return new RSAPrivateCrtKeyParameters(keyStructure.getModulus(), + keyStructure.getPublicExponent(), keyStructure.getPrivateExponent(), + keyStructure.getPrime1(), keyStructure.getPrime2(), keyStructure.getExponent1(), + keyStructure.getExponent2(), keyStructure.getCoefficient()); + } + // TODO? +// else if (algId.getObjectId().equals(X9ObjectIdentifiers.dhpublicnumber)) + else if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + DHParameter params = DHParameter.getInstance(algId.getParameters()); + ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey(); + + BigInteger lVal = params.getL(); + int l = lVal == null ? 0 : lVal.intValue(); + DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l); + + return new DHPrivateKeyParameters(derX.getValue(), dhParams); + } + else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm)) + { + ElGamalParameter params = new ElGamalParameter((ASN1Sequence)algId.getParameters()); + ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey(); + + return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters( + params.getP(), params.getG())); + } + else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_dsa)) + { + ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey(); + ASN1Encodable de = algId.getParameters(); + + DSAParameters parameters = null; + if (de != null) + { + DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive()); + parameters = new DSAParameters(params.getP(), params.getQ(), params.getG()); + } + + return new DSAPrivateKeyParameters(derX.getValue(), parameters); + } + else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + X962Parameters params = new X962Parameters((ASN1Primitive)algId.getParameters()); + + X9ECParameters x9; + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + x9 = ECNamedCurveTable.getByOID(oid); + } + else + { + x9 = X9ECParameters.getInstance(params.getParameters()); + } + + ECPrivateKey ec = ECPrivateKey.getInstance(keyInfo.parsePrivateKey()); + BigInteger d = ec.getKey(); + + // TODO We lose any named parameters here + + ECDomainParameters dParams = new ECDomainParameters( + x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed()); + + return new ECPrivateKeyParameters(d, dParams); + } + else + { + throw new RuntimeException("algorithm identifier in key not recognised"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PrivateKeyInfoFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PrivateKeyInfoFactory.java new file mode 100644 index 000000000..49cdcaa81 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PrivateKeyInfoFactory.java @@ -0,0 +1,82 @@ +package org.spongycastle.crypto.util; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.pkcs.RSAPrivateKey; +import org.spongycastle.asn1.sec.ECPrivateKey; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; + +/** + * Factory to create ASN.1 private key info objects from lightweight private keys. + */ +public class PrivateKeyInfoFactory +{ + /** + * Create a PrivateKeyInfo representation of a private key. + * + * @param privateKey the SubjectPublicKeyInfo encoding + * @return the appropriate key parameter + * @throws java.io.IOException on an error encoding the key + */ + public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) throws IOException + { + if (privateKey instanceof RSAKeyParameters) + { + RSAPrivateCrtKeyParameters priv = (RSAPrivateCrtKeyParameters)privateKey; + + return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKey(priv.getModulus(), priv.getPublicExponent(), priv.getExponent(), priv.getP(), priv.getQ(), priv.getDP(), priv.getDQ(), priv.getQInv())); + } + else if (privateKey instanceof DSAPrivateKeyParameters) + { + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)privateKey; + DSAParameters params = priv.getParameters(); + + return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX())); + } + else if (privateKey instanceof ECPrivateKeyParameters) + { + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey; + ECDomainParameters domainParams = priv.getParameters(); + ASN1Encodable params; + + // TODO: need to handle named curves + if (domainParams == null) + { + params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA + } + else + { + X9ECParameters ecP = new X9ECParameters( + domainParams.getCurve(), + domainParams.getG(), + domainParams.getN(), + domainParams.getH(), + domainParams.getSeed()); + + params = new X962Parameters(ecP); + } + + return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(priv.getD(), params)); + } + else + { + throw new IOException("key parameters not recognised."); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PublicKeyFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PublicKeyFactory.java new file mode 100644 index 000000000..aaa4e412d --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/PublicKeyFactory.java @@ -0,0 +1,188 @@ +package org.spongycastle.crypto.util; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.oiw.ElGamalParameter; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.DHParameter; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSAPublicKey; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.asn1.x9.DHDomainParameters; +import org.spongycastle.asn1.x9.DHPublicKey; +import org.spongycastle.asn1.x9.DHValidationParms; +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.crypto.params.DHValidationParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; + +/** + * Factory to create asymmetric public key parameters for asymmetric ciphers from range of + * ASN.1 encoded SubjectPublicKeyInfo objects. + */ +public class PublicKeyFactory +{ + /** + * Create a public key from a SubjectPublicKeyInfo encoding + * + * @param keyInfoData the SubjectPublicKeyInfo encoding + * @return the appropriate key parameter + * @throws IOException on an error decoding the key + */ + public static AsymmetricKeyParameter createKey(byte[] keyInfoData) throws IOException + { + return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData))); + } + + /** + * Create a public key from a SubjectPublicKeyInfo encoding read from a stream + * + * @param inStr the stream to read the SubjectPublicKeyInfo encoding from + * @return the appropriate key parameter + * @throws IOException on an error decoding the key + */ + public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException + { + return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); + } + + /** + * Create a public key from the passed in SubjectPublicKeyInfo + * + * @param keyInfo the SubjectPublicKeyInfo containing the key data + * @return the appropriate key parameter + * @throws IOException on an error decoding the key + */ + public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) throws IOException + { + AlgorithmIdentifier algId = keyInfo.getAlgorithm(); + + if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption) + || algId.getAlgorithm().equals(X509ObjectIdentifiers.id_ea_rsa)) + { + RSAPublicKey pubKey = RSAPublicKey.getInstance(keyInfo.parsePublicKey()); + + return new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent()); + } + else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.dhpublicnumber)) + { + DHPublicKey dhPublicKey = DHPublicKey.getInstance(keyInfo.parsePublicKey()); + + BigInteger y = dhPublicKey.getY().getValue(); + + DHDomainParameters dhParams = DHDomainParameters.getInstance(algId.getParameters()); + + BigInteger p = dhParams.getP().getValue(); + BigInteger g = dhParams.getG().getValue(); + BigInteger q = dhParams.getQ().getValue(); + + BigInteger j = null; + if (dhParams.getJ() != null) + { + j = dhParams.getJ().getValue(); + } + + DHValidationParameters validation = null; + DHValidationParms dhValidationParms = dhParams.getValidationParms(); + if (dhValidationParms != null) + { + byte[] seed = dhValidationParms.getSeed().getBytes(); + BigInteger pgenCounter = dhValidationParms.getPgenCounter().getValue(); + + // TODO Check pgenCounter size? + + validation = new DHValidationParameters(seed, pgenCounter.intValue()); + } + + return new DHPublicKeyParameters(y, new DHParameters(p, g, q, j, validation)); + } + else if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + DHParameter params = DHParameter.getInstance(algId.getParameters()); + ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey(); + + BigInteger lVal = params.getL(); + int l = lVal == null ? 0 : lVal.intValue(); + DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l); + + return new DHPublicKeyParameters(derY.getValue(), dhParams); + } + else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm)) + { + ElGamalParameter params = new ElGamalParameter((ASN1Sequence)algId.getParameters()); + ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey(); + + return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters( + params.getP(), params.getG())); + } + else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_dsa) + || algId.getAlgorithm().equals(OIWObjectIdentifiers.dsaWithSHA1)) + { + ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey(); + ASN1Encodable de = algId.getParameters(); + + DSAParameters parameters = null; + if (de != null) + { + DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive()); + parameters = new DSAParameters(params.getP(), params.getQ(), params.getG()); + } + + return new DSAPublicKeyParameters(derY.getValue(), parameters); + } + else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + X962Parameters params = X962Parameters.getInstance(algId.getParameters()); + + X9ECParameters x9; + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); + x9 = ECNamedCurveTable.getByOID(oid); + } + else + { + x9 = X9ECParameters.getInstance(params.getParameters()); + } + + ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes()); + X9ECPoint derQ = new X9ECPoint(x9.getCurve(), key); + + // TODO We lose any named parameters here + + ECDomainParameters dParams = new ECDomainParameters( + x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed()); + + return new ECPublicKeyParameters(derQ.getPoint(), dParams); + } + else + { + throw new RuntimeException("algorithm identifier in key not recognised"); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/SubjectPublicKeyInfoFactory.java new file mode 100644 index 000000000..131ba445f --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/util/SubjectPublicKeyInfoFactory.java @@ -0,0 +1,81 @@ +package org.spongycastle.crypto.util; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSAPublicKey; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; + +/** + * Factory to create ASN.1 subject public key info objects from lightweight public keys. + */ +public class SubjectPublicKeyInfoFactory +{ + /** + * Create a SubjectPublicKeyInfo public key. + * + * @param publicKey the SubjectPublicKeyInfo encoding + * @return the appropriate key parameter + * @throws java.io.IOException on an error encoding the key + */ + public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) throws IOException + { + if (publicKey instanceof RSAKeyParameters) + { + RSAKeyParameters pub = (RSAKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKey(pub.getModulus(), pub.getExponent())); + } + else if (publicKey instanceof DSAPublicKeyParameters) + { + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa), new ASN1Integer(pub.getY())); + } + else if (publicKey instanceof ECPublicKeyParameters) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters)publicKey; + ECDomainParameters domainParams = pub.getParameters(); + ASN1Encodable params; + + // TODO: need to handle named curves + if (domainParams == null) + { + params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA + } + else + { + X9ECParameters ecP = new X9ECParameters( + domainParams.getCurve(), + domainParams.getG(), + domainParams.getN(), + domainParams.getH(), + domainParams.getSeed()); + + params = new X962Parameters(ecP); + } + + ASN1OctetString p = (ASN1OctetString)new X9ECPoint(pub.getQ()).toASN1Primitive(); + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); + } + else + { + throw new IOException("key parameters not recognised."); + } + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/ErrorBundle.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/ErrorBundle.java new file mode 100644 index 000000000..fc703e019 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/ErrorBundle.java @@ -0,0 +1,120 @@ +package org.spongycastle.i18n; + +import java.io.UnsupportedEncodingException; +import java.util.Locale; +import java.util.TimeZone; + +public class ErrorBundle extends MessageBundle +{ + + /** + * summary entry key + */ + public static final String SUMMARY_ENTRY = "summary"; + + /** + * detail entry key + */ + public static final String DETAIL_ENTRY = "details"; + + /** + * Constructs a new ErrorBundle using
resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public ErrorBundle(String resource, String id) throws NullPointerException
+ {
+ super(resource, id);
+ }
+
+ /**
+ * Constructs a new ErrorBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public ErrorBundle(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ /**
+ * Constructs a new ErrorBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public ErrorBundle(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ super(resource, id, arguments);
+ }
+
+ /**
+ * Constructs a new ErrorBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public ErrorBundle(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ /**
+ * Returns the summary message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the summary message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getSummary(Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(SUMMARY_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the summary message in the given locale and the default timezone.
+ * @param loc the {@link Locale}
+ * @return the summary message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getSummary(Locale loc) throws MissingEntryException
+ {
+ return getEntry(SUMMARY_ENTRY,loc,TimeZone.getDefault());
+ }
+
+ /**
+ * Returns the detail message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the detail message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getDetail(Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(DETAIL_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the detail message in the given locale and the default timezone.
+ * @param loc the {@link Locale}
+ * @return the detail message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getDetail(Locale loc) throws MissingEntryException
+ {
+ return getEntry(DETAIL_ENTRY,loc,TimeZone.getDefault());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocaleString.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocaleString.java
new file mode 100644
index 000000000..26a85b8ab
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocaleString.java
@@ -0,0 +1,30 @@
+package org.spongycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+public class LocaleString extends LocalizedMessage
+{
+
+ public LocaleString(String resource, String id)
+ {
+ super(resource, id);
+ }
+
+ public LocaleString(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ public LocaleString(String resource, String id, String encoding, Object[] arguments)
+ throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ public String getLocaleString(Locale locale)
+ {
+ return this.getEntry(null, locale, null);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocalizedException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocalizedException.java
new file mode 100644
index 000000000..4b07c2cb5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocalizedException.java
@@ -0,0 +1,49 @@
+package org.spongycastle.i18n;
+
+import java.util.Locale;
+
+/**
+ * Base class for all Exceptions with localized messages.
+ */
+public class LocalizedException extends Exception
+{
+
+ protected ErrorBundle message;
+ private Throwable cause;
+
+ /**
+ * Constructs a new LocalizedException with the specified localized message.
+ * @param message the {@link ErrorBundle} that contains the message for the exception
+ */
+ public LocalizedException(ErrorBundle message)
+ {
+ super(message.getText(Locale.getDefault()));
+ this.message = message;
+ }
+
+ /**
+ * Constructs a new LocalizedException with the specified localized message and cause.
+ * @param message the {@link ErrorBundle} that contains the message for the exception
+ * @param throwable the cause
+ */
+ public LocalizedException(ErrorBundle message, Throwable throwable)
+ {
+ super(message.getText(Locale.getDefault()));
+ this.message = message;
+ this.cause = throwable;
+ }
+
+ /**
+ * Returns the localized error message of the exception.
+ * @return the localized error message as {@link ErrorBundle}
+ */
+ public ErrorBundle getErrorMessage()
+ {
+ return message;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocalizedMessage.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocalizedMessage.java
new file mode 100644
index 000000000..e4e403282
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/LocalizedMessage.java
@@ -0,0 +1,476 @@
+package org.spongycastle.i18n;
+
+import org.spongycastle.i18n.filter.Filter;
+import org.spongycastle.i18n.filter.TrustedInput;
+import org.spongycastle.i18n.filter.UntrustedInput;
+import org.spongycastle.i18n.filter.UntrustedUrlInput;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+public class LocalizedMessage
+{
+
+ protected final String id;
+ protected final String resource;
+
+ // ISO-8859-1 is the default encoding
+ public static final String DEFAULT_ENCODING = "ISO-8859-1";
+ protected String encoding = DEFAULT_ENCODING;
+
+ protected FilteredArguments arguments;
+ protected FilteredArguments extraArgs = null;
+
+ protected Filter filter = null;
+
+ protected ClassLoader loader = null;
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource,String id) throws NullPointerException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource,String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ if (!Charset.isSupported(encoding))
+ {
+ throw new UnsupportedEncodingException("The encoding \"" + encoding + "\" is not supported.");
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ if (!Charset.isSupported(encoding))
+ {
+ throw new UnsupportedEncodingException("The encoding \"" + encoding + "\" is not supported.");
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * Reads the entry id + "." + key
from the resource file and returns a
+ * formated message for the given Locale and TimeZone.
+ * @param key second part of the entry id
+ * @param loc the used {@link Locale}
+ * @param timezone the used {@link TimeZone}
+ * @return a Strng containing the localized message
+ * @throws MissingEntryException if the resource file is not available or the entry does not exist.
+ */
+ public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ String entry = id;
+ if (key != null)
+ {
+ entry += "." + key;
+ }
+
+ try
+ {
+ ResourceBundle bundle;
+ if (loader == null)
+ {
+ bundle = ResourceBundle.getBundle(resource,loc);
+ }
+ else
+ {
+ bundle = ResourceBundle.getBundle(resource, loc, loader);
+ }
+ String result = bundle.getString(entry);
+ if (!encoding.equals(DEFAULT_ENCODING))
+ {
+ result = new String(result.getBytes(DEFAULT_ENCODING), encoding);
+ }
+ if (!arguments.isEmpty())
+ {
+ result = formatWithTimeZone(result,arguments.getFilteredArgs(loc),loc,timezone);
+ }
+ result = addExtraArgs(result, loc);
+ return result;
+ }
+ catch (MissingResourceException mre)
+ {
+ throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
+ resource,
+ entry,
+ loc,
+ loader != null ? loader : this.getClassLoader());
+ }
+ catch (UnsupportedEncodingException use)
+ {
+ // should never occur - cause we already test this in the constructor
+ throw new RuntimeException(use);
+ }
+ }
+
+ protected String formatWithTimeZone(
+ String template,
+ Object[] arguments,
+ Locale locale,
+ TimeZone timezone)
+ {
+ MessageFormat mf = new MessageFormat(" ");
+ mf.setLocale(locale);
+ mf.applyPattern(template);
+ if (!timezone.equals(TimeZone.getDefault()))
+ {
+ Format[] formats = mf.getFormats();
+ for (int i = 0; i < formats.length; i++)
+ {
+ if (formats[i] instanceof DateFormat)
+ {
+ DateFormat temp = (DateFormat) formats[i];
+ temp.setTimeZone(timezone);
+ mf.setFormat(i,temp);
+ }
+ }
+ }
+ return mf.format(arguments);
+ }
+
+ protected String addExtraArgs(String msg, Locale locale)
+ {
+ if (extraArgs != null)
+ {
+ StringBuffer sb = new StringBuffer(msg);
+ Object[] filteredArgs = extraArgs.getFilteredArgs(locale);
+ for (int i = 0; i < filteredArgs.length; i++)
+ {
+ sb.append(filteredArgs[i]);
+ }
+ msg = sb.toString();
+ }
+ return msg;
+ }
+
+ /**
+ * Sets the {@link Filter} that is used to filter the arguments of this message
+ * @param filter the {@link Filter} to use. null
to disable filtering.
+ */
+ public void setFilter(Filter filter)
+ {
+ arguments.setFilter(filter);
+ if (extraArgs != null)
+ {
+ extraArgs.setFilter(filter);
+ }
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the current filter.
+ * @return the current filter
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ /**
+ * Set the {@link ClassLoader} which loads the resource files. If it is set to null
+ * then the default {@link ClassLoader} is used.
+ * @param loader the {@link ClassLoader} which loads the resource files
+ */
+ public void setClassLoader(ClassLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} which loads the resource files or null
+ * if the default ClassLoader is used.
+ * @return the {@link ClassLoader} which loads the resource files
+ */
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ /**
+ * Returns the id of the message in the resource bundle.
+ * @return the id of the message
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the name of the resource bundle for this message
+ * @return name of the resource file
+ */
+ public String getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Returns an Object[]
containing the message arguments.
+ * @return the message arguments
+ */
+ public Object[] getArguments()
+ {
+ return arguments.getArguments();
+ }
+
+ /**
+ *
+ * @param extraArg
+ */
+ public void setExtraArgument(Object extraArg)
+ {
+ setExtraArguments(new Object[] {extraArg});
+ }
+
+ /**
+ *
+ * @param extraArgs
+ */
+ public void setExtraArguments(Object[] extraArgs)
+ {
+ if (extraArgs != null)
+ {
+ this.extraArgs = new FilteredArguments(extraArgs);
+ this.extraArgs.setFilter(filter);
+ }
+ else
+ {
+ this.extraArgs = null;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Object[] getExtraArgs()
+ {
+ return (extraArgs == null) ? null : extraArgs.getArguments();
+ }
+
+ protected class FilteredArguments
+ {
+ protected static final int NO_FILTER = 0;
+ protected static final int FILTER = 1;
+ protected static final int FILTER_URL = 2;
+
+ protected Filter filter = null;
+
+ protected boolean[] isLocaleSpecific;
+ protected int[] argFilterType;
+ protected Object[] arguments;
+ protected Object[] unpackedArgs;
+ protected Object[] filteredArgs;
+
+ FilteredArguments()
+ {
+ this(new Object[0]);
+ }
+
+ FilteredArguments(Object[] args)
+ {
+ this.arguments = args;
+ this.unpackedArgs = new Object[args.length];
+ this.filteredArgs = new Object[args.length];
+ this.isLocaleSpecific = new boolean[args.length];
+ this.argFilterType = new int[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i] instanceof TrustedInput)
+ {
+ this.unpackedArgs[i] = ((TrustedInput) args[i]).getInput();
+ argFilterType[i] = NO_FILTER;
+ }
+ else if (args[i] instanceof UntrustedInput)
+ {
+ this.unpackedArgs[i] = ((UntrustedInput) args[i]).getInput();
+ if (args[i] instanceof UntrustedUrlInput)
+ {
+ argFilterType[i] = FILTER_URL;
+ }
+ else
+ {
+ argFilterType[i] = FILTER;
+ }
+ }
+ else
+ {
+ this.unpackedArgs[i] = args[i];
+ argFilterType[i] = FILTER;
+ }
+
+ // locale specific
+ this.isLocaleSpecific[i] = (this.unpackedArgs[i] instanceof LocaleString);
+ }
+ }
+
+ public boolean isEmpty()
+ {
+ return unpackedArgs.length == 0;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Object[] getFilteredArgs(Locale locale)
+ {
+ Object[] result = new Object[unpackedArgs.length];
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ Object arg;
+ if (filteredArgs[i] != null)
+ {
+ arg = filteredArgs[i];
+ }
+ else
+ {
+ arg = unpackedArgs[i];
+ if (isLocaleSpecific[i])
+ {
+ // get locale
+ arg = ((LocaleString) arg).getLocaleString(locale);
+ arg = filter(argFilterType[i], arg);
+ }
+ else
+ {
+ arg = filter(argFilterType[i], arg);
+ filteredArgs[i] = arg;
+ }
+ }
+ result[i] = arg;
+ }
+ return result;
+ }
+
+ private Object filter(int type, Object obj)
+ {
+ if (filter != null)
+ {
+ Object o = (null == obj) ? "null" : obj;
+ switch (type)
+ {
+ case NO_FILTER:
+ return o;
+ case FILTER:
+ return filter.doFilter(o.toString());
+ case FILTER_URL:
+ return filter.doFilterUrl(o.toString());
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ return obj;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(Filter filter)
+ {
+ if (filter != this.filter)
+ {
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ filteredArgs[i] = null;
+ }
+ }
+ this.filter = filter;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Resource: \"").append(resource);
+ sb.append("\" Id: \"").append(id).append("\"");
+ sb.append(" Arguments: ").append(arguments.getArguments().length).append(" normal");
+ if (extraArgs != null && extraArgs.getArguments().length > 0)
+ {
+ sb.append(", ").append(extraArgs.getArguments().length).append(" extra");
+ }
+ sb.append(" Encoding: ").append(encoding);
+ sb.append(" ClassLoader: ").append(loader);
+ return sb.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/MessageBundle.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/MessageBundle.java
new file mode 100644
index 000000000..407e9cabc
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/MessageBundle.java
@@ -0,0 +1,92 @@
+package org.spongycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class MessageBundle extends TextBundle
+{
+
+ /**
+ * title entry key
+ */
+ public static final String TITLE_ENTRY = "title";
+
+ /**
+ * Constructs a new MessageBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public MessageBundle(String resource, String id) throws NullPointerException
+ {
+ super(resource, id);
+ }
+
+ /**
+ * Constructs a new MessageBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public MessageBundle(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ /**
+ * Constructs a new MessageBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public MessageBundle(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ super(resource, id, arguments);
+ }
+
+ /**
+ * Constructs a new MessageBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public MessageBundle(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ /**
+ * Returns the title message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the title message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getTitle(Locale loc,TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(TITLE_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the title message in the given locale and the default timezone.
+ * @param loc the {@link Locale}
+ * @return the title message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getTitle(Locale loc) throws MissingEntryException
+ {
+ return getEntry(TITLE_ENTRY,loc,TimeZone.getDefault());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/MissingEntryException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/MissingEntryException.java
new file mode 100644
index 000000000..5ee4df861
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/MissingEntryException.java
@@ -0,0 +1,73 @@
+package org.spongycastle.i18n;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+
+public class MissingEntryException extends RuntimeException
+{
+
+ protected final String resource;
+ protected final String key;
+ protected final ClassLoader loader;
+ protected final Locale locale;
+
+ private String debugMsg;
+
+ public MissingEntryException(String message, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public MissingEntryException(String message, Throwable cause, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message, cause);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public String getResource()
+ {
+ return resource;
+ }
+
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ public String getDebugMsg()
+ {
+ if (debugMsg == null)
+ {
+ debugMsg = "Can not find entry " + key + " in resource file " + resource + " for the locale " + locale + ".";
+ if (loader instanceof URLClassLoader)
+ {
+ URL[] urls = ((URLClassLoader) loader).getURLs();
+ debugMsg += " The following entries in the classpath were searched: ";
+ for (int i = 0; i != urls.length; i++)
+ {
+ debugMsg += urls[i] + " ";
+ }
+ }
+ }
+ return debugMsg;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/TextBundle.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/TextBundle.java
new file mode 100644
index 000000000..6432c3b4a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/TextBundle.java
@@ -0,0 +1,92 @@
+package org.spongycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class TextBundle extends LocalizedMessage
+{
+
+ /**
+ * text entry key
+ */
+ public static final String TEXT_ENTRY = "text";
+
+ /**
+ * Constructs a new TextBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public TextBundle(String resource, String id) throws NullPointerException
+ {
+ super(resource, id);
+ }
+
+ /**
+ * Constructs a new TextBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public TextBundle(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ /**
+ * Constructs a new TextBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public TextBundle(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ super(resource, id, arguments);
+ }
+
+ /**
+ * Constructs a new TextBundle using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public TextBundle(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ /**
+ * Returns the text message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the text message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getText(Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(TEXT_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the text message in the given locale and the defaut timezone.
+ * @param loc the {@link Locale}
+ * @return the text message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getText(Locale loc) throws MissingEntryException
+ {
+ return getEntry(TEXT_ENTRY,loc,TimeZone.getDefault());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/Filter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/Filter.java
new file mode 100644
index 000000000..7483241cb
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/Filter.java
@@ -0,0 +1,21 @@
+
+package org.spongycastle.i18n.filter;
+
+public interface Filter
+{
+
+ /**
+ * Runs the filter on the input String and returns the filtered String
+ * @param input input String
+ * @return filtered String
+ */
+ public String doFilter(String input);
+
+ /**
+ * Runs the filter on the input url and returns the filtered String
+ * @param input input url String
+ * @return filtered String
+ */
+ public String doFilterUrl(String input);
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/HTMLFilter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/HTMLFilter.java
new file mode 100644
index 000000000..a6af7fe7c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/HTMLFilter.java
@@ -0,0 +1,68 @@
+
+package org.spongycastle.i18n.filter;
+
+/**
+ * HTML Filter
+ */
+public class HTMLFilter implements Filter
+{
+
+ public String doFilter(String input)
+ {
+ StringBuffer buf = new StringBuffer(input);
+ int i = 0;
+ while (i < buf.length())
+ {
+ char ch = buf.charAt(i);
+ switch (ch)
+ {
+ case '<':
+ buf.replace(i,i+1,"<");
+ break;
+ case '>':
+ buf.replace(i,i+1,">");
+ break;
+ case '(':
+ buf.replace(i,i+1,"(");
+ break;
+ case ')':
+ buf.replace(i,i+1,")");
+ break;
+ case '#':
+ buf.replace(i,i+1,"#");
+ break;
+ case '&':
+ buf.replace(i,i+1,"&");
+ break;
+ case '\"':
+ buf.replace(i,i+1,""");
+ break;
+ case '\'':
+ buf.replace(i,i+1,"'");
+ break;
+ case '%':
+ buf.replace(i,i+1,"%");
+ break;
+ case ';':
+ buf.replace(i,i+1,";");
+ break;
+ case '+':
+ buf.replace(i,i+1,"+");
+ break;
+ case '-':
+ buf.replace(i,i+1,"-");
+ break;
+ default:
+ i -= 3;
+ }
+ i += 4;
+ }
+ return buf.toString();
+ }
+
+ public String doFilterUrl(String input)
+ {
+ return doFilter(input);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/SQLFilter.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/SQLFilter.java
new file mode 100644
index 000000000..73ec5efef
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/SQLFilter.java
@@ -0,0 +1,69 @@
+
+package org.spongycastle.i18n.filter;
+
+/**
+ * Filter for strings to store in a SQL table.
+ *
+ * escapes ' " = - / \ ; \r \n
+ */
+public class SQLFilter implements Filter
+{
+
+ public String doFilter(String input)
+ {
+ StringBuffer buf = new StringBuffer(input);
+ int i = 0;
+ while (i < buf.length())
+ {
+ char ch = buf.charAt(i);
+ switch (ch)
+ {
+ case '\'':
+ buf.replace(i,i+1,"\\\'");
+ i += 1;
+ break;
+ case '\"':
+ buf.replace(i,i+1,"\\\"");
+ i += 1;
+ break;
+ case '=':
+ buf.replace(i,i+1,"\\=");
+ i += 1;
+ break;
+ case '-':
+ buf.replace(i,i+1,"\\-");
+ i += 1;
+ break;
+ case '/':
+ buf.replace(i,i+1,"\\/");
+ i += 1;
+ break;
+ case '\\':
+ buf.replace(i,i+1,"\\\\");
+ i += 1;
+ break;
+ case ';':
+ buf.replace(i,i+1,"\\;");
+ i += 1;
+ break;
+ case '\r':
+ buf.replace(i,i+1,"\\r");
+ i += 1;
+ break;
+ case '\n':
+ buf.replace(i,i+1,"\\n");
+ i += 1;
+ break;
+ default:
+ }
+ i++;
+ }
+ return buf.toString();
+ }
+
+ public String doFilterUrl(String input)
+ {
+ return doFilter(input);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/TrustedInput.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/TrustedInput.java
new file mode 100644
index 000000000..adbf0a3e1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/TrustedInput.java
@@ -0,0 +1,23 @@
+package org.spongycastle.i18n.filter;
+
+public class TrustedInput
+{
+
+ protected Object input;
+
+ public TrustedInput(Object input)
+ {
+ this.input = input;
+ }
+
+ public Object getInput()
+ {
+ return input;
+ }
+
+ public String toString()
+ {
+ return input.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/UntrustedInput.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/UntrustedInput.java
new file mode 100644
index 000000000..79754a0b5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/UntrustedInput.java
@@ -0,0 +1,44 @@
+
+package org.spongycastle.i18n.filter;
+
+/**
+ * Wrapper class to mark untrusted input.
+ */
+public class UntrustedInput
+{
+
+ protected Object input;
+
+ /**
+ * Construct a new UntrustedInput instance.
+ * @param input the untrusted input Object
+ */
+ public UntrustedInput(Object input)
+ {
+ this.input = input;
+ }
+
+ /**
+ * Returns the untrusted input as Object.
+ * @return the input
as Object
+ */
+ public Object getInput()
+ {
+ return input;
+ }
+
+ /**
+ * Returns the untrusted input convertet to a String.
+ * @return the input
as String
+ */
+ public String getString()
+ {
+ return input.toString();
+ }
+
+ public String toString()
+ {
+ return input.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/UntrustedUrlInput.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/UntrustedUrlInput.java
new file mode 100644
index 000000000..3a3cc39a8
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/i18n/filter/UntrustedUrlInput.java
@@ -0,0 +1,14 @@
+package org.spongycastle.i18n.filter;
+
+/**
+ *
+ * Wrapper class to mark an untrusted Url
+ */
+public class UntrustedUrlInput extends UntrustedInput
+{
+ public UntrustedUrlInput(Object url)
+ {
+ super(url);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java
new file mode 100644
index 000000000..93de81c9f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java
@@ -0,0 +1,20 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public abstract class AbstractECMultiplier implements ECMultiplier
+{
+ public ECPoint multiply(ECPoint p, BigInteger k)
+ {
+ int sign = k.signum();
+ if (sign == 0 || p.isInfinity())
+ {
+ return p.getCurve().getInfinity();
+ }
+
+ ECPoint positive = multiplyPositive(p, k.abs());
+ return sign > 0 ? positive : positive.negate();
+ }
+
+ protected abstract ECPoint multiplyPositive(ECPoint p, BigInteger k);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java
new file mode 100644
index 000000000..cc98a3a21
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java
@@ -0,0 +1,24 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class DoubleAddMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Joye's double-add algorithm.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint[] R = new ECPoint[]{ p.getCurve().getInfinity(), p };
+
+ int n = k.bitLength();
+ for (int i = 0; i < n; ++i)
+ {
+ int b = k.testBit(i) ? 1 : 0;
+ int bp = 1 - b;
+ R[bp] = R[bp].twicePlus(R[b]);
+ }
+
+ return R[0];
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java
new file mode 100644
index 000000000..a1e923e51
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java
@@ -0,0 +1,129 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ECAlgorithms
+{
+ public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a,
+ ECPoint Q, BigInteger b)
+ {
+ ECCurve cp = P.getCurve();
+ Q = importPoint(cp, Q);
+
+ // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
+ if (cp instanceof ECCurve.F2m)
+ {
+ ECCurve.F2m f2mCurve = (ECCurve.F2m)cp;
+ if (f2mCurve.isKoblitz())
+ {
+ return P.multiply(a).add(Q.multiply(b));
+ }
+ }
+
+ return implShamirsTrick(P, a, Q, b);
+ }
+
+ /*
+ * "Shamir's Trick", originally due to E. G. Straus
+ * (Addition chains of vectors. American Mathematical Monthly,
+ * 71(7):806-808, Aug./Sept. 1964)
+ * + * Input: The points P, Q, scalar k = (km?, ... , k1, k0) + * and scalar l = (lm?, ... , l1, l0). + * Output: R = k * P + l * Q. + * 1: Z <- P + Q + * 2: R <- O + * 3: for i from m-1 down to 0 do + * 4: R <- R + R {point doubling} + * 5: if (ki = 1) and (li = 0) then R <- R + P end if + * 6: if (ki = 0) and (li = 1) then R <- R + Q end if + * 7: if (ki = 1) and (li = 1) then R <- R + Z end if + * 8: end for + * 9: return R + *+ */ + public static ECPoint shamirsTrick(ECPoint P, BigInteger k, + ECPoint Q, BigInteger l) + { + ECCurve cp = P.getCurve(); + Q = importPoint(cp, Q); + + return implShamirsTrick(P, k, Q, l); + } + + public static ECPoint importPoint(ECCurve c, ECPoint p) + { + ECCurve cp = p.getCurve(); + if (!c.equals(cp)) + { + throw new IllegalArgumentException("Point must be on the same curve"); + } + return c.importPoint(p); + } + + static void implMontgomeryTrick(ECFieldElement[] zs, int off, int len) + { + /* + * Uses the "Montgomery Trick" to invert many field elements, with only a single actual + * field inversion. See e.g. the paper: + * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick" + * by Katsuyuki Okeya, Kouichi Sakurai. + */ + + ECFieldElement[] c = new ECFieldElement[len]; + c[0] = zs[off]; + + int i = 0; + while (++i < len) + { + c[i] = c[i - 1].multiply(zs[off + i]); + } + + ECFieldElement u = c[--i].invert(); + + while (i > 0) + { + int j = off + i--; + ECFieldElement tmp = zs[j]; + zs[j] = c[i].multiply(u); + u = u.multiply(tmp); + } + + zs[off] = u; + } + + static ECPoint implShamirsTrick(ECPoint P, BigInteger k, + ECPoint Q, BigInteger l) + { + ECCurve curve = P.getCurve(); + ECPoint infinity = curve.getInfinity(); + + // TODO conjugate co-Z addition (ZADDC) can return both of these + ECPoint PaddQ = P.add(Q); + ECPoint PsubQ = P.subtract(Q); + + ECPoint[] points = new ECPoint[]{ Q, PsubQ, P, PaddQ }; + curve.normalizeAll(points); + + ECPoint[] table = new ECPoint[] { + points[3].negate(), points[2].negate(), points[1].negate(), + points[0].negate(), infinity, points[0], + points[1], points[2], points[3] }; + + byte[] jsf = WNafUtil.generateJSF(k, l); + + ECPoint R = infinity; + + int i = jsf.length; + while (--i >= 0) + { + int jsfi = jsf[i]; + int kDigit = (jsfi >> 4), lDigit = ((jsfi << 28) >> 28); + + int index = 4 + (kDigit * 3) + lDigit; + R = R.twicePlus(table[index]); + } + + return R; + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECConstants.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECConstants.java new file mode 100644 index 000000000..c2b2a09a4 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECConstants.java @@ -0,0 +1,12 @@ +package org.spongycastle.math.ec; + +import java.math.BigInteger; + +public interface ECConstants +{ + public static final BigInteger ZERO = BigInteger.valueOf(0); + public static final BigInteger ONE = BigInteger.valueOf(1); + public static final BigInteger TWO = BigInteger.valueOf(2); + public static final BigInteger THREE = BigInteger.valueOf(3); + public static final BigInteger FOUR = BigInteger.valueOf(4); +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECCurve.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECCurve.java new file mode 100644 index 000000000..137681e3b --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECCurve.java @@ -0,0 +1,972 @@ +package org.spongycastle.math.ec; + +import java.math.BigInteger; +import java.util.Random; + +import org.spongycastle.util.BigIntegers; + +/** + * base class for an elliptic curve + */ +public abstract class ECCurve +{ + public static final int COORD_AFFINE = 0; + public static final int COORD_HOMOGENEOUS = 1; + public static final int COORD_JACOBIAN = 2; + public static final int COORD_JACOBIAN_CHUDNOVSKY = 3; + public static final int COORD_JACOBIAN_MODIFIED = 4; + public static final int COORD_LAMBDA_AFFINE = 5; + public static final int COORD_LAMBDA_PROJECTIVE = 6; + public static final int COORD_SKEWED = 7; + + public static int[] getAllCoordinateSystems() + { + return new int[]{ COORD_AFFINE, COORD_HOMOGENEOUS, COORD_JACOBIAN, COORD_JACOBIAN_CHUDNOVSKY, + COORD_JACOBIAN_MODIFIED, COORD_LAMBDA_AFFINE, COORD_LAMBDA_PROJECTIVE, COORD_SKEWED }; + } + + public class Config + { + protected int coord; + protected ECMultiplier multiplier; + + Config(int coord, ECMultiplier multiplier) + { + this.coord = coord; + this.multiplier = multiplier; + } + + public Config setCoordinateSystem(int coord) + { + this.coord = coord; + return this; + } + + public Config setMultiplier(ECMultiplier multiplier) + { + this.multiplier = multiplier; + return this; + } + + public ECCurve create() + { + if (!supportsCoordinateSystem(coord)) + { + throw new IllegalStateException("unsupported coordinate system"); + } + + ECCurve c = cloneCurve(); + if (c == ECCurve.this) + { + throw new IllegalStateException("implementation returned current curve"); + } + + c.coord = coord; + c.multiplier = multiplier; + + return c; + } + } + + protected ECFieldElement a, b; + protected int coord = COORD_AFFINE; + protected ECMultiplier multiplier = null; + + public abstract int getFieldSize(); + + public abstract ECFieldElement fromBigInteger(BigInteger x); + + public Config configure() + { + return new Config(this.coord, this.multiplier); + } + + public ECPoint createPoint(BigInteger x, BigInteger y) + { + return createPoint(x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, use {@link #createPoint(BigInteger, BigInteger)} + * and refer {@link ECPoint#getEncoded(boolean)} + */ + public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression) + { + return createRawPoint(fromBigInteger(x), fromBigInteger(y), withCompression); + } + + protected abstract ECCurve cloneCurve(); + + protected abstract ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression); + + protected ECMultiplier createDefaultMultiplier() + { + return new WNafL2RMultiplier(); + } + + public boolean supportsCoordinateSystem(int coord) + { + return coord == COORD_AFFINE; + } + + public PreCompInfo getPreCompInfo(ECPoint p) + { + checkPoint(p); + return p.preCompInfo; + } + + /** + * Sets the
PreCompInfo
for a point on this curve. Used by
+ * ECMultiplier
s to save the precomputation for this ECPoint
for use
+ * by subsequent multiplication.
+ *
+ * @param point
+ * The ECPoint
to store precomputations for.
+ * @param preCompInfo
+ * The values precomputed by the ECMultiplier
.
+ */
+ public void setPreCompInfo(ECPoint point, PreCompInfo preCompInfo)
+ {
+ checkPoint(point);
+ point.preCompInfo = preCompInfo;
+ }
+
+ public ECPoint importPoint(ECPoint p)
+ {
+ if (this == p.getCurve())
+ {
+ return p;
+ }
+ if (p.isInfinity())
+ {
+ return getInfinity();
+ }
+
+ // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
+ p = p.normalize();
+
+ return createPoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression);
+ }
+
+ /**
+ * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+ * coordinates reflect those of the equivalent point in an affine coordinate system. Where more
+ * than one point is to be normalized, this method will generally be more efficient than
+ * normalizing each point separately.
+ *
+ * @param points
+ * An array of points that will be updated in place with their normalized versions,
+ * where necessary
+ */
+ public void normalizeAll(ECPoint[] points)
+ {
+ checkPoints(points);
+
+ if (this.getCoordinateSystem() == ECCurve.COORD_AFFINE)
+ {
+ return;
+ }
+
+ /*
+ * Figure out which of the points actually need to be normalized
+ */
+ ECFieldElement[] zs = new ECFieldElement[points.length];
+ int[] indices = new int[points.length];
+ int count = 0;
+ for (int i = 0; i < points.length; ++i)
+ {
+ ECPoint p = points[i];
+ if (null != p && !p.isNormalized())
+ {
+ zs[count] = p.getZCoord(0);
+ indices[count++] = i;
+ }
+ }
+
+ if (count == 0)
+ {
+ return;
+ }
+
+ ECAlgorithms.implMontgomeryTrick(zs, 0, count);
+
+ for (int j = 0; j < count; ++j)
+ {
+ int index = indices[j];
+ points[index] = points[index].normalize(zs[j]);
+ }
+ }
+
+ public abstract ECPoint getInfinity();
+
+ public ECFieldElement getA()
+ {
+ return a;
+ }
+
+ public ECFieldElement getB()
+ {
+ return b;
+ }
+
+ public int getCoordinateSystem()
+ {
+ return coord;
+ }
+
+ protected abstract ECPoint decompressPoint(int yTilde, BigInteger X1);
+
+ /**
+ * Sets the default ECMultiplier
, unless already set.
+ */
+ public ECMultiplier getMultiplier()
+ {
+ if (this.multiplier == null)
+ {
+ this.multiplier = createDefaultMultiplier();
+ }
+ return this.multiplier;
+ }
+
+ /**
+ * Decode a point on this curve from its ASN.1 encoding. The different
+ * encodings are taken account of, including point compression for
+ * Fp
(X9.62 s 4.2.1 pg 17).
+ * @return The decoded point.
+ */
+ public ECPoint decodePoint(byte[] encoded)
+ {
+ ECPoint p = null;
+ int expectedLength = (getFieldSize() + 7) / 8;
+
+ switch (encoded[0])
+ {
+ case 0x00: // infinity
+ {
+ if (encoded.length != 1)
+ {
+ throw new IllegalArgumentException("Incorrect length for infinity encoding");
+ }
+
+ p = getInfinity();
+ break;
+ }
+ case 0x02: // compressed
+ case 0x03: // compressed
+ {
+ if (encoded.length != (expectedLength + 1))
+ {
+ throw new IllegalArgumentException("Incorrect length for compressed encoding");
+ }
+
+ int yTilde = encoded[0] & 1;
+ BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength);
+
+ p = decompressPoint(yTilde, X);
+ break;
+ }
+ case 0x04: // uncompressed
+ case 0x06: // hybrid
+ case 0x07: // hybrid
+ {
+ if (encoded.length != (2 * expectedLength + 1))
+ {
+ throw new IllegalArgumentException("Incorrect length for uncompressed/hybrid encoding");
+ }
+
+ BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength);
+ BigInteger Y = BigIntegers.fromUnsignedByteArray(encoded, 1 + expectedLength, expectedLength);
+
+ p = createPoint(X, Y);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Invalid point encoding 0x" + Integer.toString(encoded[0], 16));
+ }
+
+ return p;
+ }
+
+ protected void checkPoint(ECPoint point)
+ {
+ if (null == point || (this != point.getCurve()))
+ {
+ throw new IllegalArgumentException("'point' must be non-null and on this curve");
+ }
+ }
+
+ protected void checkPoints(ECPoint[] points)
+ {
+ if (points == null)
+ {
+ throw new IllegalArgumentException("'points' cannot be null");
+ }
+
+ for (int i = 0; i < points.length; ++i)
+ {
+ ECPoint point = points[i];
+ if (null != point && this != point.getCurve())
+ {
+ throw new IllegalArgumentException("'points' entries must be null or on this curve");
+ }
+ }
+ }
+
+ /**
+ * Elliptic curve over Fp
+ */
+ public static class Fp extends ECCurve
+ {
+ private static final int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+ BigInteger q, r;
+ ECPoint.Fp infinity;
+
+ public Fp(BigInteger q, BigInteger a, BigInteger b)
+ {
+ this.q = q;
+ this.r = ECFieldElement.Fp.calculateResidue(q);
+ this.infinity = new ECPoint.Fp(this, null, null);
+
+ this.a = fromBigInteger(a);
+ this.b = fromBigInteger(b);
+ this.coord = FP_DEFAULT_COORDS;
+ }
+
+ protected Fp(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b)
+ {
+ this.q = q;
+ this.r = r;
+ this.infinity = new ECPoint.Fp(this, null, null);
+
+ this.a = a;
+ this.b = b;
+ this.coord = FP_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new Fp(q, r, a, b);
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_AFFINE:
+ case COORD_HOMOGENEOUS:
+ case COORD_JACOBIAN:
+ case COORD_JACOBIAN_MODIFIED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new ECFieldElement.Fp(this.q, this.r, x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new ECPoint.Fp(this, x, y, withCompression);
+ }
+
+ public ECPoint importPoint(ECPoint p)
+ {
+ if (this != p.getCurve() && this.getCoordinateSystem() == COORD_JACOBIAN && !p.isInfinity())
+ {
+ switch (p.getCurve().getCoordinateSystem())
+ {
+ case COORD_JACOBIAN:
+ case COORD_JACOBIAN_CHUDNOVSKY:
+ case COORD_JACOBIAN_MODIFIED:
+ return new ECPoint.Fp(this,
+ fromBigInteger(p.x.toBigInteger()),
+ fromBigInteger(p.y.toBigInteger()),
+ new ECFieldElement[]{ fromBigInteger(p.zs[0].toBigInteger()) },
+ p.withCompression);
+ default:
+ break;
+ }
+ }
+
+ return super.importPoint(p);
+ }
+
+ protected ECPoint decompressPoint(int yTilde, BigInteger X1)
+ {
+ ECFieldElement x = fromBigInteger(X1);
+ ECFieldElement alpha = x.multiply(x.square().add(a)).add(b);
+ ECFieldElement beta = alpha.sqrt();
+
+ //
+ // if we can't find a sqrt we haven't got a point on the
+ // curve - run!
+ //
+ if (beta == null)
+ {
+ throw new RuntimeException("Invalid point compression");
+ }
+
+ BigInteger betaValue = beta.toBigInteger();
+ if (betaValue.testBit(0) != (yTilde == 1))
+ {
+ // Use the other root
+ beta = fromBigInteger(q.subtract(betaValue));
+ }
+
+ return new ECPoint.Fp(this, x, beta, true);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean equals(
+ Object anObject)
+ {
+ if (anObject == this)
+ {
+ return true;
+ }
+
+ if (!(anObject instanceof ECCurve.Fp))
+ {
+ return false;
+ }
+
+ ECCurve.Fp other = (ECCurve.Fp) anObject;
+
+ return this.q.equals(other.q)
+ && a.equals(other.a) && b.equals(other.b);
+ }
+
+ public int hashCode()
+ {
+ return a.hashCode() ^ b.hashCode() ^ q.hashCode();
+ }
+ }
+
+ /**
+ * Elliptic curves over F2m. The Weierstrass equation is given by
+ * y2 + xy = x3 + ax2 + b
.
+ */
+ public static class F2m extends ECCurve
+ {
+ private static final int F2M_DEFAULT_COORDS = COORD_AFFINE;
+
+ /**
+ * The exponent m
of F2m
.
+ */
+ private int m; // can't be final - JDK 1.1
+
+ /**
+ * TPB: The integer k
where xm +
+ * xk + 1
represents the reduction polynomial
+ * f(z)
.k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.μ
of the elliptic curve if this is
+ * a Koblitz curve.
+ */
+ private byte mu = 0;
+
+ /**
+ * The auxiliary values s0
and
+ * s1
used for partial modular reduction for
+ * Koblitz curves.
+ */
+ private BigInteger[] si = null;
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (TPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k The integer k
where xm +
+ * xk + 1
represents the reduction
+ * polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ */
+ public F2m(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b)
+ {
+ this(m, k, 0, 0, a, b, null, null);
+ }
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (TPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k The integer k
where xm +
+ * xk + 1
represents the reduction
+ * polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param n The order of the main subgroup of the elliptic curve.
+ * @param h The cofactor of the elliptic curve, i.e.
+ * #Ea(F2m) = h * n
.
+ */
+ public F2m(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b,
+ BigInteger n,
+ BigInteger h)
+ {
+ this(m, k, 0, 0, a, b, n, h);
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (PPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k1 The integer k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k2 The integer k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k3 The integer k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b)
+ {
+ this(m, k1, k2, k3, a, b, null, null);
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (PPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k1 The integer k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k2 The integer k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k3 The integer k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param n The order of the main subgroup of the elliptic curve.
+ * @param h The cofactor of the elliptic curve, i.e.
+ * #Ea(F2m) = h * n
.
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b,
+ BigInteger n,
+ BigInteger h)
+ {
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ this.n = n;
+ this.h = h;
+
+ if (k1 == 0)
+ {
+ throw new IllegalArgumentException("k1 must be > 0");
+ }
+
+ if (k2 == 0)
+ {
+ if (k3 != 0)
+ {
+ throw new IllegalArgumentException("k3 must be 0 if k2 == 0");
+ }
+ }
+ else
+ {
+ if (k2 <= k1)
+ {
+ throw new IllegalArgumentException("k2 must be > k1");
+ }
+
+ if (k3 <= k2)
+ {
+ throw new IllegalArgumentException("k3 must be > k2");
+ }
+ }
+
+ this.infinity = new ECPoint.F2m(this, null, null);
+ this.a = fromBigInteger(a);
+ this.b = fromBigInteger(b);
+ this.coord = F2M_DEFAULT_COORDS;
+ }
+
+ protected F2m(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger n, BigInteger h)
+ {
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ this.n = n;
+ this.h = h;
+
+ this.infinity = new ECPoint.F2m(this, null, null);
+ this.a = a;
+ this.b = b;
+ this.coord = F2M_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new F2m(m, k1, k2, k3, a, b, n, h);
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_AFFINE:
+ case COORD_HOMOGENEOUS:
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ if (isKoblitz())
+ {
+ return new WTauNafMultiplier();
+ }
+
+ return super.createDefaultMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return m;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
+ }
+
+ public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+ {
+ ECFieldElement X = fromBigInteger(x), Y = fromBigInteger(y);
+
+ switch (this.getCoordinateSystem())
+ {
+ case COORD_LAMBDA_AFFINE:
+ case COORD_LAMBDA_PROJECTIVE:
+ {
+ if (!X.isZero())
+ {
+ // Y becomes Lambda (X + Y/X) here
+ Y = Y.divide(X).add(X);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return createRawPoint(X, Y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new ECPoint.F2m(this, x, y, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ /**
+ * Returns true if this is a Koblitz curve (ABC curve).
+ * @return true if this is a Koblitz curve (ABC curve), false otherwise
+ */
+ public boolean isKoblitz()
+ {
+ return n != null && h != null && a.bitLength() <= 1 && b.bitLength() == 1;
+ }
+
+ /**
+ * Returns the parameter μ
of the elliptic curve.
+ * @return μ
of the elliptic curve.
+ * @throws IllegalArgumentException if the given ECCurve is not a
+ * Koblitz curve.
+ */
+ synchronized byte getMu()
+ {
+ if (mu == 0)
+ {
+ mu = Tnaf.getMu(this);
+ }
+ return mu;
+ }
+
+ /**
+ * @return the auxiliary values s0
and
+ * s1
used for partial modular reduction for
+ * Koblitz curves.
+ */
+ synchronized BigInteger[] getSi()
+ {
+ if (si == null)
+ {
+ si = Tnaf.getSi(this);
+ }
+ return si;
+ }
+
+ /**
+ * Decompresses a compressed point P = (xp, yp) (X9.62 s 4.2.2).
+ *
+ * @param yTilde
+ * ~yp, an indication bit for the decompression of yp.
+ * @param X1
+ * The field element xp.
+ * @return the decompressed point.
+ */
+ protected ECPoint decompressPoint(int yTilde, BigInteger X1)
+ {
+ ECFieldElement xp = fromBigInteger(X1);
+ ECFieldElement yp = null;
+ if (xp.isZero())
+ {
+ yp = (ECFieldElement.F2m)b;
+ for (int i = 0; i < m - 1; i++)
+ {
+ yp = yp.square();
+ }
+ }
+ else
+ {
+ ECFieldElement beta = xp.add(a).add(b.multiply(xp.square().invert()));
+ ECFieldElement z = solveQuadraticEquation(beta);
+ if (z == null)
+ {
+ throw new IllegalArgumentException("Invalid point compression");
+ }
+ if (z.testBitZero() != (yTilde == 1))
+ {
+ z = z.addOne();
+ }
+
+ yp = xp.multiply(z);
+
+ switch (this.getCoordinateSystem())
+ {
+ case COORD_LAMBDA_AFFINE:
+ case COORD_LAMBDA_PROJECTIVE:
+ {
+ yp = yp.divide(xp).add(xp);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ return new ECPoint.F2m(this, xp, yp, true);
+ }
+
+ /**
+ * Solves a quadratic equation z2 + z = beta
(X9.62
+ * D.1.6) The other solution is z + 1
.
+ *
+ * @param beta
+ * The value to solve the quadratic equation for.
+ * @return the solution for z2 + z = beta
or
+ * null
if no solution exists.
+ */
+ private ECFieldElement solveQuadraticEquation(ECFieldElement beta)
+ {
+ if (beta.isZero())
+ {
+ return beta;
+ }
+
+ ECFieldElement zeroElement = fromBigInteger(ECConstants.ZERO);
+
+ ECFieldElement z = null;
+ ECFieldElement gamma = null;
+
+ Random rand = new Random();
+ do
+ {
+ ECFieldElement t = fromBigInteger(new BigInteger(m, rand));
+ z = zeroElement;
+ ECFieldElement w = beta;
+ for (int i = 1; i <= m - 1; i++)
+ {
+ ECFieldElement w2 = w.square();
+ z = z.square().add(w2.multiply(t));
+ w = w2.add(beta);
+ }
+ if (!w.isZero())
+ {
+ return null;
+ }
+ gamma = z.square().add(z);
+ }
+ while (gamma.isZero());
+
+ return z;
+ }
+
+ public boolean equals(
+ Object anObject)
+ {
+ if (anObject == this)
+ {
+ return true;
+ }
+
+ if (!(anObject instanceof ECCurve.F2m))
+ {
+ return false;
+ }
+
+ ECCurve.F2m other = (ECCurve.F2m)anObject;
+
+ return (this.m == other.m) && (this.k1 == other.k1)
+ && (this.k2 == other.k2) && (this.k3 == other.k3)
+ && a.equals(other.a) && b.equals(other.b);
+ }
+
+ public int hashCode()
+ {
+ return this.a.hashCode() ^ this.b.hashCode() ^ m ^ k1 ^ k2 ^ k3;
+ }
+
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * Return true if curve uses a Trinomial basis.
+ *
+ * @return true if curve Trinomial, false otherwise.
+ */
+ public boolean isTrinomial()
+ {
+ return k2 == 0 && k3 == 0;
+ }
+
+ public int getK1()
+ {
+ return k1;
+ }
+
+ public int getK2()
+ {
+ return k2;
+ }
+
+ public int getK3()
+ {
+ return k3;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public BigInteger getH()
+ {
+ return h;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java
new file mode 100644
index 000000000..d72d50782
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java
@@ -0,0 +1,1305 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.BigIntegers;
+
+public abstract class ECFieldElement
+ implements ECConstants
+{
+ public abstract BigInteger toBigInteger();
+ public abstract String getFieldName();
+ public abstract int getFieldSize();
+ public abstract ECFieldElement add(ECFieldElement b);
+ public abstract ECFieldElement addOne();
+ public abstract ECFieldElement subtract(ECFieldElement b);
+ public abstract ECFieldElement multiply(ECFieldElement b);
+ public abstract ECFieldElement divide(ECFieldElement b);
+ public abstract ECFieldElement negate();
+ public abstract ECFieldElement square();
+ public abstract ECFieldElement invert();
+ public abstract ECFieldElement sqrt();
+
+ public int bitLength()
+ {
+ return toBigInteger().bitLength();
+ }
+
+ public boolean isZero()
+ {
+ return 0 == toBigInteger().signum();
+ }
+
+ public boolean testBitZero()
+ {
+ return toBigInteger().testBit(0);
+ }
+
+ public String toString()
+ {
+ return this.toBigInteger().toString(16);
+ }
+
+ public byte[] getEncoded()
+ {
+ return BigIntegers.asUnsignedByteArray((getFieldSize() + 7) / 8, toBigInteger());
+ }
+
+ public static class Fp extends ECFieldElement
+ {
+ BigInteger q, r, x;
+
+// static int[] calculateNaf(BigInteger p)
+// {
+// int[] naf = WNafUtil.generateCompactNaf(p);
+//
+// int bit = 0;
+// for (int i = 0; i < naf.length; ++i)
+// {
+// int ni = naf[i];
+// int digit = ni >> 16, zeroes = ni & 0xFFFF;
+//
+// bit += zeroes;
+// naf[i] = digit < 0 ? ~bit : bit;
+// ++bit;
+// }
+//
+// int last = naf.length - 1;
+// if (last > 0 && last <= 16)
+// {
+// int top = naf[last], top2 = naf[last - 1];
+// if (top2 < 0)
+// {
+// top2 = ~top2;
+// }
+// if (top - top2 >= 64)
+// {
+// return naf;
+// }
+// }
+//
+// return null;
+// }
+
+ static BigInteger calculateResidue(BigInteger p)
+ {
+ int bitLength = p.bitLength();
+ if (bitLength > 128)
+ {
+ BigInteger firstWord = p.shiftRight(bitLength - 64);
+ if (firstWord.longValue() == -1L)
+ {
+ return ONE.shiftLeft(bitLength).subtract(p);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use ECCurve.fromBigInteger to construct field elements
+ */
+ public Fp(BigInteger q, BigInteger x)
+ {
+ this(q, calculateResidue(q), x);
+ }
+
+ Fp(BigInteger q, BigInteger r, BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
+ {
+ throw new IllegalArgumentException("x value invalid in Fp field element");
+ }
+
+ this.q = q;
+ this.r = r;
+ this.x = x;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return x;
+ }
+
+ /**
+ * return the field name for this field.
+ *
+ * @return the string "Fp".
+ */
+ public String getFieldName()
+ {
+ return "Fp";
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ return new Fp(q, r, modAdd(x, b.toBigInteger()));
+ }
+
+ public ECFieldElement addOne()
+ {
+ BigInteger x2 = x.add(ECConstants.ONE);
+ if (x2.compareTo(q) == 0)
+ {
+ x2 = ECConstants.ZERO;
+ }
+ return new Fp(q, r, x2);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ BigInteger x2 = b.toBigInteger();
+ BigInteger x3 = x.subtract(x2);
+ if (x3.signum() < 0)
+ {
+ x3 = x3.add(q);
+ }
+ return new Fp(q, r, x3);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ return new Fp(q, r, modMult(x, b.toBigInteger()));
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return new Fp(q, modMult(x, b.toBigInteger().modInverse(q)));
+ }
+
+ public ECFieldElement negate()
+ {
+ BigInteger x2;
+ if (x.signum() == 0)
+ {
+ x2 = x;
+ }
+ else if (ONE.equals(r))
+ {
+ x2 = q.xor(x);
+ }
+ else
+ {
+ x2 = q.subtract(x);
+ }
+ return new Fp(q, r, x2);
+ }
+
+ public ECFieldElement square()
+ {
+ return new Fp(q, r, modMult(x, x));
+ }
+
+ public ECFieldElement invert()
+ {
+ // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime.
+ return new Fp(q, r, x.modInverse(q));
+ }
+
+ // D.1.4 91
+ /**
+ * return a sqrt root - the routine verifies that the calculation
+ * returns the right value - if none exists it returns null.
+ */
+ public ECFieldElement sqrt()
+ {
+ if (!q.testBit(0))
+ {
+ throw new RuntimeException("not done yet");
+ }
+
+ // note: even though this class implements ECConstants don't be tempted to
+ // remove the explicit declaration, some J2ME environments don't cope.
+ // p mod 4 == 3
+ if (q.testBit(1))
+ {
+ // z = g^(u+1) + p, p = 4u + 3
+ ECFieldElement z = new Fp(q, r, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q));
+
+ return z.square().equals(this) ? z : null;
+ }
+
+ // p mod 4 == 1
+ BigInteger qMinusOne = q.subtract(ECConstants.ONE);
+
+ BigInteger legendreExponent = qMinusOne.shiftRight(1);
+ if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE)))
+ {
+ return null;
+ }
+
+ BigInteger u = qMinusOne.shiftRight(2);
+ BigInteger k = u.shiftLeft(1).add(ECConstants.ONE);
+
+ BigInteger Q = this.x;
+ BigInteger fourQ = modDouble(modDouble(Q));
+
+ BigInteger U, V;
+ Random rand = new Random();
+ do
+ {
+ BigInteger P;
+ do
+ {
+ P = new BigInteger(q.bitLength(), rand);
+ }
+ while (P.compareTo(q) >= 0
+ || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, q).equals(qMinusOne)));
+
+ BigInteger[] result = lucasSequence(P, Q, k);
+ U = result[0];
+ V = result[1];
+
+ if (modMult(V, V).equals(fourQ))
+ {
+ // Integer division by 2, mod q
+ if (V.testBit(0))
+ {
+ V = V.add(q);
+ }
+
+ V = V.shiftRight(1);
+
+ //assert V.multiply(V).mod(q).equals(x);
+
+ return new ECFieldElement.Fp(q, r, V);
+ }
+ }
+ while (U.equals(ECConstants.ONE) || U.equals(qMinusOne));
+
+ return null;
+
+// BigInteger qMinusOne = q.subtract(ECConstants.ONE);
+// BigInteger legendreExponent = qMinusOne.shiftRight(1); //divide(ECConstants.TWO);
+// if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE)))
+// {
+// return null;
+// }
+//
+// Random rand = new Random();
+// BigInteger fourX = x.shiftLeft(2);
+//
+// BigInteger r;
+// do
+// {
+// r = new BigInteger(q.bitLength(), rand);
+// }
+// while (r.compareTo(q) >= 0
+// || !(r.multiply(r).subtract(fourX).modPow(legendreExponent, q).equals(qMinusOne)));
+//
+// BigInteger n1 = qMinusOne.shiftRight(2); //.divide(ECConstants.FOUR);
+// BigInteger n2 = n1.add(ECConstants.ONE); //q.add(ECConstants.THREE).divide(ECConstants.FOUR);
+//
+// BigInteger wOne = WOne(r, x, q);
+// BigInteger wSum = W(n1, wOne, q).add(W(n2, wOne, q)).mod(q);
+// BigInteger twoR = r.shiftLeft(1); //ECConstants.TWO.multiply(r);
+//
+// BigInteger root = twoR.modPow(q.subtract(ECConstants.TWO), q)
+// .multiply(x).mod(q)
+// .multiply(wSum).mod(q);
+//
+// return new Fp(q, root);
+ }
+
+// private static BigInteger W(BigInteger n, BigInteger wOne, BigInteger p)
+// {
+// if (n.equals(ECConstants.ONE))
+// {
+// return wOne;
+// }
+// boolean isEven = !n.testBit(0);
+// n = n.shiftRight(1);//divide(ECConstants.TWO);
+// if (isEven)
+// {
+// BigInteger w = W(n, wOne, p);
+// return w.multiply(w).subtract(ECConstants.TWO).mod(p);
+// }
+// BigInteger w1 = W(n.add(ECConstants.ONE), wOne, p);
+// BigInteger w2 = W(n, wOne, p);
+// return w1.multiply(w2).subtract(wOne).mod(p);
+// }
+//
+// private BigInteger WOne(BigInteger r, BigInteger x, BigInteger p)
+// {
+// return r.multiply(r).multiply(x.modPow(q.subtract(ECConstants.TWO), q)).subtract(ECConstants.TWO).mod(p);
+// }
+
+ private BigInteger[] lucasSequence(
+ BigInteger P,
+ BigInteger Q,
+ BigInteger k)
+ {
+ int n = k.bitLength();
+ int s = k.getLowestSetBit();
+
+ BigInteger Uh = ECConstants.ONE;
+ BigInteger Vl = ECConstants.TWO;
+ BigInteger Vh = P;
+ BigInteger Ql = ECConstants.ONE;
+ BigInteger Qh = ECConstants.ONE;
+
+ for (int j = n - 1; j >= s + 1; --j)
+ {
+ Ql = modMult(Ql, Qh);
+
+ if (k.testBit(j))
+ {
+ Qh = modMult(Ql, Q);
+ Uh = modMult(Uh, Vh);
+ Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+ Vh = modReduce(Vh.multiply(Vh).subtract(Qh.shiftLeft(1)));
+ }
+ else
+ {
+ Qh = Ql;
+ Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
+ Vh = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+ Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
+ }
+ }
+
+ Ql = modMult(Ql, Qh);
+ Qh = modMult(Ql, Q);
+ Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
+ Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+ Ql = modMult(Ql, Qh);
+
+ for (int j = 1; j <= s; ++j)
+ {
+ Uh = modMult(Uh, Vl);
+ Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
+ Ql = modMult(Ql, Ql);
+ }
+
+ return new BigInteger[]{ Uh, Vl };
+ }
+
+ protected BigInteger modAdd(BigInteger x1, BigInteger x2)
+ {
+ BigInteger x3 = x1.add(x2);
+ if (x3.compareTo(q) >= 0)
+ {
+ x3 = x3.subtract(q);
+ }
+ return x3;
+ }
+
+ protected BigInteger modDouble(BigInteger x)
+ {
+ BigInteger _2x = x.shiftLeft(1);
+ if (_2x.compareTo(q) >= 0)
+ {
+ _2x = _2x.subtract(q);
+ }
+ return _2x;
+ }
+
+ protected BigInteger modMult(BigInteger x1, BigInteger x2)
+ {
+ return modReduce(x1.multiply(x2));
+ }
+
+ protected BigInteger modReduce(BigInteger x)
+ {
+// if (naf != null)
+// {
+// int last = naf.length - 1;
+// int bits = naf[last];
+// while (x.bitLength() > (bits + 1))
+// {
+// BigInteger u = x.shiftRight(bits);
+// BigInteger v = x.subtract(u.shiftLeft(bits));
+//
+// x = v;
+//
+// for (int i = 0; i < last; ++i)
+// {
+// int ni = naf[i];
+// if (ni < 0)
+// {
+// x = x.add(u.shiftLeft(~ni));
+// }
+// else
+// {
+// x = x.subtract(u.shiftLeft(ni));
+// }
+// }
+// }
+// while (x.compareTo(q) >= 0)
+// {
+// x = x.subtract(q);
+// }
+// }
+// else
+ if (r != null)
+ {
+ int qLen = q.bitLength();
+ while (x.bitLength() > (qLen + 1))
+ {
+ BigInteger u = x.shiftRight(qLen);
+ BigInteger v = x.subtract(u.shiftLeft(qLen));
+ if (!r.equals(ONE))
+ {
+ u = u.multiply(r);
+ }
+ x = u.add(v);
+ }
+ while (x.compareTo(q) >= 0)
+ {
+ x = x.subtract(q);
+ }
+ }
+ else
+ {
+ x = x.mod(q);
+ }
+ return x;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof ECFieldElement.Fp))
+ {
+ return false;
+ }
+
+ ECFieldElement.Fp o = (ECFieldElement.Fp)other;
+ return q.equals(o.q) && x.equals(o.x);
+ }
+
+ public int hashCode()
+ {
+ return q.hashCode() ^ x.hashCode();
+ }
+ }
+
+// /**
+// * Class representing the Elements of the finite field
+// * F2m
in polynomial basis (PB)
+// * representation. Both trinomial (TPB) and pentanomial (PPB) polynomial
+// * basis representations are supported. Gaussian normal basis (GNB)
+// * representation is not supported.
+// */
+// public static class F2m extends ECFieldElement
+// {
+// BigInteger x;
+//
+// /**
+// * Indicates gaussian normal basis representation (GNB). Number chosen
+// * according to X9.62. GNB is not implemented at present.
+// */
+// public static final int GNB = 1;
+//
+// /**
+// * Indicates trinomial basis representation (TPB). Number chosen
+// * according to X9.62.
+// */
+// public static final int TPB = 2;
+//
+// /**
+// * Indicates pentanomial basis representation (PPB). Number chosen
+// * according to X9.62.
+// */
+// public static final int PPB = 3;
+//
+// /**
+// * TPB or PPB.
+// */
+// private int representation;
+//
+// /**
+// * The exponent m
of F2m
.
+// */
+// private int m;
+//
+// /**
+// * TPB: The integer k
where xm +
+// * xk + 1
represents the reduction polynomial
+// * f(z)
.k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.m
of
+// * F2m
.
+// * @param k1 The integer k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.
+// * @param k2 The integer k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.
+// * @param k3 The integer k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.
+// * @param x The BigInteger representing the value of the field element.
+// */
+// public F2m(
+// int m,
+// int k1,
+// int k2,
+// int k3,
+// BigInteger x)
+// {
+//// super(x);
+// this.x = x;
+//
+// if ((k2 == 0) && (k3 == 0))
+// {
+// this.representation = TPB;
+// }
+// else
+// {
+// if (k2 >= k3)
+// {
+// throw new IllegalArgumentException(
+// "k2 must be smaller than k3");
+// }
+// if (k2 <= 0)
+// {
+// throw new IllegalArgumentException(
+// "k2 must be larger than 0");
+// }
+// this.representation = PPB;
+// }
+//
+// if (x.signum() < 0)
+// {
+// throw new IllegalArgumentException("x value cannot be negative");
+// }
+//
+// this.m = m;
+// this.k1 = k1;
+// this.k2 = k2;
+// this.k3 = k3;
+// }
+//
+// /**
+// * Constructor for TPB.
+// * @param m The exponent m
of
+// * F2m
.
+// * @param k The integer k
where xm +
+// * xk + 1
represents the reduction
+// * polynomial f(z)
.
+// * @param x The BigInteger representing the value of the field element.
+// */
+// public F2m(int m, int k, BigInteger x)
+// {
+// // Set k1 to k, and set k2 and k3 to 0
+// this(m, k, 0, 0, x);
+// }
+//
+// public BigInteger toBigInteger()
+// {
+// return x;
+// }
+//
+// public String getFieldName()
+// {
+// return "F2m";
+// }
+//
+// public int getFieldSize()
+// {
+// return m;
+// }
+//
+// /**
+// * Checks, if the ECFieldElements a
and b
+// * are elements of the same field F2m
+// * (having the same representation).
+// * @param a field element.
+// * @param b field element to be compared.
+// * @throws IllegalArgumentException if a
and b
+// * are not elements of the same field
+// * F2m
(having the same
+// * representation).
+// */
+// public static void checkFieldElements(
+// ECFieldElement a,
+// ECFieldElement b)
+// {
+// if ((!(a instanceof F2m)) || (!(b instanceof F2m)))
+// {
+// throw new IllegalArgumentException("Field elements are not "
+// + "both instances of ECFieldElement.F2m");
+// }
+//
+// if ((a.toBigInteger().signum() < 0) || (b.toBigInteger().signum() < 0))
+// {
+// throw new IllegalArgumentException(
+// "x value may not be negative");
+// }
+//
+// ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a;
+// ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b;
+//
+// if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1)
+// || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3))
+// {
+// throw new IllegalArgumentException("Field elements are not "
+// + "elements of the same field F2m");
+// }
+//
+// if (aF2m.representation != bF2m.representation)
+// {
+// // Should never occur
+// throw new IllegalArgumentException(
+// "One of the field "
+// + "elements are not elements has incorrect representation");
+// }
+// }
+//
+// /**
+// * Computes z * a(z) mod f(z)
, where f(z)
is
+// * the reduction polynomial of this
.
+// * @param a The polynomial a(z)
to be multiplied by
+// * z mod f(z)
.
+// * @return z * a(z) mod f(z)
+// */
+// private BigInteger multZModF(final BigInteger a)
+// {
+// // Left-shift of a(z)
+// BigInteger az = a.shiftLeft(1);
+// if (az.testBit(this.m))
+// {
+// // If the coefficient of z^m in a(z) equals 1, reduction
+// // modulo f(z) is performed: Add f(z) to to a(z):
+// // Step 1: Unset mth coeffient of a(z)
+// az = az.clearBit(this.m);
+//
+// // Step 2: Add r(z) to a(z), where r(z) is defined as
+// // f(z) = z^m + r(z), and k1, k2, k3 are the positions of
+// // the non-zero coefficients in r(z)
+// az = az.flipBit(0);
+// az = az.flipBit(this.k1);
+// if (this.representation == PPB)
+// {
+// az = az.flipBit(this.k2);
+// az = az.flipBit(this.k3);
+// }
+// }
+// return az;
+// }
+//
+// public ECFieldElement add(final ECFieldElement b)
+// {
+// // No check performed here for performance reasons. Instead the
+// // elements involved are checked in ECPoint.F2m
+// // checkFieldElements(this, b);
+// if (b.toBigInteger().signum() == 0)
+// {
+// return this;
+// }
+//
+// return new F2m(this.m, this.k1, this.k2, this.k3, this.x.xor(b.toBigInteger()));
+// }
+//
+// public ECFieldElement subtract(final ECFieldElement b)
+// {
+// // Addition and subtraction are the same in F2m
+// return add(b);
+// }
+//
+//
+// public ECFieldElement multiply(final ECFieldElement b)
+// {
+// // Left-to-right shift-and-add field multiplication in F2m
+// // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+// // Output: c(z) = a(z) * b(z) mod f(z)
+//
+// // No check performed here for performance reasons. Instead the
+// // elements involved are checked in ECPoint.F2m
+// // checkFieldElements(this, b);
+// final BigInteger az = this.x;
+// BigInteger bz = b.toBigInteger();
+// BigInteger cz;
+//
+// // Compute c(z) = a(z) * b(z) mod f(z)
+// if (az.testBit(0))
+// {
+// cz = bz;
+// }
+// else
+// {
+// cz = ECConstants.ZERO;
+// }
+//
+// for (int i = 1; i < this.m; i++)
+// {
+// // b(z) := z * b(z) mod f(z)
+// bz = multZModF(bz);
+//
+// if (az.testBit(i))
+// {
+// // If the coefficient of x^i in a(z) equals 1, b(z) is added
+// // to c(z)
+// cz = cz.xor(bz);
+// }
+// }
+// return new ECFieldElement.F2m(m, this.k1, this.k2, this.k3, cz);
+// }
+//
+//
+// public ECFieldElement divide(final ECFieldElement b)
+// {
+// // There may be more efficient implementations
+// ECFieldElement bInv = b.invert();
+// return multiply(bInv);
+// }
+//
+// public ECFieldElement negate()
+// {
+// // -x == x holds for all x in F2m
+// return this;
+// }
+//
+// public ECFieldElement square()
+// {
+// // Naive implementation, can probably be speeded up using modular
+// // reduction
+// return multiply(this);
+// }
+//
+// public ECFieldElement invert()
+// {
+// // Inversion in F2m using the extended Euclidean algorithm
+// // Input: A nonzero polynomial a(z) of degree at most m-1
+// // Output: a(z)^(-1) mod f(z)
+//
+// // u(z) := a(z)
+// BigInteger uz = this.x;
+// if (uz.signum() <= 0)
+// {
+// throw new ArithmeticException("x is zero or negative, " +
+// "inversion is impossible");
+// }
+//
+// // v(z) := f(z)
+// BigInteger vz = ECConstants.ZERO.setBit(m);
+// vz = vz.setBit(0);
+// vz = vz.setBit(this.k1);
+// if (this.representation == PPB)
+// {
+// vz = vz.setBit(this.k2);
+// vz = vz.setBit(this.k3);
+// }
+//
+// // g1(z) := 1, g2(z) := 0
+// BigInteger g1z = ECConstants.ONE;
+// BigInteger g2z = ECConstants.ZERO;
+//
+// // while u != 1
+// while (!(uz.equals(ECConstants.ZERO)))
+// {
+// // j := deg(u(z)) - deg(v(z))
+// int j = uz.bitLength() - vz.bitLength();
+//
+// // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+// if (j < 0)
+// {
+// final BigInteger uzCopy = uz;
+// uz = vz;
+// vz = uzCopy;
+//
+// final BigInteger g1zCopy = g1z;
+// g1z = g2z;
+// g2z = g1zCopy;
+//
+// j = -j;
+// }
+//
+// // u(z) := u(z) + z^j * v(z)
+// // Note, that no reduction modulo f(z) is required, because
+// // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+// // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+// // = deg(u(z))
+// uz = uz.xor(vz.shiftLeft(j));
+//
+// // g1(z) := g1(z) + z^j * g2(z)
+// g1z = g1z.xor(g2z.shiftLeft(j));
+//// if (g1z.bitLength() > this.m) {
+//// throw new ArithmeticException(
+//// "deg(g1z) >= m, g1z = " + g1z.toString(16));
+//// }
+// }
+// return new ECFieldElement.F2m(
+// this.m, this.k1, this.k2, this.k3, g2z);
+// }
+//
+// public ECFieldElement sqrt()
+// {
+// throw new RuntimeException("Not implemented");
+// }
+//
+// /**
+// * @return the representation of the field
+// * F2m
, either of
+// * TPB (trinomial
+// * basis representation) or
+// * PPB (pentanomial
+// * basis representation).
+// */
+// public int getRepresentation()
+// {
+// return this.representation;
+// }
+//
+// /**
+// * @return the degree m
of the reduction polynomial
+// * f(z)
.
+// */
+// public int getM()
+// {
+// return this.m;
+// }
+//
+// /**
+// * @return TPB: The integer k
where xm +
+// * xk + 1
represents the reduction polynomial
+// * f(z)
.k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.F2m
in polynomial basis (PB)
+ * representation. Both trinomial (TPB) and pentanomial (PPB) polynomial
+ * basis representations are supported. Gaussian normal basis (GNB)
+ * representation is not supported.
+ */
+ public static class F2m extends ECFieldElement
+ {
+ /**
+ * Indicates gaussian normal basis representation (GNB). Number chosen
+ * according to X9.62. GNB is not implemented at present.
+ */
+ public static final int GNB = 1;
+
+ /**
+ * Indicates trinomial basis representation (TPB). Number chosen
+ * according to X9.62.
+ */
+ public static final int TPB = 2;
+
+ /**
+ * Indicates pentanomial basis representation (PPB). Number chosen
+ * according to X9.62.
+ */
+ public static final int PPB = 3;
+
+ /**
+ * TPB or PPB.
+ */
+ private int representation;
+
+ /**
+ * The exponent m
of F2m
.
+ */
+ private int m;
+
+// /**
+// * TPB: The integer k
where xm +
+// * xk + 1
represents the reduction polynomial
+// * f(z)
.k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.LongArray
holding the bits.
+ */
+ private LongArray x;
+
+ /**
+ * Constructor for PPB.
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k1 The integer k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k2 The integer k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k3 The integer k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param x The BigInteger representing the value of the field element.
+ * @deprecated Use ECCurve.fromBigInteger to construct field elements
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger x)
+ {
+ if ((k2 == 0) && (k3 == 0))
+ {
+ this.representation = TPB;
+ this.ks = new int[]{ k1 };
+ }
+ else
+ {
+ if (k2 >= k3)
+ {
+ throw new IllegalArgumentException(
+ "k2 must be smaller than k3");
+ }
+ if (k2 <= 0)
+ {
+ throw new IllegalArgumentException(
+ "k2 must be larger than 0");
+ }
+ this.representation = PPB;
+ this.ks = new int[]{ k1, k2, k3 };
+ }
+
+ this.m = m;
+ this.x = new LongArray(x);
+ }
+
+ /**
+ * Constructor for TPB.
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k The integer k
where xm +
+ * xk + 1
represents the reduction
+ * polynomial f(z)
.
+ * @param x The BigInteger representing the value of the field element.
+ * @deprecated Use ECCurve.fromBigInteger to construct field elements
+ */
+ public F2m(int m, int k, BigInteger x)
+ {
+ // Set k1 to k, and set k2 and k3 to 0
+ this(m, k, 0, 0, x);
+ }
+
+ private F2m(int m, int[] ks, LongArray x)
+ {
+ this.m = m;
+ this.representation = (ks.length == 1) ? TPB : PPB;
+ this.ks = ks;
+ this.x = x;
+ }
+
+ public int bitLength()
+ {
+ return x.degree();
+ }
+
+ public boolean isZero()
+ {
+ return x.isZero();
+ }
+
+ public boolean testBitZero()
+ {
+ return x.testBitZero();
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return x.toBigInteger();
+ }
+
+ public String getFieldName()
+ {
+ return "F2m";
+ }
+
+ public int getFieldSize()
+ {
+ return m;
+ }
+
+ /**
+ * Checks, if the ECFieldElements a
and b
+ * are elements of the same field F2m
+ * (having the same representation).
+ * @param a field element.
+ * @param b field element to be compared.
+ * @throws IllegalArgumentException if a
and b
+ * are not elements of the same field
+ * F2m
(having the same
+ * representation).
+ */
+ public static void checkFieldElements(
+ ECFieldElement a,
+ ECFieldElement b)
+ {
+ if ((!(a instanceof F2m)) || (!(b instanceof F2m)))
+ {
+ throw new IllegalArgumentException("Field elements are not "
+ + "both instances of ECFieldElement.F2m");
+ }
+
+ ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a;
+ ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b;
+
+ if (aF2m.representation != bF2m.representation)
+ {
+ // Should never occur
+ throw new IllegalArgumentException("One of the F2m field elements has incorrect representation");
+ }
+
+ if ((aF2m.m != bF2m.m) || !Arrays.areEqual(aF2m.ks, bF2m.ks))
+ {
+ throw new IllegalArgumentException("Field elements are not elements of the same field F2m");
+ }
+ }
+
+ public ECFieldElement add(final ECFieldElement b)
+ {
+ // No check performed here for performance reasons. Instead the
+ // elements involved are checked in ECPoint.F2m
+ // checkFieldElements(this, b);
+ LongArray iarrClone = (LongArray)this.x.clone();
+ F2m bF2m = (F2m)b;
+ iarrClone.addShiftedByWords(bF2m.x, 0);
+ return new F2m(m, ks, iarrClone);
+ }
+
+ public ECFieldElement addOne()
+ {
+ return new F2m(m, ks, x.addOne());
+ }
+
+ public ECFieldElement subtract(final ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(final ECFieldElement b)
+ {
+ // Right-to-left comb multiplication in the LongArray
+ // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+ // Output: c(z) = a(z) * b(z) mod f(z)
+
+ // No check performed here for performance reasons. Instead the
+ // elements involved are checked in ECPoint.F2m
+ // checkFieldElements(this, b);
+ return new F2m(m, ks, x.modMultiply(((F2m)b).x, m, ks));
+ }
+
+ public ECFieldElement divide(final ECFieldElement b)
+ {
+ // There may be more efficient implementations
+ ECFieldElement bInv = b.invert();
+ return multiply(bInv);
+ }
+
+ public ECFieldElement negate()
+ {
+ // -x == x holds for all x in F2m
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ return new F2m(m, ks, x.modSquare(m, ks));
+ }
+
+ public ECFieldElement invert()
+ {
+ return new ECFieldElement.F2m(this.m, this.ks, this.x.modInverse(m, ks));
+ }
+
+ public ECFieldElement sqrt()
+ {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * @return the representation of the field
+ * F2m
, either of
+ * TPB (trinomial
+ * basis representation) or
+ * PPB (pentanomial
+ * basis representation).
+ */
+ public int getRepresentation()
+ {
+ return this.representation;
+ }
+
+ /**
+ * @return the degree m
of the reduction polynomial
+ * f(z)
.
+ */
+ public int getM()
+ {
+ return this.m;
+ }
+
+ /**
+ * @return TPB: The integer k
where xm +
+ * xk + 1
represents the reduction polynomial
+ * f(z)
.k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.ECPoint
s.
+ */
+public interface ECMultiplier
+{
+ /**
+ * Multiplies the ECPoint p
by k
, i.e.
+ * p
is added k
times to itself.
+ * @param p The ECPoint
to be multiplied.
+ * @param k The factor by which p
is multiplied.
+ * @return p
multiplied by k
.
+ */
+ ECPoint multiply(ECPoint p, BigInteger k);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECPoint.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECPoint.java
new file mode 100644
index 000000000..81d7d0957
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ECPoint.java
@@ -0,0 +1,1733 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * base class for points on elliptic curves.
+ */
+public abstract class ECPoint
+{
+ protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0];
+
+ protected static ECFieldElement[] getInitialZCoords(ECCurve curve)
+ {
+ // Cope with null curve, most commonly used by implicitlyCa
+ int coord = null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ return EMPTY_ZS;
+ default:
+ break;
+ }
+
+ ECFieldElement one = curve.fromBigInteger(ECConstants.ONE);
+
+ switch (coord)
+ {
+ case ECCurve.COORD_HOMOGENEOUS:
+ case ECCurve.COORD_JACOBIAN:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ return new ECFieldElement[]{ one };
+ case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+ return new ECFieldElement[]{ one, one, one };
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ return new ECFieldElement[]{ one, curve.getA() };
+ default:
+ throw new IllegalArgumentException("unknown coordinate system");
+ }
+ }
+
+ protected ECCurve curve;
+ protected ECFieldElement x;
+ protected ECFieldElement y;
+ protected ECFieldElement[] zs;
+
+ protected boolean withCompression;
+
+ protected PreCompInfo preCompInfo = null;
+
+ protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, getInitialZCoords(curve));
+ }
+
+ protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs)
+ {
+ this.curve = curve;
+ this.x = x;
+ this.y = y;
+ this.zs = zs;
+ }
+
+ public ECCurve getCurve()
+ {
+ return curve;
+ }
+
+ protected int getCurveCoordinateSystem()
+ {
+ // Cope with null curve, most commonly used by implicitlyCa
+ return null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem();
+ }
+
+ /**
+ * Normalizes this point, and then returns the affine x-coordinate.
+ *
+ * Note: normalization can be expensive, this method is deprecated in favour
+ * of caller-controlled normalization.
+ *
+ * @deprecated Use getAffineXCoord, or normalize() and getXCoord(), instead
+ */
+ public ECFieldElement getX()
+ {
+ return normalize().getXCoord();
+ }
+
+
+ /**
+ * Normalizes this point, and then returns the affine y-coordinate.
+ *
+ * Note: normalization can be expensive, this method is deprecated in favour
+ * of caller-controlled normalization.
+ *
+ * @deprecated Use getAffineYCoord, or normalize() and getYCoord(), instead
+ */
+ public ECFieldElement getY()
+ {
+ return normalize().getYCoord();
+ }
+
+ /**
+ * Returns the affine x-coordinate after checking that this point is normalized.
+ *
+ * @return The affine x-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ public ECFieldElement getAffineXCoord()
+ {
+ checkNormalized();
+ return getXCoord();
+ }
+
+ /**
+ * Returns the affine y-coordinate after checking that this point is normalized
+ *
+ * @return The affine y-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ public ECFieldElement getAffineYCoord()
+ {
+ checkNormalized();
+ return getYCoord();
+ }
+
+ /**
+ * Returns the x-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineXCoord if you expect the point to already have been
+ * normalized.
+ *
+ * @return the x-coordinate of this point
+ */
+ public ECFieldElement getXCoord()
+ {
+ return x;
+ }
+
+ /**
+ * Returns the y-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineYCoord if you expect the point to already have been
+ * normalized.
+ *
+ * @return the y-coordinate of this point
+ */
+ public ECFieldElement getYCoord()
+ {
+ return y;
+ }
+
+ public ECFieldElement getZCoord(int index)
+ {
+ return (index < 0 || index >= zs.length) ? null : zs[index];
+ }
+
+ public ECFieldElement[] getZCoords()
+ {
+ int zsLen = zs.length;
+ if (zsLen == 0)
+ {
+ return zs;
+ }
+ ECFieldElement[] copy = new ECFieldElement[zsLen];
+ System.arraycopy(zs, 0, copy, 0, zsLen);
+ return copy;
+ }
+
+ protected ECFieldElement getRawXCoord()
+ {
+ return x;
+ }
+
+ protected ECFieldElement getRawYCoord()
+ {
+ return y;
+ }
+
+ protected void checkNormalized()
+ {
+ if (!isNormalized())
+ {
+ throw new IllegalStateException("point not in normal form");
+ }
+ }
+
+ public boolean isNormalized()
+ {
+ int coord = this.getCurveCoordinateSystem();
+
+ return coord == ECCurve.COORD_AFFINE
+ || coord == ECCurve.COORD_LAMBDA_AFFINE
+ || isInfinity()
+ || zs[0].bitLength() == 1;
+ }
+
+ /**
+ * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+ * coordinates reflect those of the equivalent point in an affine coordinate system.
+ *
+ * @return a new ECPoint instance representing the same point, but with normalized coordinates
+ */
+ public ECPoint normalize()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_AFFINE:
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ {
+ return this;
+ }
+ default:
+ {
+ ECFieldElement Z1 = getZCoord(0);
+ if (Z1.bitLength() == 1)
+ {
+ return this;
+ }
+
+ return normalize(Z1.invert());
+ }
+ }
+ }
+
+ ECPoint normalize(ECFieldElement zInv)
+ {
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_HOMOGENEOUS:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ return createScaledPoint(zInv, zInv);
+ }
+ case ECCurve.COORD_JACOBIAN:
+ case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ ECFieldElement zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv);
+ return createScaledPoint(zInv2, zInv3);
+ }
+ default:
+ {
+ throw new IllegalStateException("not a projective coordinate system");
+ }
+ }
+ }
+
+ protected ECPoint createScaledPoint(ECFieldElement sx, ECFieldElement sy)
+ {
+ return this.getCurve().createRawPoint(getRawXCoord().multiply(sx), getRawYCoord().multiply(sy), this.withCompression);
+ }
+
+ public boolean isInfinity()
+ {
+ return x == null || y == null || (zs.length > 0 && zs[0].isZero());
+ }
+
+ public boolean isCompressed()
+ {
+ return this.withCompression;
+ }
+
+ public boolean equals(ECPoint other)
+ {
+ if (null == other)
+ {
+ return false;
+ }
+
+ ECCurve c1 = this.getCurve(), c2 = other.getCurve();
+ boolean n1 = (null == c1), n2 = (null == c2);
+ boolean i1 = isInfinity(), i2 = other.isInfinity();
+
+ if (i1 || i2)
+ {
+ return (i1 && i2) && (n1 || n2 || c1.equals(c2));
+ }
+
+ ECPoint p1 = this, p2 = other;
+ if (n1 && n2)
+ {
+ // Points with null curve are in affine form, so already normalized
+ }
+ else if (n1)
+ {
+ p2 = p2.normalize();
+ }
+ else if (n2)
+ {
+ p1 = p1.normalize();
+ }
+ else if (!c1.equals(c2))
+ {
+ return false;
+ }
+ else
+ {
+ // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+ ECPoint[] points = new ECPoint[]{ this, c1.importPoint(p2) };
+
+ // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal
+ c1.normalizeAll(points);
+
+ p1 = points[0];
+ p2 = points[1];
+ }
+
+ return p1.getXCoord().equals(p2.getXCoord()) && p1.getYCoord().equals(p2.getYCoord());
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof ECPoint))
+ {
+ return false;
+ }
+
+ return equals((ECPoint)other);
+ }
+
+ public int hashCode()
+ {
+ ECCurve c = this.getCurve();
+ int hc = (null == c) ? 0 : ~c.hashCode();
+
+ if (!this.isInfinity())
+ {
+ // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+ ECPoint p = normalize();
+
+ hc ^= p.getXCoord().hashCode() * 17;
+ hc ^= p.getYCoord().hashCode() * 257;
+ }
+
+ return hc;
+ }
+
+ public String toString()
+ {
+ if (this.isInfinity())
+ {
+ return "INF";
+ }
+
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ sb.append(getRawXCoord());
+ sb.append(',');
+ sb.append(getRawYCoord());
+ for (int i = 0; i < zs.length; ++i)
+ {
+ sb.append(',');
+ sb.append(zs[i]);
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ public byte[] getEncoded()
+ {
+ return getEncoded(this.withCompression);
+ }
+
+ /**
+ * return the field element encoded with point compression. (S 4.3.6)
+ */
+ public byte[] getEncoded(boolean compressed)
+ {
+ if (this.isInfinity())
+ {
+ return new byte[1];
+ }
+
+ ECPoint normed = normalize();
+
+ byte[] X = normed.getXCoord().getEncoded();
+
+ if (compressed)
+ {
+ byte[] PO = new byte[X.length + 1];
+ PO[0] = (byte)(normed.getCompressionYTilde() ? 0x03 : 0x02);
+ System.arraycopy(X, 0, PO, 1, X.length);
+ return PO;
+ }
+
+ byte[] Y = normed.getYCoord().getEncoded();
+
+ byte[] PO = new byte[X.length + Y.length + 1];
+ PO[0] = 0x04;
+ System.arraycopy(X, 0, PO, 1, X.length);
+ System.arraycopy(Y, 0, PO, X.length + 1, Y.length);
+ return PO;
+ }
+
+ protected abstract boolean getCompressionYTilde();
+
+ public abstract ECPoint add(ECPoint b);
+
+ public abstract ECPoint negate();
+
+ public abstract ECPoint subtract(ECPoint b);
+
+ public ECPoint timesPow2(int e)
+ {
+ if (e < 0)
+ {
+ throw new IllegalArgumentException("'e' cannot be negative");
+ }
+
+ ECPoint p = this;
+ while (--e >= 0)
+ {
+ p = p.twice();
+ }
+ return p;
+ }
+
+ public abstract ECPoint twice();
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ return twice().add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ return twicePlus(this);
+ }
+
+ /**
+ * Multiplies this ECPoint
by the given number.
+ * @param k The multiplicator.
+ * @return k * this
.
+ */
+ public ECPoint multiply(BigInteger k)
+ {
+ return this.getCurve().getMultiplier().multiply(this, k);
+ }
+
+ /**
+ * Elliptic curve points over Fp
+ */
+ public static class Fp extends ECPoint
+ {
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ * @param withCompression if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x != null && y == null) || (x == null && y != null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ return this.getAffineYCoord().testBitZero();
+ }
+
+ public ECFieldElement getZCoord(int index)
+ {
+ if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.getCurveCoordinateSystem())
+ {
+ return getJacobianModifiedW();
+ }
+
+ return super.getZCoord(index);
+ }
+
+ // B.3 pg 62
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x, Y1 = this.y;
+ ECFieldElement X2 = b.x, Y2 = b.y;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1);
+
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ // this == b, i.e. this must be doubled
+ return twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ ECFieldElement gamma = dy.divide(dx);
+ ECFieldElement X3 = gamma.square().subtract(X1).subtract(X2);
+ ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1);
+
+ return new ECPoint.Fp(curve, X3, Y3, this.withCompression);
+ }
+
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Z1 = this.zs[0];
+ ECFieldElement Z2 = b.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+ boolean Z2IsOne = Z2.bitLength() == 1;
+
+ ECFieldElement u1 = Z1IsOne ? Y2 : Y2.multiply(Z1);
+ ECFieldElement u2 = Z2IsOne ? Y1 : Y1.multiply(Z2);
+ ECFieldElement u = u1.subtract(u2);
+ ECFieldElement v1 = Z1IsOne ? X2 : X2.multiply(Z1);
+ ECFieldElement v2 = Z2IsOne ? X1 : X1.multiply(Z2);
+ ECFieldElement v = v1.subtract(v2);
+
+ // Check if b == this or b == -this
+ if (v.isZero())
+ {
+ if (u.isZero())
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ // TODO Optimize for when w == 1
+ ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.multiply(Z2);
+ ECFieldElement vSquared = v.square();
+ ECFieldElement vCubed = vSquared.multiply(v);
+ ECFieldElement vSquaredV2 = vSquared.multiply(v2);
+ ECFieldElement A = u.square().multiply(w).subtract(vCubed).subtract(two(vSquaredV2));
+
+ ECFieldElement X3 = v.multiply(A);
+ ECFieldElement Y3 = vSquaredV2.subtract(A).multiply(u).subtract(vCubed.multiply(u2));
+ ECFieldElement Z3 = vCubed.multiply(w);
+
+ return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ case ECCurve.COORD_JACOBIAN:
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ ECFieldElement Z1 = this.zs[0];
+ ECFieldElement Z2 = b.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+
+ ECFieldElement X3, Y3, Z3, Z3Squared = null;
+
+ if (!Z1IsOne && Z1.equals(Z2))
+ {
+ // TODO Make this available as public method coZAdd?
+
+ ECFieldElement dx = X1.subtract(X2), dy = Y1.subtract(Y2);
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ return twice();
+ }
+ return curve.getInfinity();
+ }
+
+ ECFieldElement C = dx.square();
+ ECFieldElement W1 = X1.multiply(C), W2 = X2.multiply(C);
+ ECFieldElement A1 = W1.subtract(W2).multiply(Y1);
+
+ X3 = dy.square().subtract(W1).subtract(W2);
+ Y3 = W1.subtract(X3).multiply(dy).subtract(A1);
+ Z3 = dx;
+
+ if (Z1IsOne)
+ {
+ Z3Squared = C;
+ }
+ else
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+ else
+ {
+ ECFieldElement Z1Squared, U2, S2;
+ if (Z1IsOne)
+ {
+ Z1Squared = Z1; U2 = X2; S2 = Y2;
+ }
+ else
+ {
+ Z1Squared = Z1.square();
+ U2 = Z1Squared.multiply(X2);
+ ECFieldElement Z1Cubed = Z1Squared.multiply(Z1);
+ S2 = Z1Cubed.multiply(Y2);
+ }
+
+ boolean Z2IsOne = Z2.bitLength() == 1;
+ ECFieldElement Z2Squared, U1, S1;
+ if (Z2IsOne)
+ {
+ Z2Squared = Z2; U1 = X1; S1 = Y1;
+ }
+ else
+ {
+ Z2Squared = Z2.square();
+ U1 = Z2Squared.multiply(X1);
+ ECFieldElement Z2Cubed = Z2Squared.multiply(Z2);
+ S1 = Z2Cubed.multiply(Y1);
+ }
+
+ ECFieldElement H = U1.subtract(U2);
+ ECFieldElement R = S1.subtract(S2);
+
+ // Check if b == this or b == -this
+ if (H.isZero())
+ {
+ if (R.isZero())
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ ECFieldElement HSquared = H.square();
+ ECFieldElement G = HSquared.multiply(H);
+ ECFieldElement V = HSquared.multiply(U1);
+
+ X3 = R.square().add(G).subtract(two(V));
+ Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G));
+
+ Z3 = H;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ if (!Z2IsOne)
+ {
+ Z3 = Z3.multiply(Z2);
+ }
+
+ // Alternative calculation of Z3 using fast square
+ // X3 = four(X3);
+ // Y3 = eight(Y3);
+ // Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).multiply(H);
+
+ if (Z3 == H)
+ {
+ Z3Squared = HSquared;
+ }
+ }
+
+ ECFieldElement[] zs;
+ if (coord == ECCurve.COORD_JACOBIAN_MODIFIED)
+ {
+ // TODO If the result will only be used in a subsequent addition, we don't need W3
+ ECFieldElement W3 = calculateJacobianModifiedW(Z3, Z3Squared);
+
+ zs = new ECFieldElement[]{ Z3, W3 };
+ }
+ else
+ {
+ zs = new ECFieldElement[]{ Z3 };
+ }
+
+ return new ECPoint.Fp(curve, X3, Y3, zs, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ // B.3 pg 62
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement X1Squared = X1.square();
+ ECFieldElement gamma = three(X1Squared).add(this.getCurve().getA()).divide(two(Y1));
+ ECFieldElement X3 = gamma.square().subtract(two(X1));
+ ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1);
+
+ return new ECPoint.Fp(curve, X3, Y3, this.withCompression);
+ }
+
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+ ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square();
+
+ // TODO Optimize for small negative a4 and -3
+ ECFieldElement w = curve.getA();
+ if (!Z1IsOne)
+ {
+ w = w.multiply(Z1Squared);
+ }
+ w = w.add(three(X1.square()));
+
+ ECFieldElement s = Z1IsOne ? Y1 : Y1.multiply(Z1);
+ ECFieldElement t = Z1IsOne ? Y1.square() : s.multiply(Y1);
+ ECFieldElement B = X1.multiply(t);
+ ECFieldElement _4B = four(B);
+ ECFieldElement h = w.square().subtract(two(_4B));
+
+ ECFieldElement X3 = two(h.multiply(s));
+ ECFieldElement Y3 = w.multiply(_4B.subtract(h)).subtract(two(two(t).square()));
+ ECFieldElement _4sSquared = Z1IsOne ? four(t) : two(s).square();
+ ECFieldElement Z3 = two(_4sSquared).multiply(s);
+
+ return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ case ECCurve.COORD_JACOBIAN:
+ {
+ ECFieldElement Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+ ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square();
+
+ ECFieldElement Y1Squared = Y1.square();
+ ECFieldElement T = Y1Squared.square();
+
+ ECFieldElement a4 = curve.getA();
+ ECFieldElement a4Neg = a4.negate();
+
+ ECFieldElement M, S;
+ if (a4Neg.toBigInteger().equals(BigInteger.valueOf(3)))
+ {
+ M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared)));
+ S = four(Y1Squared.multiply(X1));
+ }
+ else
+ {
+ ECFieldElement X1Squared = X1.square();
+ M = three(X1Squared);
+ if (Z1IsOne)
+ {
+ M = M.add(a4);
+ }
+ else
+ {
+ ECFieldElement Z1Pow4 = Z1Squared.square();
+ if (a4Neg.bitLength() < a4.bitLength())
+ {
+ M = M.subtract(Z1Pow4.multiply(a4Neg));
+ }
+ else
+ {
+ M = M.add(Z1Pow4.multiply(a4));
+ }
+ }
+ S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+ }
+
+ ECFieldElement X3 = M.square().subtract(two(S));
+ ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T));
+
+ ECFieldElement Z3 = two(Y1);
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+
+ // Alternative calculation of Z3 using fast square
+// ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared);
+
+ return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ return twiceJacobianModified(true);
+ }
+
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this == b)
+ {
+ return threeTimes();
+ }
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return b;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.x, Y2 = b.y;
+
+ ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1);
+
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ // this == b i.e. the result is 3P
+ return threeTimes();
+ }
+
+ // this == -b, i.e. the result is P
+ return this;
+ }
+
+ /*
+ * Optimized calculation of 2P + Q, as described in "Trading Inversions for
+ * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery.
+ */
+
+ ECFieldElement X = dx.square(), Y = dy.square();
+ ECFieldElement d = X.multiply(two(X1).add(X2)).subtract(Y);
+ if (d.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ ECFieldElement D = d.multiply(dx);
+ ECFieldElement I = D.invert();
+ ECFieldElement L1 = d.multiply(I).multiply(dy);
+ ECFieldElement L2 = two(Y1).multiply(X).multiply(dx).multiply(I).subtract(L1);
+ ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X2);
+ ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1);
+
+ return new ECPoint.Fp(curve, X4, Y4, this.withCompression);
+ }
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ return twiceJacobianModified(false).add(b);
+ }
+ default:
+ {
+ return twice().add(b);
+ }
+ }
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity() || this.y.isZero())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement X1 = this.x, Y1 = this.y;
+
+ ECFieldElement _2Y1 = two(Y1);
+ ECFieldElement X = _2Y1.square();
+ ECFieldElement Z = three(X1.square()).add(this.getCurve().getA());
+ ECFieldElement Y = Z.square();
+
+ ECFieldElement d = three(X1).multiply(X).subtract(Y);
+ if (d.isZero())
+ {
+ return this.getCurve().getInfinity();
+ }
+
+ ECFieldElement D = d.multiply(_2Y1);
+ ECFieldElement I = D.invert();
+ ECFieldElement L1 = d.multiply(I).multiply(Z);
+ ECFieldElement L2 = X.square().multiply(I).subtract(L1);
+
+ ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X1);
+ ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1);
+ return new ECPoint.Fp(curve, X4, Y4, this.withCompression);
+ }
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ return twiceJacobianModified(false).add(this);
+ }
+ default:
+ {
+ // NOTE: Be careful about recursions between twicePlus and threeTimes
+ return twice().add(this);
+ }
+ }
+ }
+
+ protected ECFieldElement two(ECFieldElement x)
+ {
+ return x.add(x);
+ }
+
+ protected ECFieldElement three(ECFieldElement x)
+ {
+ return two(x).add(x);
+ }
+
+ protected ECFieldElement four(ECFieldElement x)
+ {
+ return two(two(x));
+ }
+
+ protected ECFieldElement eight(ECFieldElement x)
+ {
+ return four(two(x));
+ }
+
+ protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b,
+ ECFieldElement aSquared, ECFieldElement bSquared)
+ {
+ /*
+ * NOTE: If squaring in the field is faster than multiplication, then this is a quicker
+ * way to calculate 2.A.B, if A^2 and B^2 are already known.
+ */
+ return a.add(b).square().subtract(aSquared).subtract(bSquared);
+ }
+
+ // D.3.2 pg 102 (see Note:)
+ public ECPoint subtract(ECPoint b)
+ {
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ // Add -b
+ return add(b.negate());
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ if (ECCurve.COORD_AFFINE != coord)
+ {
+ return new ECPoint.Fp(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+
+ return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression);
+ }
+
+ protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared)
+ {
+ if (ZSquared == null)
+ {
+ ZSquared = Z.square();
+ }
+
+ ECFieldElement W = ZSquared.square();
+ ECFieldElement a4 = this.getCurve().getA();
+ ECFieldElement a4Neg = a4.negate();
+ if (a4Neg.bitLength() < a4.bitLength())
+ {
+ W = W.multiply(a4Neg).negate();
+ }
+ else
+ {
+ W = W.multiply(a4);
+ }
+ return W;
+ }
+
+ protected ECFieldElement getJacobianModifiedW()
+ {
+ ECFieldElement W = this.zs[1];
+ if (W == null)
+ {
+ // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here
+ this.zs[1] = W = calculateJacobianModifiedW(this.zs[0], null);
+ }
+ return W;
+ }
+
+ protected ECPoint.Fp twiceJacobianModified(boolean calculateW)
+ {
+ ECFieldElement X1 = this.x, Y1 = this.y, Z1 = this.zs[0], W1 = getJacobianModifiedW();
+
+ ECFieldElement X1Squared = X1.square();
+ ECFieldElement M = three(X1Squared).add(W1);
+ ECFieldElement Y1Squared = Y1.square();
+ ECFieldElement T = Y1Squared.square();
+ ECFieldElement S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+ ECFieldElement X3 = M.square().subtract(two(S));
+ ECFieldElement _8T = eight(T);
+ ECFieldElement Y3 = M.multiply(S.subtract(X3)).subtract(_8T);
+ ECFieldElement W3 = calculateW ? two(_8T.multiply(W1)) : null;
+ ECFieldElement Z3 = two(Z1.bitLength() == 1 ? Y1 : Y1.multiply(Z1));
+
+ return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression);
+ }
+ }
+
+ /**
+ * Elliptic curve points over F2m
+ */
+ public static class F2m extends ECPoint
+ {
+ /**
+ * @param curve base curve
+ * @param x x point
+ * @param y y point
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @param curve base curve
+ * @param x x point
+ * @param y y point
+ * @param withCompression true if encode with point compression.
+ *
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x != null && y == null) || (x == null && y != null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ if (x != null)
+ {
+ // Check if x and y are elements of the same field
+ ECFieldElement.F2m.checkFieldElements(this.x, this.y);
+
+ // Check if x and a are elements of the same field
+ if (curve != null)
+ {
+ ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA());
+ }
+ }
+
+ this.withCompression = withCompression;
+
+// checkCurveEquation();
+ }
+
+ F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+
+// checkCurveEquation();
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ int coord = this.getCurveCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // TODO The X == 0 stuff needs further thought
+ if (this.isInfinity() || x.isZero())
+ {
+ return y;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement X = x, L = y;
+ ECFieldElement Y = L.subtract(X).multiply(X);
+ if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord)
+ {
+ ECFieldElement Z = zs[0];
+ if (Z.bitLength() != 1)
+ {
+ Y = Y.divide(Z);
+ }
+ }
+ return Y;
+ }
+ default:
+ {
+ return y;
+ }
+ }
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // Y is actually Lambda (X + Y/X) here
+ return Y.subtract(X).testBitZero();
+ }
+ default:
+ {
+ return Y.divide(X).testBitZero();
+ }
+ }
+ }
+
+ /**
+ * Check, if two ECPoint
s can be added or subtracted.
+ * @param a The first ECPoint
to check.
+ * @param b The second ECPoint
to check.
+ * @throws IllegalArgumentException if a
and b
+ * cannot be added.
+ */
+ private static void checkPoints(ECPoint a, ECPoint b)
+ {
+ // Check, if points are on the same curve
+ if (a.curve != b.curve)
+ {
+ throw new IllegalArgumentException("Only points on the same "
+ + "curve can be added or subtracted");
+ }
+
+// ECFieldElement.F2m.checkFieldElements(a.x, b.x);
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.math.ec.ECPoint#add(org.spongycastle.math.ec.ECPoint)
+ */
+ public ECPoint add(ECPoint b)
+ {
+ checkPoints(this, b);
+ return addSimple((ECPoint.F2m)b);
+ }
+
+ /**
+ * Adds another ECPoints.F2m
to this
without
+ * checking if both points are on the same curve. Used by multiplication
+ * algorithms, because there all points are a multiple of the same point
+ * and hence the checks can be omitted.
+ * @param b The other ECPoints.F2m
to add to
+ * this
.
+ * @return this + b
+ */
+ public ECPoint.F2m addSimple(ECPoint.F2m b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.x;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement Y1 = this.y;
+ ECFieldElement Y2 = b.y;
+
+ if (X1.equals(X2))
+ {
+ if (Y1.equals(Y2))
+ {
+ return (ECPoint.F2m)twice();
+ }
+
+ return (ECPoint.F2m)curve.getInfinity();
+ }
+
+ ECFieldElement sumX = X1.add(X2);
+ ECFieldElement L = Y1.add(Y2).divide(sumX);
+
+ ECFieldElement X3 = L.square().add(L).add(sumX).add(curve.getA());
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+
+ return new ECPoint.F2m(curve, X3, Y3, this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+ ECFieldElement Y2 = b.y, Z2 = b.zs[0];
+
+ boolean Z2IsOne = Z2.bitLength() == 1;
+
+ ECFieldElement U1 = Z1.multiply(Y2);
+ ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2);
+ ECFieldElement U = U1.subtract(U2);
+ ECFieldElement V1 = Z1.multiply(X2);
+ ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2);
+ ECFieldElement V = V1.subtract(V2);
+
+ if (V1.equals(V2))
+ {
+ if (U1.equals(U2))
+ {
+ return (ECPoint.F2m)twice();
+ }
+
+ return (ECPoint.F2m)curve.getInfinity();
+ }
+
+ ECFieldElement VSq = V.square();
+ ECFieldElement W = Z2IsOne ? Z1 : Z1.multiply(Z2);
+ ECFieldElement A = U.square().add(U.multiply(V).add(VSq.multiply(curve.getA()))).multiply(W).add(V.multiply(VSq));
+
+ ECFieldElement X3 = V.multiply(A);
+ ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.multiply(Z2);
+ ECFieldElement Y3 = VSqZ2.multiply(U.multiply(X1).add(Y1.multiply(V))).add(A.multiply(U.add(V)));
+ ECFieldElement Z3 = VSq.multiply(V).multiply(W);
+
+ return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ if (X1.isZero())
+ {
+ return b.addSimple(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.y, Z2 = b.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.bitLength() == 1;
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return (ECPoint.F2m)twice();
+ }
+
+ return (ECPoint.F2m)curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+
+ ECFieldElement Y1 = getYCoord(), Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = X3.isZero() ? Y3 : Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ X3 = AU1.multiply(AU2);
+ L3 = AU2.add(B).square().add(ABZ2.multiply(L1.add(Z1)));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.math.ec.ECPoint#subtract(org.spongycastle.math.ec.ECPoint)
+ */
+ public ECPoint subtract(ECPoint b)
+ {
+ checkPoints(this, b);
+ return subtractSimple((ECPoint.F2m)b);
+ }
+
+ /**
+ * Subtracts another ECPoints.F2m
from this
+ * without checking if both points are on the same curve. Used by
+ * multiplication algorithms, because there all points are a multiple
+ * of the same point and hence the checks can be omitted.
+ * @param b The other ECPoints.F2m
to subtract from
+ * this
.
+ * @return this - b
+ */
+ public ECPoint.F2m subtractSimple(ECPoint.F2m b)
+ {
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ // Add -b
+ return addSimple((ECPoint.F2m)b.negate());
+ }
+
+ public ECPoint.F2m tau()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ {
+ ECFieldElement Y1 = this.y;
+ return new ECPoint.F2m(curve, X1.square(), Y1.square(), this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+ return new ECPoint.F2m(curve, X1.square(), Y1.square(), new ECFieldElement[]{ Z1.square() }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement Y1 = this.y;
+
+ ECFieldElement L1 = Y1.divide(X1).add(X1);
+
+ ECFieldElement X3 = L1.square().add(L1).add(curve.getA());
+ ECFieldElement Y3 = X1.square().add(X3.multiply(L1.addOne()));
+
+ return new ECPoint.F2m(curve, X3, Y3, this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1);
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement S = X1Sq.add(Y1Z1);
+ ECFieldElement V = X1Z1;
+ ECFieldElement vSquared = V.square();
+ ECFieldElement h = S.square().add(S.multiply(V)).add(curve.getA().multiply(vSquared));
+
+ ECFieldElement X3 = V.multiply(h);
+ ECFieldElement Y3 = h.multiply(S.add(V)).add(X1Sq.square().multiply(V));
+ ECFieldElement Z3 = V.multiply(vSquared);
+
+ return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.bitLength() == 1;
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement b = curve.getB();
+ ECFieldElement L3;
+ if (b.bitLength() < (curve.getFieldSize() >> 1))
+ {
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement t2 = aZ1Sq.square();
+ ECFieldElement t3 = curve.getB().multiply(Z1Sq.square());
+ L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2.add(t3)).add(X3).add(a.addOne().multiply(Z3));
+ }
+ else
+ {
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ L3 = X1Z1.square().add(X3).add(T.multiply(L1Z1)).add(Z3);
+ }
+
+ return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.x, Z2 = b.zs[0];
+ if (X2.isZero() || Z2.bitLength() != 1)
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.y;
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiply(T).add(X1Sq.multiply(Z1Sq));
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiply(T).add(L2plus1.multiply(Z3));
+
+ return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ default:
+ {
+ return twice().add(b);
+ }
+ }
+ }
+
+ protected void checkCurveEquation()
+ {
+ if (this.isInfinity())
+ {
+ return;
+ }
+
+ ECFieldElement Z;
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ Z = curve.fromBigInteger(ECConstants.ONE);
+ break;
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ Z = this.zs[0];
+ break;
+ default:
+ return;
+ }
+
+ if (Z.isZero())
+ {
+ throw new IllegalStateException();
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ // NOTE: For x == 0, we expect the affine-y instead of the lambda-y
+ ECFieldElement Y = this.y;
+ if (!Y.square().equals(curve.getB().multiply(Z)))
+ {
+ throw new IllegalStateException();
+ }
+
+ return;
+ }
+
+ ECFieldElement L = this.y;
+ ECFieldElement XSq = X.square();
+ ECFieldElement ZSq = Z.square();
+
+ ECFieldElement lhs = L.square().add(L.multiply(Z)).add(this.getCurve().getA().multiply(ZSq)).multiply(XSq);
+ ECFieldElement rhs = ZSq.square().multiply(this.getCurve().getB()).add(XSq.square());
+
+ if (!lhs.equals(rhs))
+ {
+ throw new IllegalStateException("F2m Lambda-Projective invariant broken");
+ }
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement Y = this.y;
+ return new ECPoint.F2m(curve, X, Y.add(X), this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Y = this.y, Z = this.zs[0];
+ return new ECPoint.F2m(curve, X, Y.add(X), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ {
+ ECFieldElement L = this.y;
+ return new ECPoint.F2m(curve, X, L.addOne(), this.withCompression);
+ }
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new ECPoint.F2m(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/IntArray.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/IntArray.java
new file mode 100644
index 000000000..f86cfbc16
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/IntArray.java
@@ -0,0 +1,860 @@
+package org.spongycastle.math.ec;
+
+import org.spongycastle.util.Arrays;
+
+import java.math.BigInteger;
+
+class IntArray
+{
+// private static int DEINTERLEAVE_MASK = 0x55555555;
+
+ /*
+ * This expands 8 bit indices into 16 bit contents, by inserting 0s between bits.
+ * In a binary field, this operation is the same as squaring an 8 bit number.
+ */
+ private static final int[] INTERLEAVE_TABLE = new int[] { 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014,
+ 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, 0x0105, 0x0110,
+ 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404,
+ 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500,
+ 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554,
+ 0x0555, 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044, 0x1045, 0x1050,
+ 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144,
+ 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440,
+ 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514,
+ 0x1515, 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004, 0x4005, 0x4010,
+ 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104,
+ 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400,
+ 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454,
+ 0x4455, 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544, 0x4545, 0x4550,
+ 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044,
+ 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140,
+ 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414,
+ 0x5415, 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504, 0x5505, 0x5510,
+ 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555 };
+
+ // For toString(); must have length 32
+ private static final String ZEROES = "00000000000000000000000000000000";
+
+ private final static byte[] bitLengths =
+ {
+ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
+ };
+
+ public static int getWordLength(int bits)
+ {
+ return (bits + 31) >>> 5;
+ }
+
+ // TODO make m fixed for the IntArray, and hence compute T once and for all
+
+ private int[] m_ints;
+
+ public IntArray(int intLen)
+ {
+ m_ints = new int[intLen];
+ }
+
+ public IntArray(int[] ints)
+ {
+ m_ints = ints;
+ }
+
+ public IntArray(BigInteger bigInt)
+ {
+ if (bigInt == null || bigInt.signum() < 0)
+ {
+ throw new IllegalArgumentException("invalid F2m field value");
+ }
+
+ if (bigInt.signum() == 0)
+ {
+ m_ints = new int[] { 0 };
+ return;
+ }
+
+ byte[] barr = bigInt.toByteArray();
+ int barrLen = barr.length;
+ int barrStart = 0;
+ if (barr[0] == 0)
+ {
+ // First byte is 0 to enforce highest (=sign) bit is zero.
+ // In this case ignore barr[0].
+ barrLen--;
+ barrStart = 1;
+ }
+ int intLen = (barrLen + 3) / 4;
+ m_ints = new int[intLen];
+
+ int iarrJ = intLen - 1;
+ int rem = barrLen % 4 + barrStart;
+ int temp = 0;
+ int barrI = barrStart;
+ if (barrStart < rem)
+ {
+ for (; barrI < rem; barrI++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI] & 0xFF;
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ--] = temp;
+ }
+
+ for (; iarrJ >= 0; iarrJ--)
+ {
+ temp = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI++] & 0xFF;
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ] = temp;
+ }
+ }
+
+ public boolean isZero()
+ {
+ int[] a = m_ints;
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int getUsedLength()
+ {
+ return getUsedLengthFrom(m_ints.length);
+ }
+
+ public int getUsedLengthFrom(int from)
+ {
+ int[] a = m_ints;
+ from = Math.min(from, a.length);
+
+ if (from < 1)
+ {
+ return 0;
+ }
+
+ // Check if first element will act as sentinel
+ if (a[0] != 0)
+ {
+ while (a[--from] == 0)
+ {
+ }
+ return from + 1;
+ }
+
+ do
+ {
+ if (a[--from] != 0)
+ {
+ return from + 1;
+ }
+ }
+ while (from > 0);
+
+ return 0;
+ }
+
+ public int degree()
+ {
+ int i = m_ints.length, w;
+ do
+ {
+ if (i == 0)
+ {
+ return 0;
+ }
+ w = m_ints[--i];
+ }
+ while (w == 0);
+
+ return (i << 5) + bitLength(w);
+ }
+
+ private static int bitLength(int w)
+ {
+ int t = w >>> 16;
+ if (t == 0)
+ {
+ t = w >>> 8;
+ return (t == 0) ? bitLengths[w] : 8 + bitLengths[t];
+ }
+
+ int u = t >>> 8;
+ return (u == 0) ? 16 + bitLengths[t] : 24 + bitLengths[u];
+ }
+
+ private int[] resizedInts(int newLen)
+ {
+ int[] newInts = new int[newLen];
+ System.arraycopy(m_ints, 0, newInts, 0, Math.min(m_ints.length, newLen));
+ return newInts;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ int usedLen = getUsedLength();
+ if (usedLen == 0)
+ {
+ return ECConstants.ZERO;
+ }
+
+ int highestInt = m_ints[usedLen - 1];
+ byte[] temp = new byte[4];
+ int barrI = 0;
+ boolean trailingZeroBytesDone = false;
+ for (int j = 3; j >= 0; j--)
+ {
+ byte thisByte = (byte) (highestInt >>> (8 * j));
+ if (trailingZeroBytesDone || (thisByte != 0))
+ {
+ trailingZeroBytesDone = true;
+ temp[barrI++] = thisByte;
+ }
+ }
+
+ int barrLen = 4 * (usedLen - 1) + barrI;
+ byte[] barr = new byte[barrLen];
+ for (int j = 0; j < barrI; j++)
+ {
+ barr[j] = temp[j];
+ }
+ // Highest value int is done now
+
+ for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+ {
+ for (int j = 3; j >= 0; j--)
+ {
+ barr[barrI++] = (byte) (m_ints[iarrJ] >>> (8 * j));
+ }
+ }
+ return new BigInteger(1, barr);
+ }
+
+ private static int shiftLeft(int[] x, int count)
+ {
+ int prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ int next = x[i];
+ x[i] = (next << 1) | prev;
+ prev = next >>> 31;
+ }
+ return prev;
+ }
+
+ public void addOneShifted(int shift)
+ {
+ if (shift >= m_ints.length)
+ {
+ m_ints = resizedInts(shift + 1);
+ }
+
+ m_ints[shift] ^= 1;
+ }
+
+ private void addShiftedByBits(IntArray other, int bits)
+ {
+ int words = bits >>> 5;
+ int shift = bits & 0x1F;
+
+ if (shift == 0)
+ {
+ addShiftedByWords(other, words);
+ return;
+ }
+
+ int otherUsedLen = other.getUsedLength();
+ if (otherUsedLen == 0)
+ {
+ return;
+ }
+
+ int minLen = otherUsedLen + words + 1;
+ if (minLen > m_ints.length)
+ {
+ m_ints = resizedInts(minLen);
+ }
+
+ int shiftInv = 32 - shift, prev = 0;
+ for (int i = 0; i < otherUsedLen; ++i)
+ {
+ int next = other.m_ints[i];
+ m_ints[i + words] ^= (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ m_ints[otherUsedLen + words] ^= prev;
+ }
+
+ private static int addShiftedByBits(int[] x, int[] y, int count, int shift)
+ {
+ int shiftInv = 32 - shift, prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ int next = y[i];
+ x[i] ^= (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ private static int addShiftedByBits(int[] x, int xOff, int[] y, int yOff, int count, int shift)
+ {
+ int shiftInv = 32 - shift, prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ int next = y[yOff + i];
+ x[xOff + i] ^= (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ public void addShiftedByWords(IntArray other, int words)
+ {
+ int otherUsedLen = other.getUsedLength();
+ if (otherUsedLen == 0)
+ {
+ return;
+ }
+
+ int minLen = otherUsedLen + words;
+ if (minLen > m_ints.length)
+ {
+ m_ints = resizedInts(minLen);
+ }
+
+ for (int i = 0; i < otherUsedLen; i++)
+ {
+ m_ints[words + i] ^= other.m_ints[i];
+ }
+ }
+
+ private static void addShiftedByWords(int[] x, int xOff, int[] y, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ x[xOff + i] ^= y[i];
+ }
+ }
+
+ private static void add(int[] x, int[] y, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ x[i] ^= y[i];
+ }
+ }
+
+ private static void distribute(int[] x, int dst1, int dst2, int src, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ int v = x[src + i];
+ x[dst1 + i] ^= v;
+ x[dst2 + i] ^= v;
+ }
+ }
+
+ public int getLength()
+ {
+ return m_ints.length;
+ }
+
+ public void flipWord(int bit, int word)
+ {
+ int len = m_ints.length;
+ int n = bit >>> 5;
+ if (n < len)
+ {
+ int shift = bit & 0x1F;
+ if (shift == 0)
+ {
+ m_ints[n] ^= word;
+ }
+ else
+ {
+ m_ints[n] ^= word << shift;
+ if (++n < len)
+ {
+ m_ints[n] ^= word >>> (32 - shift);
+ }
+ }
+ }
+ }
+
+ public int getWord(int bit)
+ {
+ int len = m_ints.length;
+ int n = bit >>> 5;
+ if (n >= len)
+ {
+ return 0;
+ }
+ int shift = bit & 0x1F;
+ if (shift == 0)
+ {
+ return m_ints[n];
+ }
+ int result = m_ints[n] >>> shift;
+ if (++n < len)
+ {
+ result |= m_ints[n] << (32 - shift);
+ }
+ return result;
+ }
+
+ public boolean testBitZero()
+ {
+ return m_ints.length > 0 && (m_ints[0] & 1) != 0;
+ }
+
+ public boolean testBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >>> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int tester = 1 << theBit;
+ return ((m_ints[theInt] & tester) != 0);
+ }
+
+ public void flipBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >>> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int flipper = 1 << theBit;
+ m_ints[theInt] ^= flipper;
+ }
+
+ public void setBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >>> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int setter = 1 << theBit;
+ m_ints[theInt] |= setter;
+ }
+
+ public void clearBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >>> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int setter = 1 << theBit;
+ m_ints[theInt] &= ~setter;
+ }
+
+ public IntArray multiply(IntArray other, int m)
+ {
+ int aLen = getUsedLength();
+ if (aLen == 0)
+ {
+ return new IntArray(1);
+ }
+
+ int bLen = other.getUsedLength();
+ if (bLen == 0)
+ {
+ return new IntArray(1);
+ }
+
+ IntArray A = this, B = other;
+ if (aLen > bLen)
+ {
+ A = other; B = this;
+ int tmp = aLen; aLen = bLen; bLen = tmp;
+ }
+
+ if (aLen == 1)
+ {
+ int a = A.m_ints[0];
+ int[] b = B.m_ints;
+ int[] c = new int[aLen + bLen];
+ if ((a & 1) != 0)
+ {
+ add(c, b, bLen);
+ }
+ int k = 1;
+ while ((a >>>= 1) != 0)
+ {
+ if ((a & 1) != 0)
+ {
+ addShiftedByBits(c, b, bLen, k);
+ }
+ ++k;
+ }
+ return new IntArray(c);
+ }
+
+ // TODO It'd be better to be able to tune the width directly (need support for interleaving arbitrary widths)
+ int complexity = aLen <= 8 ? 1 : 2;
+
+ int width = 1 << complexity;
+ int shifts = (32 >>> complexity);
+
+ int bExt = bLen;
+ if ((B.m_ints[bLen - 1] >>> (33 - shifts)) != 0)
+ {
+ ++bExt;
+ }
+
+ int cLen = bExt + aLen;
+
+ int[] c = new int[cLen << width];
+ System.arraycopy(B.m_ints, 0, c, 0, bLen);
+ interleave(A.m_ints, 0, c, bExt, aLen, complexity);
+
+ int[] ci = new int[1 << width];
+ for (int i = 1; i < ci.length; ++i)
+ {
+ ci[i] = ci[i - 1] + cLen;
+ }
+
+ int MASK = (1 << width) - 1;
+
+ int k = 0;
+ for (;;)
+ {
+ for (int aPos = 0; aPos < aLen; ++aPos)
+ {
+ int index = (c[bExt + aPos] >>> k) & MASK;
+ if (index != 0)
+ {
+ addShiftedByWords(c, aPos + ci[index], c, bExt);
+ }
+ }
+
+ if ((k += width) >= 32)
+ {
+ break;
+ }
+
+ shiftLeft(c, bExt);
+ }
+
+ int ciPos = ci.length, pow2 = ciPos >>> 1, offset = 32;
+ while (--ciPos > 1)
+ {
+ if (ciPos == pow2)
+ {
+ offset -= shifts;
+ addShiftedByBits(c, ci[1], c, ci[pow2], cLen, offset);
+ pow2 >>>= 1;
+ }
+ else
+ {
+ distribute(c, ci[pow2], ci[ciPos - pow2], ci[ciPos], cLen);
+ }
+ }
+
+ // TODO reduce in place to avoid extra copying
+ IntArray p = new IntArray(cLen);
+ System.arraycopy(c, ci[1], p.m_ints, 0, cLen);
+ return p;
+ }
+
+// private static void deInterleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds)
+// {
+// for (int i = 0; i < count; ++i)
+// {
+// z[zOff + i] = deInterleave(x[zOff + i], rounds);
+// }
+// }
+//
+// private static int deInterleave(int x, int rounds)
+// {
+// while (--rounds >= 0)
+// {
+// x = deInterleave16(x & DEINTERLEAVE_MASK) | (deInterleave16((x >>> 1) & DEINTERLEAVE_MASK) << 16);
+// }
+// return x;
+// }
+//
+// private static int deInterleave16(int x)
+// {
+// x = (x | (x >>> 1)) & 0x33333333;
+// x = (x | (x >>> 2)) & 0x0F0F0F0F;
+// x = (x | (x >>> 4)) & 0x00FF00FF;
+// x = (x | (x >>> 8)) & 0x0000FFFF;
+// return x;
+// }
+
+ public void reduce(int m, int[] ks)
+ {
+ int len = getUsedLength();
+ int mLen = (m + 31) >>> 5;
+ if (len < mLen)
+ {
+ return;
+ }
+
+ int _2m = m << 1;
+ int pos = Math.min(_2m - 2, (len << 5) - 1);
+
+ int kMax = ks[ks.length - 1];
+ if (kMax < m - 31)
+ {
+ reduceWordWise(pos, m, ks);
+ }
+ else
+ {
+ reduceBitWise(pos, m, ks);
+ }
+
+ // Instead of flipping the high bits in the loop, explicitly clear any partial word above m bits
+ int partial = m & 0x1F;
+ if (partial != 0)
+ {
+ m_ints[mLen - 1] &= (1 << partial) - 1;
+ }
+
+ if (len > mLen)
+ {
+ m_ints = resizedInts(mLen);
+ }
+ }
+
+ private void reduceBitWise(int from, int m, int[] ks)
+ {
+ for (int i = from; i >= m; --i)
+ {
+ if (testBit(i))
+ {
+// clearBit(i);
+ int bit = i - m;
+ flipBit(bit);
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipBit(ks[j] + bit);
+ }
+ }
+ }
+ }
+
+ private void reduceWordWise(int from, int m, int[] ks)
+ {
+ int pos = m + ((from - m) & ~0x1F);
+ for (int i = pos; i >= m; i -= 32)
+ {
+ int word = getWord(i);
+ if (word != 0)
+ {
+// flipWord(i);
+ int bit = i - m;
+ flipWord(bit, word);
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipWord(ks[j] + bit, word);
+ }
+ }
+ }
+ }
+
+ public IntArray square(int m)
+ {
+ int len = getUsedLength();
+ if (len == 0)
+ {
+ return this;
+ }
+
+ int _2len = len << 1;
+ int[] r = new int[_2len];
+
+ int pos = 0;
+ while (pos < _2len)
+ {
+ int mi = m_ints[pos >>> 1];
+ r[pos++] = interleave16(mi & 0xFFFF);
+ r[pos++] = interleave16(mi >>> 16);
+ }
+
+ return new IntArray(r);
+ }
+
+ private static void interleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave(x[xOff + i], rounds);
+ }
+ }
+
+ private static int interleave(int x, int rounds)
+ {
+ while (--rounds >= 0)
+ {
+ x = interleave16(x & 0xFFFF) | (interleave16(x >>> 16) << 1);
+ }
+ return x;
+ }
+
+ private static int interleave16(int n)
+ {
+ return INTERLEAVE_TABLE[n & 0xFF] | INTERLEAVE_TABLE[n >>> 8] << 16;
+ }
+
+ public IntArray modInverse(int m, int[] ks)
+ {
+ // Inversion in F2m using the extended Euclidean algorithm
+ // Input: A nonzero polynomial a(z) of degree at most m-1
+ // Output: a(z)^(-1) mod f(z)
+
+ int uzDegree = degree();
+ if (uzDegree == 1)
+ {
+ return this;
+ }
+
+ // u(z) := a(z)
+ IntArray uz = (IntArray)clone();
+
+ int t = getWordLength(m);
+
+ // v(z) := f(z)
+ IntArray vz = new IntArray(t);
+ vz.setBit(m);
+ vz.setBit(0);
+ vz.setBit(ks[0]);
+ if (ks.length > 1)
+ {
+ vz.setBit(ks[1]);
+ vz.setBit(ks[2]);
+ }
+
+ // g1(z) := 1, g2(z) := 0
+ IntArray g1z = new IntArray(t);
+ g1z.setBit(0);
+ IntArray g2z = new IntArray(t);
+
+ while (uzDegree != 0)
+ {
+ // j := deg(u(z)) - deg(v(z))
+ int j = uzDegree - vz.degree();
+
+ // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+ if (j < 0)
+ {
+ final IntArray uzCopy = uz;
+ uz = vz;
+ vz = uzCopy;
+
+ final IntArray g1zCopy = g1z;
+ g1z = g2z;
+ g2z = g1zCopy;
+
+ j = -j;
+ }
+
+ // u(z) := u(z) + z^j * v(z)
+ // Note, that no reduction modulo f(z) is required, because
+ // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+ // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+ // = deg(u(z))
+ // uz = uz.xor(vz.shiftLeft(j));
+ uz.addShiftedByBits(vz, j);
+ uzDegree = uz.degree();
+
+ // g1(z) := g1(z) + z^j * g2(z)
+// g1z = g1z.xor(g2z.shiftLeft(j));
+ if (uzDegree != 0)
+ {
+ g1z.addShiftedByBits(g2z, j);
+ }
+ }
+ return g2z;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof IntArray))
+ {
+ return false;
+ }
+ IntArray other = (IntArray) o;
+ int usedLen = getUsedLength();
+ if (other.getUsedLength() != usedLen)
+ {
+ return false;
+ }
+ for (int i = 0; i < usedLen; i++)
+ {
+ if (m_ints[i] != other.m_ints[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int usedLen = getUsedLength();
+ int hash = 1;
+ for (int i = 0; i < usedLen; i++)
+ {
+ hash *= 31;
+ hash ^= m_ints[i];
+ }
+ return hash;
+ }
+
+ public Object clone()
+ {
+ return new IntArray(Arrays.clone(m_ints));
+ }
+
+ public String toString()
+ {
+ int i = getUsedLength();
+ if (i == 0)
+ {
+ return "0";
+ }
+
+ StringBuffer sb = new StringBuffer(Integer.toBinaryString(m_ints[--i]));
+ while (--i >= 0)
+ {
+ String s = Integer.toBinaryString(m_ints[i]);
+
+ // Add leading zeroes, except for highest significant word
+ int len = s.length();
+ if (len < 32)
+ {
+ sb.append(ZEROES.substring(len));
+ }
+
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/LongArray.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/LongArray.java
new file mode 100644
index 000000000..21b2e45d2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/LongArray.java
@@ -0,0 +1,1995 @@
+package org.spongycastle.math.ec;
+
+import org.spongycastle.util.Arrays;
+
+import java.math.BigInteger;
+
+class LongArray
+{
+// private static long DEINTERLEAVE_MASK = 0x5555555555555555L;
+
+ /*
+ * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits.
+ * In a binary field, this operation is the same as squaring an 8 bit number.
+ */
+ private static final int[] INTERLEAVE2_TABLE = new int[]
+ {
+ 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
+ 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
+ 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
+ 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
+ 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
+ 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
+ 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
+ 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
+ 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
+ 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
+ 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
+ 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
+ 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
+ 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
+ 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
+ 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
+ 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
+ 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
+ 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
+ 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
+ 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
+ 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
+ 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
+ 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
+ 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
+ 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
+ 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
+ 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
+ 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
+ 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
+ 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
+ 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
+ };
+
+ /*
+ * This expands 7 bit indices into 21 bit contents (high bit 18), by inserting 0s between bits.
+ */
+ private static final int[] INTERLEAVE3_TABLE = new int[]
+ {
+ 0x00000, 0x00001, 0x00008, 0x00009, 0x00040, 0x00041, 0x00048, 0x00049,
+ 0x00200, 0x00201, 0x00208, 0x00209, 0x00240, 0x00241, 0x00248, 0x00249,
+ 0x01000, 0x01001, 0x01008, 0x01009, 0x01040, 0x01041, 0x01048, 0x01049,
+ 0x01200, 0x01201, 0x01208, 0x01209, 0x01240, 0x01241, 0x01248, 0x01249,
+ 0x08000, 0x08001, 0x08008, 0x08009, 0x08040, 0x08041, 0x08048, 0x08049,
+ 0x08200, 0x08201, 0x08208, 0x08209, 0x08240, 0x08241, 0x08248, 0x08249,
+ 0x09000, 0x09001, 0x09008, 0x09009, 0x09040, 0x09041, 0x09048, 0x09049,
+ 0x09200, 0x09201, 0x09208, 0x09209, 0x09240, 0x09241, 0x09248, 0x09249,
+ 0x40000, 0x40001, 0x40008, 0x40009, 0x40040, 0x40041, 0x40048, 0x40049,
+ 0x40200, 0x40201, 0x40208, 0x40209, 0x40240, 0x40241, 0x40248, 0x40249,
+ 0x41000, 0x41001, 0x41008, 0x41009, 0x41040, 0x41041, 0x41048, 0x41049,
+ 0x41200, 0x41201, 0x41208, 0x41209, 0x41240, 0x41241, 0x41248, 0x41249,
+ 0x48000, 0x48001, 0x48008, 0x48009, 0x48040, 0x48041, 0x48048, 0x48049,
+ 0x48200, 0x48201, 0x48208, 0x48209, 0x48240, 0x48241, 0x48248, 0x48249,
+ 0x49000, 0x49001, 0x49008, 0x49009, 0x49040, 0x49041, 0x49048, 0x49049,
+ 0x49200, 0x49201, 0x49208, 0x49209, 0x49240, 0x49241, 0x49248, 0x49249
+ };
+
+ /*
+ * This expands 8 bit indices into 32 bit contents (high bit 28), by inserting 0s between bits.
+ */
+ private static final int[] INTERLEAVE4_TABLE = new int[]
+ {
+ 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 0x00000101, 0x00000110, 0x00000111,
+ 0x00001000, 0x00001001, 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110, 0x00001111,
+ 0x00010000, 0x00010001, 0x00010010, 0x00010011, 0x00010100, 0x00010101, 0x00010110, 0x00010111,
+ 0x00011000, 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101, 0x00011110, 0x00011111,
+ 0x00100000, 0x00100001, 0x00100010, 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111,
+ 0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100, 0x00101101, 0x00101110, 0x00101111,
+ 0x00110000, 0x00110001, 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110, 0x00110111,
+ 0x00111000, 0x00111001, 0x00111010, 0x00111011, 0x00111100, 0x00111101, 0x00111110, 0x00111111,
+ 0x01000000, 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101, 0x01000110, 0x01000111,
+ 0x01001000, 0x01001001, 0x01001010, 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111,
+ 0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100, 0x01010101, 0x01010110, 0x01010111,
+ 0x01011000, 0x01011001, 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110, 0x01011111,
+ 0x01100000, 0x01100001, 0x01100010, 0x01100011, 0x01100100, 0x01100101, 0x01100110, 0x01100111,
+ 0x01101000, 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101, 0x01101110, 0x01101111,
+ 0x01110000, 0x01110001, 0x01110010, 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111,
+ 0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100, 0x01111101, 0x01111110, 0x01111111,
+ 0x10000000, 0x10000001, 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110, 0x10000111,
+ 0x10001000, 0x10001001, 0x10001010, 0x10001011, 0x10001100, 0x10001101, 0x10001110, 0x10001111,
+ 0x10010000, 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101, 0x10010110, 0x10010111,
+ 0x10011000, 0x10011001, 0x10011010, 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111,
+ 0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100, 0x10100101, 0x10100110, 0x10100111,
+ 0x10101000, 0x10101001, 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110, 0x10101111,
+ 0x10110000, 0x10110001, 0x10110010, 0x10110011, 0x10110100, 0x10110101, 0x10110110, 0x10110111,
+ 0x10111000, 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101, 0x10111110, 0x10111111,
+ 0x11000000, 0x11000001, 0x11000010, 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111,
+ 0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100, 0x11001101, 0x11001110, 0x11001111,
+ 0x11010000, 0x11010001, 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110, 0x11010111,
+ 0x11011000, 0x11011001, 0x11011010, 0x11011011, 0x11011100, 0x11011101, 0x11011110, 0x11011111,
+ 0x11100000, 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101, 0x11100110, 0x11100111,
+ 0x11101000, 0x11101001, 0x11101010, 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111,
+ 0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100, 0x11110101, 0x11110110, 0x11110111,
+ 0x11111000, 0x11111001, 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110, 0x11111111
+ };
+
+ /*
+ * This expands 7 bit indices into 35 bit contents (high bit 30), by inserting 0s between bits.
+ */
+ private static final int[] INTERLEAVE5_TABLE = new int[] {
+ 0x00000000, 0x00000001, 0x00000020, 0x00000021, 0x00000400, 0x00000401, 0x00000420, 0x00000421,
+ 0x00008000, 0x00008001, 0x00008020, 0x00008021, 0x00008400, 0x00008401, 0x00008420, 0x00008421,
+ 0x00100000, 0x00100001, 0x00100020, 0x00100021, 0x00100400, 0x00100401, 0x00100420, 0x00100421,
+ 0x00108000, 0x00108001, 0x00108020, 0x00108021, 0x00108400, 0x00108401, 0x00108420, 0x00108421,
+ 0x02000000, 0x02000001, 0x02000020, 0x02000021, 0x02000400, 0x02000401, 0x02000420, 0x02000421,
+ 0x02008000, 0x02008001, 0x02008020, 0x02008021, 0x02008400, 0x02008401, 0x02008420, 0x02008421,
+ 0x02100000, 0x02100001, 0x02100020, 0x02100021, 0x02100400, 0x02100401, 0x02100420, 0x02100421,
+ 0x02108000, 0x02108001, 0x02108020, 0x02108021, 0x02108400, 0x02108401, 0x02108420, 0x02108421,
+ 0x40000000, 0x40000001, 0x40000020, 0x40000021, 0x40000400, 0x40000401, 0x40000420, 0x40000421,
+ 0x40008000, 0x40008001, 0x40008020, 0x40008021, 0x40008400, 0x40008401, 0x40008420, 0x40008421,
+ 0x40100000, 0x40100001, 0x40100020, 0x40100021, 0x40100400, 0x40100401, 0x40100420, 0x40100421,
+ 0x40108000, 0x40108001, 0x40108020, 0x40108021, 0x40108400, 0x40108401, 0x40108420, 0x40108421,
+ 0x42000000, 0x42000001, 0x42000020, 0x42000021, 0x42000400, 0x42000401, 0x42000420, 0x42000421,
+ 0x42008000, 0x42008001, 0x42008020, 0x42008021, 0x42008400, 0x42008401, 0x42008420, 0x42008421,
+ 0x42100000, 0x42100001, 0x42100020, 0x42100021, 0x42100400, 0x42100401, 0x42100420, 0x42100421,
+ 0x42108000, 0x42108001, 0x42108020, 0x42108021, 0x42108400, 0x42108401, 0x42108420, 0x42108421
+ };
+
+ /*
+ * This expands 9 bit indices into 63 bit (long) contents (high bit 56), by inserting 0s between bits.
+ */
+ private static final long[] INTERLEAVE7_TABLE = new long[]
+ {
+ 0x0000000000000000L, 0x0000000000000001L, 0x0000000000000080L, 0x0000000000000081L,
+ 0x0000000000004000L, 0x0000000000004001L, 0x0000000000004080L, 0x0000000000004081L,
+ 0x0000000000200000L, 0x0000000000200001L, 0x0000000000200080L, 0x0000000000200081L,
+ 0x0000000000204000L, 0x0000000000204001L, 0x0000000000204080L, 0x0000000000204081L,
+ 0x0000000010000000L, 0x0000000010000001L, 0x0000000010000080L, 0x0000000010000081L,
+ 0x0000000010004000L, 0x0000000010004001L, 0x0000000010004080L, 0x0000000010004081L,
+ 0x0000000010200000L, 0x0000000010200001L, 0x0000000010200080L, 0x0000000010200081L,
+ 0x0000000010204000L, 0x0000000010204001L, 0x0000000010204080L, 0x0000000010204081L,
+ 0x0000000800000000L, 0x0000000800000001L, 0x0000000800000080L, 0x0000000800000081L,
+ 0x0000000800004000L, 0x0000000800004001L, 0x0000000800004080L, 0x0000000800004081L,
+ 0x0000000800200000L, 0x0000000800200001L, 0x0000000800200080L, 0x0000000800200081L,
+ 0x0000000800204000L, 0x0000000800204001L, 0x0000000800204080L, 0x0000000800204081L,
+ 0x0000000810000000L, 0x0000000810000001L, 0x0000000810000080L, 0x0000000810000081L,
+ 0x0000000810004000L, 0x0000000810004001L, 0x0000000810004080L, 0x0000000810004081L,
+ 0x0000000810200000L, 0x0000000810200001L, 0x0000000810200080L, 0x0000000810200081L,
+ 0x0000000810204000L, 0x0000000810204001L, 0x0000000810204080L, 0x0000000810204081L,
+ 0x0000040000000000L, 0x0000040000000001L, 0x0000040000000080L, 0x0000040000000081L,
+ 0x0000040000004000L, 0x0000040000004001L, 0x0000040000004080L, 0x0000040000004081L,
+ 0x0000040000200000L, 0x0000040000200001L, 0x0000040000200080L, 0x0000040000200081L,
+ 0x0000040000204000L, 0x0000040000204001L, 0x0000040000204080L, 0x0000040000204081L,
+ 0x0000040010000000L, 0x0000040010000001L, 0x0000040010000080L, 0x0000040010000081L,
+ 0x0000040010004000L, 0x0000040010004001L, 0x0000040010004080L, 0x0000040010004081L,
+ 0x0000040010200000L, 0x0000040010200001L, 0x0000040010200080L, 0x0000040010200081L,
+ 0x0000040010204000L, 0x0000040010204001L, 0x0000040010204080L, 0x0000040010204081L,
+ 0x0000040800000000L, 0x0000040800000001L, 0x0000040800000080L, 0x0000040800000081L,
+ 0x0000040800004000L, 0x0000040800004001L, 0x0000040800004080L, 0x0000040800004081L,
+ 0x0000040800200000L, 0x0000040800200001L, 0x0000040800200080L, 0x0000040800200081L,
+ 0x0000040800204000L, 0x0000040800204001L, 0x0000040800204080L, 0x0000040800204081L,
+ 0x0000040810000000L, 0x0000040810000001L, 0x0000040810000080L, 0x0000040810000081L,
+ 0x0000040810004000L, 0x0000040810004001L, 0x0000040810004080L, 0x0000040810004081L,
+ 0x0000040810200000L, 0x0000040810200001L, 0x0000040810200080L, 0x0000040810200081L,
+ 0x0000040810204000L, 0x0000040810204001L, 0x0000040810204080L, 0x0000040810204081L,
+ 0x0002000000000000L, 0x0002000000000001L, 0x0002000000000080L, 0x0002000000000081L,
+ 0x0002000000004000L, 0x0002000000004001L, 0x0002000000004080L, 0x0002000000004081L,
+ 0x0002000000200000L, 0x0002000000200001L, 0x0002000000200080L, 0x0002000000200081L,
+ 0x0002000000204000L, 0x0002000000204001L, 0x0002000000204080L, 0x0002000000204081L,
+ 0x0002000010000000L, 0x0002000010000001L, 0x0002000010000080L, 0x0002000010000081L,
+ 0x0002000010004000L, 0x0002000010004001L, 0x0002000010004080L, 0x0002000010004081L,
+ 0x0002000010200000L, 0x0002000010200001L, 0x0002000010200080L, 0x0002000010200081L,
+ 0x0002000010204000L, 0x0002000010204001L, 0x0002000010204080L, 0x0002000010204081L,
+ 0x0002000800000000L, 0x0002000800000001L, 0x0002000800000080L, 0x0002000800000081L,
+ 0x0002000800004000L, 0x0002000800004001L, 0x0002000800004080L, 0x0002000800004081L,
+ 0x0002000800200000L, 0x0002000800200001L, 0x0002000800200080L, 0x0002000800200081L,
+ 0x0002000800204000L, 0x0002000800204001L, 0x0002000800204080L, 0x0002000800204081L,
+ 0x0002000810000000L, 0x0002000810000001L, 0x0002000810000080L, 0x0002000810000081L,
+ 0x0002000810004000L, 0x0002000810004001L, 0x0002000810004080L, 0x0002000810004081L,
+ 0x0002000810200000L, 0x0002000810200001L, 0x0002000810200080L, 0x0002000810200081L,
+ 0x0002000810204000L, 0x0002000810204001L, 0x0002000810204080L, 0x0002000810204081L,
+ 0x0002040000000000L, 0x0002040000000001L, 0x0002040000000080L, 0x0002040000000081L,
+ 0x0002040000004000L, 0x0002040000004001L, 0x0002040000004080L, 0x0002040000004081L,
+ 0x0002040000200000L, 0x0002040000200001L, 0x0002040000200080L, 0x0002040000200081L,
+ 0x0002040000204000L, 0x0002040000204001L, 0x0002040000204080L, 0x0002040000204081L,
+ 0x0002040010000000L, 0x0002040010000001L, 0x0002040010000080L, 0x0002040010000081L,
+ 0x0002040010004000L, 0x0002040010004001L, 0x0002040010004080L, 0x0002040010004081L,
+ 0x0002040010200000L, 0x0002040010200001L, 0x0002040010200080L, 0x0002040010200081L,
+ 0x0002040010204000L, 0x0002040010204001L, 0x0002040010204080L, 0x0002040010204081L,
+ 0x0002040800000000L, 0x0002040800000001L, 0x0002040800000080L, 0x0002040800000081L,
+ 0x0002040800004000L, 0x0002040800004001L, 0x0002040800004080L, 0x0002040800004081L,
+ 0x0002040800200000L, 0x0002040800200001L, 0x0002040800200080L, 0x0002040800200081L,
+ 0x0002040800204000L, 0x0002040800204001L, 0x0002040800204080L, 0x0002040800204081L,
+ 0x0002040810000000L, 0x0002040810000001L, 0x0002040810000080L, 0x0002040810000081L,
+ 0x0002040810004000L, 0x0002040810004001L, 0x0002040810004080L, 0x0002040810004081L,
+ 0x0002040810200000L, 0x0002040810200001L, 0x0002040810200080L, 0x0002040810200081L,
+ 0x0002040810204000L, 0x0002040810204001L, 0x0002040810204080L, 0x0002040810204081L,
+ 0x0100000000000000L, 0x0100000000000001L, 0x0100000000000080L, 0x0100000000000081L,
+ 0x0100000000004000L, 0x0100000000004001L, 0x0100000000004080L, 0x0100000000004081L,
+ 0x0100000000200000L, 0x0100000000200001L, 0x0100000000200080L, 0x0100000000200081L,
+ 0x0100000000204000L, 0x0100000000204001L, 0x0100000000204080L, 0x0100000000204081L,
+ 0x0100000010000000L, 0x0100000010000001L, 0x0100000010000080L, 0x0100000010000081L,
+ 0x0100000010004000L, 0x0100000010004001L, 0x0100000010004080L, 0x0100000010004081L,
+ 0x0100000010200000L, 0x0100000010200001L, 0x0100000010200080L, 0x0100000010200081L,
+ 0x0100000010204000L, 0x0100000010204001L, 0x0100000010204080L, 0x0100000010204081L,
+ 0x0100000800000000L, 0x0100000800000001L, 0x0100000800000080L, 0x0100000800000081L,
+ 0x0100000800004000L, 0x0100000800004001L, 0x0100000800004080L, 0x0100000800004081L,
+ 0x0100000800200000L, 0x0100000800200001L, 0x0100000800200080L, 0x0100000800200081L,
+ 0x0100000800204000L, 0x0100000800204001L, 0x0100000800204080L, 0x0100000800204081L,
+ 0x0100000810000000L, 0x0100000810000001L, 0x0100000810000080L, 0x0100000810000081L,
+ 0x0100000810004000L, 0x0100000810004001L, 0x0100000810004080L, 0x0100000810004081L,
+ 0x0100000810200000L, 0x0100000810200001L, 0x0100000810200080L, 0x0100000810200081L,
+ 0x0100000810204000L, 0x0100000810204001L, 0x0100000810204080L, 0x0100000810204081L,
+ 0x0100040000000000L, 0x0100040000000001L, 0x0100040000000080L, 0x0100040000000081L,
+ 0x0100040000004000L, 0x0100040000004001L, 0x0100040000004080L, 0x0100040000004081L,
+ 0x0100040000200000L, 0x0100040000200001L, 0x0100040000200080L, 0x0100040000200081L,
+ 0x0100040000204000L, 0x0100040000204001L, 0x0100040000204080L, 0x0100040000204081L,
+ 0x0100040010000000L, 0x0100040010000001L, 0x0100040010000080L, 0x0100040010000081L,
+ 0x0100040010004000L, 0x0100040010004001L, 0x0100040010004080L, 0x0100040010004081L,
+ 0x0100040010200000L, 0x0100040010200001L, 0x0100040010200080L, 0x0100040010200081L,
+ 0x0100040010204000L, 0x0100040010204001L, 0x0100040010204080L, 0x0100040010204081L,
+ 0x0100040800000000L, 0x0100040800000001L, 0x0100040800000080L, 0x0100040800000081L,
+ 0x0100040800004000L, 0x0100040800004001L, 0x0100040800004080L, 0x0100040800004081L,
+ 0x0100040800200000L, 0x0100040800200001L, 0x0100040800200080L, 0x0100040800200081L,
+ 0x0100040800204000L, 0x0100040800204001L, 0x0100040800204080L, 0x0100040800204081L,
+ 0x0100040810000000L, 0x0100040810000001L, 0x0100040810000080L, 0x0100040810000081L,
+ 0x0100040810004000L, 0x0100040810004001L, 0x0100040810004080L, 0x0100040810004081L,
+ 0x0100040810200000L, 0x0100040810200001L, 0x0100040810200080L, 0x0100040810200081L,
+ 0x0100040810204000L, 0x0100040810204001L, 0x0100040810204080L, 0x0100040810204081L,
+ 0x0102000000000000L, 0x0102000000000001L, 0x0102000000000080L, 0x0102000000000081L,
+ 0x0102000000004000L, 0x0102000000004001L, 0x0102000000004080L, 0x0102000000004081L,
+ 0x0102000000200000L, 0x0102000000200001L, 0x0102000000200080L, 0x0102000000200081L,
+ 0x0102000000204000L, 0x0102000000204001L, 0x0102000000204080L, 0x0102000000204081L,
+ 0x0102000010000000L, 0x0102000010000001L, 0x0102000010000080L, 0x0102000010000081L,
+ 0x0102000010004000L, 0x0102000010004001L, 0x0102000010004080L, 0x0102000010004081L,
+ 0x0102000010200000L, 0x0102000010200001L, 0x0102000010200080L, 0x0102000010200081L,
+ 0x0102000010204000L, 0x0102000010204001L, 0x0102000010204080L, 0x0102000010204081L,
+ 0x0102000800000000L, 0x0102000800000001L, 0x0102000800000080L, 0x0102000800000081L,
+ 0x0102000800004000L, 0x0102000800004001L, 0x0102000800004080L, 0x0102000800004081L,
+ 0x0102000800200000L, 0x0102000800200001L, 0x0102000800200080L, 0x0102000800200081L,
+ 0x0102000800204000L, 0x0102000800204001L, 0x0102000800204080L, 0x0102000800204081L,
+ 0x0102000810000000L, 0x0102000810000001L, 0x0102000810000080L, 0x0102000810000081L,
+ 0x0102000810004000L, 0x0102000810004001L, 0x0102000810004080L, 0x0102000810004081L,
+ 0x0102000810200000L, 0x0102000810200001L, 0x0102000810200080L, 0x0102000810200081L,
+ 0x0102000810204000L, 0x0102000810204001L, 0x0102000810204080L, 0x0102000810204081L,
+ 0x0102040000000000L, 0x0102040000000001L, 0x0102040000000080L, 0x0102040000000081L,
+ 0x0102040000004000L, 0x0102040000004001L, 0x0102040000004080L, 0x0102040000004081L,
+ 0x0102040000200000L, 0x0102040000200001L, 0x0102040000200080L, 0x0102040000200081L,
+ 0x0102040000204000L, 0x0102040000204001L, 0x0102040000204080L, 0x0102040000204081L,
+ 0x0102040010000000L, 0x0102040010000001L, 0x0102040010000080L, 0x0102040010000081L,
+ 0x0102040010004000L, 0x0102040010004001L, 0x0102040010004080L, 0x0102040010004081L,
+ 0x0102040010200000L, 0x0102040010200001L, 0x0102040010200080L, 0x0102040010200081L,
+ 0x0102040010204000L, 0x0102040010204001L, 0x0102040010204080L, 0x0102040010204081L,
+ 0x0102040800000000L, 0x0102040800000001L, 0x0102040800000080L, 0x0102040800000081L,
+ 0x0102040800004000L, 0x0102040800004001L, 0x0102040800004080L, 0x0102040800004081L,
+ 0x0102040800200000L, 0x0102040800200001L, 0x0102040800200080L, 0x0102040800200081L,
+ 0x0102040800204000L, 0x0102040800204001L, 0x0102040800204080L, 0x0102040800204081L,
+ 0x0102040810000000L, 0x0102040810000001L, 0x0102040810000080L, 0x0102040810000081L,
+ 0x0102040810004000L, 0x0102040810004001L, 0x0102040810004080L, 0x0102040810004081L,
+ 0x0102040810200000L, 0x0102040810200001L, 0x0102040810200080L, 0x0102040810200081L,
+ 0x0102040810204000L, 0x0102040810204001L, 0x0102040810204080L, 0x0102040810204081L
+ };
+
+ // For toString(); must have length 64
+ private static final String ZEROES = "0000000000000000000000000000000000000000000000000000000000000000";
+
+ final static byte[] bitLengths =
+ {
+ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
+ };
+
+ // TODO make m fixed for the LongArray, and hence compute T once and for all
+
+ private long[] m_ints;
+
+ public LongArray(int intLen)
+ {
+ m_ints = new long[intLen];
+ }
+
+ public LongArray(long[] ints)
+ {
+ m_ints = ints;
+ }
+
+ public LongArray(long[] ints, int off, int len)
+ {
+ if (off == 0 && len == ints.length)
+ {
+ m_ints = ints;
+ }
+ else
+ {
+ m_ints = new long[len];
+ System.arraycopy(ints, off, m_ints, 0, len);
+ }
+ }
+
+ public LongArray(BigInteger bigInt)
+ {
+ if (bigInt == null || bigInt.signum() < 0)
+ {
+ throw new IllegalArgumentException("invalid F2m field value");
+ }
+
+ if (bigInt.signum() == 0)
+ {
+ m_ints = new long[] { 0L };
+ return;
+ }
+
+ byte[] barr = bigInt.toByteArray();
+ int barrLen = barr.length;
+ int barrStart = 0;
+ if (barr[0] == 0)
+ {
+ // First byte is 0 to enforce highest (=sign) bit is zero.
+ // In this case ignore barr[0].
+ barrLen--;
+ barrStart = 1;
+ }
+ int intLen = (barrLen + 7) / 8;
+ m_ints = new long[intLen];
+
+ int iarrJ = intLen - 1;
+ int rem = barrLen % 8 + barrStart;
+ long temp = 0;
+ int barrI = barrStart;
+ if (barrStart < rem)
+ {
+ for (; barrI < rem; barrI++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI] & 0xFF;
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ--] = temp;
+ }
+
+ for (; iarrJ >= 0; iarrJ--)
+ {
+ temp = 0;
+ for (int i = 0; i < 8; i++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI++] & 0xFF;
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ] = temp;
+ }
+ }
+
+ public boolean isZero()
+ {
+ long[] a = m_ints;
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int getUsedLength()
+ {
+ return getUsedLengthFrom(m_ints.length);
+ }
+
+ public int getUsedLengthFrom(int from)
+ {
+ long[] a = m_ints;
+ from = Math.min(from, a.length);
+
+ if (from < 1)
+ {
+ return 0;
+ }
+
+ // Check if first element will act as sentinel
+ if (a[0] != 0)
+ {
+ while (a[--from] == 0)
+ {
+ }
+ return from + 1;
+ }
+
+ do
+ {
+ if (a[--from] != 0)
+ {
+ return from + 1;
+ }
+ }
+ while (from > 0);
+
+ return 0;
+ }
+
+ public int degree()
+ {
+ int i = m_ints.length;
+ long w;
+ do
+ {
+ if (i == 0)
+ {
+ return 0;
+ }
+ w = m_ints[--i];
+ }
+ while (w == 0);
+
+ return (i << 6) + bitLength(w);
+ }
+
+ private int degreeFrom(int limit)
+ {
+ int i = (limit + 62) >>> 6;
+ long w;
+ do
+ {
+ if (i == 0)
+ {
+ return 0;
+ }
+ w = m_ints[--i];
+ }
+ while (w == 0);
+
+ return (i << 6) + bitLength(w);
+ }
+
+// private int lowestCoefficient()
+// {
+// for (int i = 0; i < m_ints.length; ++i)
+// {
+// long mi = m_ints[i];
+// if (mi != 0)
+// {
+// int j = 0;
+// while ((mi & 0xFFL) == 0)
+// {
+// j += 8;
+// mi >>>= 8;
+// }
+// while ((mi & 1L) == 0)
+// {
+// ++j;
+// mi >>>= 1;
+// }
+// return (i << 6) + j;
+// }
+// }
+// return -1;
+// }
+
+ private static int bitLength(long w)
+ {
+ int u = (int)(w >>> 32), b;
+ if (u == 0)
+ {
+ u = (int)w;
+ b = 0;
+ }
+ else
+ {
+ b = 32;
+ }
+
+ int t = u >>> 16, k;
+ if (t == 0)
+ {
+ t = u >>> 8;
+ k = (t == 0) ? bitLengths[u] : 8 + bitLengths[t];
+ }
+ else
+ {
+ int v = t >>> 8;
+ k = (v == 0) ? 16 + bitLengths[t] : 24 + bitLengths[v];
+ }
+
+ return b + k;
+ }
+
+ private long[] resizedInts(int newLen)
+ {
+ long[] newInts = new long[newLen];
+ System.arraycopy(m_ints, 0, newInts, 0, Math.min(m_ints.length, newLen));
+ return newInts;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ int usedLen = getUsedLength();
+ if (usedLen == 0)
+ {
+ return ECConstants.ZERO;
+ }
+
+ long highestInt = m_ints[usedLen - 1];
+ byte[] temp = new byte[8];
+ int barrI = 0;
+ boolean trailingZeroBytesDone = false;
+ for (int j = 7; j >= 0; j--)
+ {
+ byte thisByte = (byte)(highestInt >>> (8 * j));
+ if (trailingZeroBytesDone || (thisByte != 0))
+ {
+ trailingZeroBytesDone = true;
+ temp[barrI++] = thisByte;
+ }
+ }
+
+ int barrLen = 8 * (usedLen - 1) + barrI;
+ byte[] barr = new byte[barrLen];
+ for (int j = 0; j < barrI; j++)
+ {
+ barr[j] = temp[j];
+ }
+ // Highest value int is done now
+
+ for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+ {
+ long mi = m_ints[iarrJ];
+ for (int j = 7; j >= 0; j--)
+ {
+ barr[barrI++] = (byte)(mi >>> (8 * j));
+ }
+ }
+ return new BigInteger(1, barr);
+ }
+
+// private static long shiftUp(long[] x, int xOff, int count)
+// {
+// long prev = 0;
+// for (int i = 0; i < count; ++i)
+// {
+// long next = x[xOff + i];
+// x[xOff + i] = (next << 1) | prev;
+// prev = next >>> 63;
+// }
+// return prev;
+// }
+
+ private static long shiftUp(long[] x, int xOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ long next = x[xOff + i];
+ x[xOff + i] = (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ private static long shiftUp(long[] x, int xOff, long[] z, int zOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ long next = x[xOff + i];
+ z[zOff + i] = (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ public LongArray addOne()
+ {
+ if (m_ints.length == 0)
+ {
+ return new LongArray(new long[]{ 1L });
+ }
+
+ int resultLen = Math.max(1, getUsedLength());
+ long[] ints = resizedInts(resultLen);
+ ints[0] ^= 1L;
+ return new LongArray(ints);
+ }
+
+// private void addShiftedByBits(LongArray other, int bits)
+// {
+// int words = bits >>> 6;
+// int shift = bits & 0x3F;
+//
+// if (shift == 0)
+// {
+// addShiftedByWords(other, words);
+// return;
+// }
+//
+// int otherUsedLen = other.getUsedLength();
+// if (otherUsedLen == 0)
+// {
+// return;
+// }
+//
+// int minLen = otherUsedLen + words + 1;
+// if (minLen > m_ints.length)
+// {
+// m_ints = resizedInts(minLen);
+// }
+//
+// long carry = addShiftedByBits(m_ints, words, other.m_ints, 0, otherUsedLen, shift);
+// m_ints[otherUsedLen + words] ^= carry;
+// }
+
+ private void addShiftedByBitsSafe(LongArray other, int otherDegree, int bits)
+ {
+ int otherLen = (otherDegree + 63) >>> 6;
+
+ int words = bits >>> 6;
+ int shift = bits & 0x3F;
+
+ if (shift == 0)
+ {
+ add(m_ints, words, other.m_ints, 0, otherLen);
+ return;
+ }
+
+ long carry = addShiftedUp(m_ints, words, other.m_ints, 0, otherLen, shift);
+ if (carry != 0L)
+ {
+ m_ints[otherLen + words] ^= carry;
+ }
+ }
+
+ private static long addShiftedUp(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ long next = y[yOff + i];
+ x[xOff + i] ^= (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ private static long addShiftedDown(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ int i = count;
+ while (--i >= 0)
+ {
+ long next = y[yOff + i];
+ x[xOff + i] ^= (next >>> shift) | prev;
+ prev = next << shiftInv;
+ }
+ return prev;
+ }
+
+ public void addShiftedByWords(LongArray other, int words)
+ {
+ int otherUsedLen = other.getUsedLength();
+ if (otherUsedLen == 0)
+ {
+ return;
+ }
+
+ int minLen = otherUsedLen + words;
+ if (minLen > m_ints.length)
+ {
+ m_ints = resizedInts(minLen);
+ }
+
+ add(m_ints, words, other.m_ints, 0, otherUsedLen);
+ }
+
+ private static void add(long[] x, int xOff, long[] y, int yOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ x[xOff + i] ^= y[yOff + i];
+ }
+ }
+
+ private static void add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = x[xOff + i] ^ y[yOff + i];
+ }
+ }
+
+ private static void addBoth(long[] x, int xOff, long[] y1, int y1Off, long[] y2, int y2Off, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ x[xOff + i] ^= y1[y1Off + i] ^ y2[y2Off + i];
+ }
+ }
+
+ private static void distribute(long[] x, int src, int dst1, int dst2, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ long v = x[src + i];
+ x[dst1 + i] ^= v;
+ x[dst2 + i] ^= v;
+ }
+ }
+
+ public int getLength()
+ {
+ return m_ints.length;
+ }
+
+ private static void flipWord(long[] buf, int off, int bit, long word)
+ {
+ int n = off + (bit >>> 6);
+ int shift = bit & 0x3F;
+ if (shift == 0)
+ {
+ buf[n] ^= word;
+ }
+ else
+ {
+ buf[n] ^= word << shift;
+ word >>>= (64 - shift);
+ if (word != 0)
+ {
+ buf[++n] ^= word;
+ }
+ }
+ }
+
+// private static long getWord(long[] buf, int off, int len, int bit)
+// {
+// int n = off + (bit >>> 6);
+// int shift = bit & 0x3F;
+// if (shift == 0)
+// {
+// return buf[n];
+// }
+// long result = buf[n] >>> shift;
+// if (++n < len)
+// {
+// result |= buf[n] << (64 - shift);
+// }
+// return result;
+// }
+
+ public boolean testBitZero()
+ {
+ return m_ints.length > 0 && (m_ints[0] & 1L) != 0;
+ }
+
+ private static boolean testBit(long[] buf, int off, int n)
+ {
+ // theInt = n / 64
+ int theInt = n >>> 6;
+ // theBit = n % 64
+ int theBit = n & 0x3F;
+ long tester = 1L << theBit;
+ return (buf[off + theInt] & tester) != 0;
+ }
+
+ private static void flipBit(long[] buf, int off, int n)
+ {
+ // theInt = n / 64
+ int theInt = n >>> 6;
+ // theBit = n % 64
+ int theBit = n & 0x3F;
+ long flipper = 1L << theBit;
+ buf[off + theInt] ^= flipper;
+ }
+
+// private static void setBit(long[] buf, int off, int n)
+// {
+// // theInt = n / 64
+// int theInt = n >>> 6;
+// // theBit = n % 64
+// int theBit = n & 0x3F;
+// long setter = 1L << theBit;
+// buf[off + theInt] |= setter;
+// }
+//
+// private static void clearBit(long[] buf, int off, int n)
+// {
+// // theInt = n / 64
+// int theInt = n >>> 6;
+// // theBit = n % 64
+// int theBit = n & 0x3F;
+// long setter = 1L << theBit;
+// buf[off + theInt] &= ~setter;
+// }
+
+ private static void multiplyWord(long a, long[] b, int bLen, long[] c, int cOff)
+ {
+ if ((a & 1L) != 0L)
+ {
+ add(c, cOff, b, 0, bLen);
+ }
+ int k = 1;
+ while ((a >>>= 1) != 0)
+ {
+ if ((a & 1L) != 0L)
+ {
+ long carry = addShiftedUp(c, cOff, b, 0, bLen, k);
+ if (carry != 0)
+ {
+ c[cOff + bLen] ^= carry;
+ }
+ }
+ ++k;
+ }
+ }
+
+ public LongArray modMultiplyLD(LongArray other, int m, int[] ks)
+ {
+ /*
+ * Find out the degree of each argument and handle the zero cases
+ */
+ int aDeg = degree();
+ if (aDeg == 0)
+ {
+ return this;
+ }
+ int bDeg = other.degree();
+ if (bDeg == 0)
+ {
+ return other;
+ }
+
+ /*
+ * Swap if necessary so that A is the smaller argument
+ */
+ LongArray A = this, B = other;
+ if (aDeg > bDeg)
+ {
+ A = other; B = this;
+ int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+ }
+
+ /*
+ * Establish the word lengths of the arguments and result
+ */
+ int aLen = (aDeg + 63) >>> 6;
+ int bLen = (bDeg + 63) >>> 6;
+ int cLen = (aDeg + bDeg + 62) >>> 6;
+
+ if (aLen == 1)
+ {
+ long a = A.m_ints[0];
+ if (a == 1L)
+ {
+ return B;
+ }
+
+ /*
+ * Fast path for small A, with performance dependent only on the number of set bits
+ */
+ long[] c = new long[cLen];
+ multiplyWord(a, B.m_ints, bLen, c, 0);
+
+ /*
+ * Reduce the raw answer against the reduction coefficients
+ */
+ return reduceResult(c, 0, cLen, m, ks);
+ }
+
+ /*
+ * Determine if B will get bigger during shifting
+ */
+ int bMax = (bDeg + 7 + 63) >>> 6;
+
+ /*
+ * Lookup table for the offset of each B in the tables
+ */
+ int[] ti = new int[16];
+
+ /*
+ * Precompute table of all 4-bit products of B
+ */
+ long[] T0 = new long[bMax << 4];
+ int tOff = bMax;
+ ti[1] = tOff;
+ System.arraycopy(B.m_ints, 0, T0, tOff, bLen);
+ for (int i = 2; i < 16; ++i)
+ {
+ ti[i] = (tOff += bMax);
+ if ((i & 1) == 0)
+ {
+ shiftUp(T0, tOff >>> 1, T0, tOff, bMax, 1);
+ }
+ else
+ {
+ add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+ }
+ }
+
+ /*
+ * Second table with all 4-bit products of B shifted 4 bits
+ */
+ long[] T1 = new long[T0.length];
+ shiftUp(T0, 0, T1, 0, T0.length, 4);
+// shiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+ long[] a = A.m_ints;
+ long[] c = new long[cLen];
+
+ int MASK = 0xF;
+
+ /*
+ * Lopez-Dahab algorithm
+ */
+
+ for (int k = 56; k >= 0; k -= 8)
+ {
+ for (int j = 1; j < aLen; j += 2)
+ {
+ int aVal = (int)(a[j] >>> k);
+ int u = aVal & MASK;
+ int v = (aVal >>> 4) & MASK;
+ addBoth(c, j - 1, T0, ti[u], T1, ti[v], bMax);
+ }
+ shiftUp(c, 0, cLen, 8);
+ }
+
+ for (int k = 56; k >= 0; k -= 8)
+ {
+ for (int j = 0; j < aLen; j += 2)
+ {
+ int aVal = (int)(a[j] >>> k);
+ int u = aVal & MASK;
+ int v = (aVal >>> 4) & MASK;
+ addBoth(c, j, T0, ti[u], T1, ti[v], bMax);
+ }
+ if (k > 0)
+ {
+ shiftUp(c, 0, cLen, 8);
+ }
+ }
+
+ /*
+ * Finally the raw answer is collected, reduce it against the reduction coefficients
+ */
+ return reduceResult(c, 0, cLen, m, ks);
+ }
+
+ public LongArray modMultiply(LongArray other, int m, int[] ks)
+ {
+ /*
+ * Find out the degree of each argument and handle the zero cases
+ */
+ int aDeg = degree();
+ if (aDeg == 0)
+ {
+ return this;
+ }
+ int bDeg = other.degree();
+ if (bDeg == 0)
+ {
+ return other;
+ }
+
+ /*
+ * Swap if necessary so that A is the smaller argument
+ */
+ LongArray A = this, B = other;
+ if (aDeg > bDeg)
+ {
+ A = other; B = this;
+ int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+ }
+
+ /*
+ * Establish the word lengths of the arguments and result
+ */
+ int aLen = (aDeg + 63) >>> 6;
+ int bLen = (bDeg + 63) >>> 6;
+ int cLen = (aDeg + bDeg + 62) >>> 6;
+
+ if (aLen == 1)
+ {
+ long a = A.m_ints[0];
+ if (a == 1L)
+ {
+ return B;
+ }
+
+ /*
+ * Fast path for small A, with performance dependent only on the number of set bits
+ */
+ long[] c = new long[cLen];
+ multiplyWord(a, B.m_ints, bLen, c, 0);
+
+ /*
+ * Reduce the raw answer against the reduction coefficients
+ */
+ return reduceResult(c, 0, cLen, m, ks);
+ }
+
+ /*
+ * Determine if B will get bigger during shifting
+ */
+ int bMax = (bDeg + 7 + 63) >>> 6;
+
+ /*
+ * Lookup table for the offset of each B in the tables
+ */
+ int[] ti = new int[16];
+
+ /*
+ * Precompute table of all 4-bit products of B
+ */
+ long[] T0 = new long[bMax << 4];
+ int tOff = bMax;
+ ti[1] = tOff;
+ System.arraycopy(B.m_ints, 0, T0, tOff, bLen);
+ for (int i = 2; i < 16; ++i)
+ {
+ ti[i] = (tOff += bMax);
+ if ((i & 1) == 0)
+ {
+ shiftUp(T0, tOff >>> 1, T0, tOff, bMax, 1);
+ }
+ else
+ {
+ add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+ }
+ }
+
+ /*
+ * Second table with all 4-bit products of B shifted 4 bits
+ */
+ long[] T1 = new long[T0.length];
+ shiftUp(T0, 0, T1, 0, T0.length, 4);
+// shiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+ long[] a = A.m_ints;
+ long[] c = new long[cLen << 3];
+
+ int MASK = 0xF;
+
+ /*
+ * Lopez-Dahab (Modified) algorithm
+ */
+
+ for (int aPos = 0; aPos < aLen; ++aPos)
+ {
+ long aVal = a[aPos];
+ int cOff = aPos;
+ for (;;)
+ {
+ int u = (int)aVal & MASK;
+ aVal >>>= 4;
+ int v = (int)aVal & MASK;
+ addBoth(c, cOff, T0, ti[u], T1, ti[v], bMax);
+ if ((aVal >>>= 4) == 0L)
+ {
+ break;
+ }
+ cOff += cLen;
+ }
+ }
+
+ int cOff = c.length;
+ while ((cOff -= cLen) != 0)
+ {
+ addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8);
+ }
+
+ /*
+ * Finally the raw answer is collected, reduce it against the reduction coefficients
+ */
+ return reduceResult(c, 0, cLen, m, ks);
+ }
+
+ public LongArray modMultiplyAlt(LongArray other, int m, int[] ks)
+ {
+ /*
+ * Find out the degree of each argument and handle the zero cases
+ */
+ int aDeg = degree();
+ if (aDeg == 0)
+ {
+ return this;
+ }
+ int bDeg = other.degree();
+ if (bDeg == 0)
+ {
+ return other;
+ }
+
+ /*
+ * Swap if necessary so that A is the smaller argument
+ */
+ LongArray A = this, B = other;
+ if (aDeg > bDeg)
+ {
+ A = other; B = this;
+ int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+ }
+
+ /*
+ * Establish the word lengths of the arguments and result
+ */
+ int aLen = (aDeg + 63) >>> 6;
+ int bLen = (bDeg + 63) >>> 6;
+ int cLen = (aDeg + bDeg + 62) >>> 6;
+
+ if (aLen == 1)
+ {
+ long a = A.m_ints[0];
+ if (a == 1L)
+ {
+ return B;
+ }
+
+ /*
+ * Fast path for small A, with performance dependent only on the number of set bits
+ */
+ long[] c = new long[cLen];
+ multiplyWord(a, B.m_ints, bLen, c, 0);
+
+ /*
+ * Reduce the raw answer against the reduction coefficients
+ */
+ return reduceResult(c, 0, cLen, m, ks);
+ }
+
+ // NOTE: This works, but is slower than width 4 processing
+// if (aLen == 2)
+// {
+// /*
+// * Use common-multiplicand optimization to save ~1/4 of the adds
+// */
+// long a1 = A.m_ints[0], a2 = A.m_ints[1];
+// long aa = a1 & a2; a1 ^= aa; a2 ^= aa;
+//
+// long[] b = B.m_ints;
+// long[] c = new long[cLen];
+// multiplyWord(aa, b, bLen, c, 1);
+// add(c, 0, c, 1, cLen - 1);
+// multiplyWord(a1, b, bLen, c, 0);
+// multiplyWord(a2, b, bLen, c, 1);
+//
+// /*
+// * Reduce the raw answer against the reduction coefficients
+// */
+// return reduceResult(c, 0, cLen, m, ks);
+// }
+
+ /*
+ * Determine the parameters of the interleaved window algorithm: the 'width' in bits to
+ * process together, the number of evaluation 'positions' implied by that width, and the
+ * 'top' position at which the regular window algorithm stops.
+ */
+ int width, positions, top, banks;
+
+ // NOTE: width 4 is the fastest over the entire range of sizes used in current crypto
+// width = 1; positions = 64; top = 64; banks = 4;
+// width = 2; positions = 32; top = 64; banks = 4;
+// width = 3; positions = 21; top = 63; banks = 3;
+ width = 4; positions = 16; top = 64; banks = 8;
+// width = 5; positions = 13; top = 65; banks = 7;
+// width = 7; positions = 9; top = 63; banks = 9;
+// width = 8; positions = 8; top = 64; banks = 8;
+
+ /*
+ * Determine if B will get bigger during shifting
+ */
+ int shifts = top < 64 ? positions : positions - 1;
+ int bMax = (bDeg + shifts + 63) >>> 6;
+
+ int bTotal = bMax * banks, stride = width * banks;
+
+ /*
+ * Create a single temporary buffer, with an offset table to find the positions of things in it
+ */
+ int[] ci = new int[1 << width];
+ int cTotal = aLen;
+ {
+ ci[0] = cTotal;
+ cTotal += bTotal;
+ ci[1] = cTotal;
+ for (int i = 2; i < ci.length; ++i)
+ {
+ cTotal += cLen;
+ ci[i] = cTotal;
+ }
+ cTotal += cLen;
+ }
+ // NOTE: Provide a safe dump for "high zeroes" since we are adding 'bMax' and not 'bLen'
+ ++cTotal;
+
+ long[] c = new long[cTotal];
+
+ // Prepare A in interleaved form, according to the chosen width
+ interleave(A.m_ints, 0, c, 0, aLen, width);
+
+ // Make a working copy of B, since we will be shifting it
+ {
+ int bOff = aLen;
+ System.arraycopy(B.m_ints, 0, c, bOff, bLen);
+ for (int bank = 1; bank < banks; ++bank)
+ {
+ shiftUp(c, aLen, c, bOff += bMax, bMax, bank);
+ }
+ }
+
+ /*
+ * The main loop analyzes the interleaved windows in A, and for each non-zero window
+ * a single word-array XOR is performed to a carefully selected slice of 'c'. The loop is
+ * breadth-first, checking the lowest window in each word, then looping again for the
+ * next higher window position.
+ */
+ int MASK = (1 << width) - 1;
+
+ int k = 0;
+ for (;;)
+ {
+ int aPos = 0;
+ do
+ {
+ long aVal = c[aPos] >>> k;
+ int bank = 0, bOff = aLen;
+ for (;;)
+ {
+ int index = (int)(aVal) & MASK;
+ if (index != 0)
+ {
+ /*
+ * Add to a 'c' buffer based on the bit-pattern of 'index'. Since A is in
+ * interleaved form, the bits represent the current B shifted by 0, 'positions',
+ * 'positions' * 2, ..., 'positions' * ('width' - 1)
+ */
+ add(c, aPos + ci[index], c, bOff, bMax);
+ }
+ if (++bank == banks)
+ {
+ break;
+ }
+ bOff += bMax;
+ aVal >>>= width;
+ }
+ }
+ while (++aPos < aLen);
+
+ if ((k += stride) >= top)
+ {
+ if (k >= 64)
+ {
+ break;
+ }
+
+ /*
+ * Adjustment for window setups with top == 63, the final bit (if any) is processed
+ * as the top-bit of a window
+ */
+ k = 64 - width;
+ MASK &= MASK << (top - k);
+ }
+
+ /*
+ * After each position has been checked for all words of A, B is shifted up 1 place
+ */
+ shiftUp(c, aLen, bTotal, banks);
+ }
+
+ int ciPos = ci.length;
+ while (--ciPos > 1)
+ {
+ if ((ciPos & 1L) == 0L)
+ {
+ /*
+ * For even numbers, shift contents and add to the half-position
+ */
+ addShiftedUp(c, ci[ciPos >>> 1], c, ci[ciPos], cLen, positions);
+ }
+ else
+ {
+ /*
+ * For odd numbers, 'distribute' contents to the result and the next-lowest position
+ */
+ distribute(c, ci[ciPos], ci[ciPos - 1], ci[1], cLen);
+ }
+ }
+
+ /*
+ * Finally the raw answer is collected, reduce it against the reduction coefficients
+ */
+ return reduceResult(c, ci[1], cLen, m, ks);
+ }
+
+ private static LongArray reduceResult(long[] buf, int off, int len, int m, int[] ks)
+ {
+ int rLen = reduceInPlace(buf, off, len, m, ks);
+ return new LongArray(buf, off, rLen);
+ }
+
+// private static void deInterleave(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+// {
+// for (int i = 0; i < count; ++i)
+// {
+// z[zOff + i] = deInterleave(x[zOff + i], rounds);
+// }
+// }
+//
+// private static long deInterleave(long x, int rounds)
+// {
+// while (--rounds >= 0)
+// {
+// x = deInterleave32(x & DEINTERLEAVE_MASK) | (deInterleave32((x >>> 1) & DEINTERLEAVE_MASK) << 32);
+// }
+// return x;
+// }
+//
+// private static long deInterleave32(long x)
+// {
+// x = (x | (x >>> 1)) & 0x3333333333333333L;
+// x = (x | (x >>> 2)) & 0x0F0F0F0F0F0F0F0FL;
+// x = (x | (x >>> 4)) & 0x00FF00FF00FF00FFL;
+// x = (x | (x >>> 8)) & 0x0000FFFF0000FFFFL;
+// x = (x | (x >>> 16)) & 0x00000000FFFFFFFFL;
+// return x;
+// }
+
+ private static int reduceInPlace(long[] buf, int off, int len, int m, int[] ks)
+ {
+ int mLen = (m + 63) >>> 6;
+ if (len < mLen)
+ {
+ return len;
+ }
+
+ int numBits = Math.min(len << 6, (m << 1) - 1); // TODO use actual degree?
+ int excessBits = (len << 6) - numBits;
+ while (excessBits >= 64)
+ {
+ --len;
+ excessBits -= 64;
+ }
+
+ int kLen = ks.length, kMax = ks[kLen - 1], kNext = kLen > 1 ? ks[kLen - 2] : 0;
+ int wordWiseLimit = Math.max(m, kMax + 64);
+ int vectorableWords = (excessBits + Math.min(numBits - wordWiseLimit, m - kNext)) >> 6;
+ if (vectorableWords > 1)
+ {
+ int vectorWiseWords = len - vectorableWords;
+ reduceVectorWise(buf, off, len, vectorWiseWords, m, ks);
+ while (len > vectorWiseWords)
+ {
+ buf[off + --len] = 0L;
+ }
+ numBits = vectorWiseWords << 6;
+ }
+
+ if (numBits > wordWiseLimit)
+ {
+ reduceWordWise(buf, off, len, wordWiseLimit, m, ks);
+ numBits = wordWiseLimit;
+ }
+
+ if (numBits > m)
+ {
+ reduceBitWise(buf, off, numBits, m, ks);
+ }
+
+ return mLen;
+ }
+
+ private static void reduceBitWise(long[] buf, int off, int bitlength, int m, int[] ks)
+ {
+ while (--bitlength >= m)
+ {
+ if (testBit(buf, off, bitlength))
+ {
+ reduceBit(buf, off, bitlength, m, ks);
+ }
+ }
+ }
+
+ private static void reduceBit(long[] buf, int off, int bit, int m, int[] ks)
+ {
+ flipBit(buf, off, bit);
+ int base = bit - m;
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipBit(buf, off, ks[j] + base);
+ }
+ flipBit(buf, off, base);
+ }
+
+ private static void reduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks)
+ {
+ int toPos = toBit >>> 6;
+
+ while (--len > toPos)
+ {
+ long word = buf[off + len];
+ if (word != 0)
+ {
+ buf[off + len] = 0;
+ reduceWord(buf, off, (len << 6), word, m, ks);
+ }
+ }
+
+ int partial = toBit & 0x3F;
+ long word = buf[off + toPos] >>> partial;
+ if (word != 0)
+ {
+ buf[off + toPos] ^= word << partial;
+ reduceWord(buf, off, toBit, word, m, ks);
+ }
+ }
+
+ private static void reduceWord(long[] buf, int off, int bit, long word, int m, int[] ks)
+ {
+ int offset = bit - m;
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipWord(buf, off, offset + ks[j], word);
+ }
+ flipWord(buf, off, offset, word);
+ }
+
+ private static void reduceVectorWise(long[] buf, int off, int len, int words, int m, int[] ks)
+ {
+ /*
+ * NOTE: It's important we go from highest coefficient to lowest, because for the highest
+ * one (only) we allow the ranges to partially overlap, and therefore any changes must take
+ * effect for the subsequent lower coefficients.
+ */
+ int baseBit = (words << 6) - m;
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipVector(buf, off, buf, off + words, len - words, baseBit + ks[j]);
+ }
+ flipVector(buf, off, buf, off + words, len - words, baseBit);
+ }
+
+ private static void flipVector(long[] x, int xOff, long[] y, int yOff, int yLen, int bits)
+ {
+ xOff += bits >>> 6;
+ bits &= 0x3F;
+
+ if (bits == 0)
+ {
+ add(x, xOff, y, yOff, yLen);
+ }
+ else
+ {
+ long carry = addShiftedDown(x, xOff + 1, y, yOff, yLen, 64 - bits);
+ x[xOff] ^= carry;
+ }
+ }
+
+ public LongArray modSquare(int m, int[] ks)
+ {
+ int len = getUsedLength();
+ if (len == 0)
+ {
+ return this;
+ }
+
+ int _2len = len << 1;
+ long[] r = new long[_2len];
+
+ int pos = 0;
+ while (pos < _2len)
+ {
+ long mi = m_ints[pos >>> 1];
+ r[pos++] = interleave2_32to64((int)mi);
+ r[pos++] = interleave2_32to64((int)(mi >>> 32));
+ }
+
+ return new LongArray(r, 0, reduceInPlace(r, 0, r.length, m, ks));
+ }
+
+// private LongArray modSquareN(int n, int m, int[] ks)
+// {
+// int len = getUsedLength();
+// if (len == 0)
+// {
+// return this;
+// }
+//
+// int mLen = (m + 63) >>> 6;
+// long[] r = new long[mLen << 1];
+// System.arraycopy(m_ints, 0, r, 0, len);
+//
+// while (--n >= 0)
+// {
+// squareInPlace(r, len, m, ks);
+// len = reduceInPlace(r, 0, r.length, m, ks);
+// }
+//
+// return new LongArray(r, 0, len);
+// }
+//
+// private static void squareInPlace(long[] x, int xLen, int m, int[] ks)
+// {
+// int pos = xLen << 1;
+// while (--xLen >= 0)
+// {
+// long xVal = x[xLen];
+// x[--pos] = interleave2_32to64((int)(xVal >>> 32));
+// x[--pos] = interleave2_32to64((int)xVal);
+// }
+// }
+
+ private static void interleave(long[] x, int xOff, long[] z, int zOff, int count, int width)
+ {
+ switch (width)
+ {
+ case 3:
+ interleave3(x, xOff, z, zOff, count);
+ break;
+ case 5:
+ interleave5(x, xOff, z, zOff, count);
+ break;
+ case 7:
+ interleave7(x, xOff, z, zOff, count);
+ break;
+ default:
+ interleave2_n(x, xOff, z, zOff, count, bitLengths[width] - 1);
+ break;
+ }
+ }
+
+ private static void interleave3(long[] x, int xOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave3(x[xOff + i]);
+ }
+ }
+
+ private static long interleave3(long x)
+ {
+ long z = x & (1L << 63);
+ return z
+ | interleave3_21to63((int)x & 0x1FFFFF)
+ | interleave3_21to63((int)(x >>> 21) & 0x1FFFFF) << 1
+ | interleave3_21to63((int)(x >>> 42) & 0x1FFFFF) << 2;
+
+// int zPos = 0, wPos = 0, xPos = 0;
+// for (;;)
+// {
+// z |= ((x >>> xPos) & 1L) << zPos;
+// if (++zPos == 63)
+// {
+// String sz2 = Long.toBinaryString(z);
+// return z;
+// }
+// if ((xPos += 21) >= 63)
+// {
+// xPos = ++wPos;
+// }
+// }
+ }
+
+ private static long interleave3_21to63(int x)
+ {
+ int r00 = INTERLEAVE3_TABLE[x & 0x7F];
+ int r21 = INTERLEAVE3_TABLE[(x >>> 7) & 0x7F];
+ int r42 = INTERLEAVE3_TABLE[x >>> 14];
+ return (r42 & 0xFFFFFFFFL) << 42 | (r21 & 0xFFFFFFFFL) << 21 | (r00 & 0xFFFFFFFFL);
+ }
+
+ private static void interleave5(long[] x, int xOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave5(x[xOff + i]);
+ }
+ }
+
+ private static long interleave5(long x)
+ {
+ return interleave3_13to65((int)x & 0x1FFF)
+ | interleave3_13to65((int)(x >>> 13) & 0x1FFF) << 1
+ | interleave3_13to65((int)(x >>> 26) & 0x1FFF) << 2
+ | interleave3_13to65((int)(x >>> 39) & 0x1FFF) << 3
+ | interleave3_13to65((int)(x >>> 52) & 0x1FFF) << 4;
+
+// long z = 0;
+// int zPos = 0, wPos = 0, xPos = 0;
+// for (;;)
+// {
+// z |= ((x >>> xPos) & 1L) << zPos;
+// if (++zPos == 64)
+// {
+// return z;
+// }
+// if ((xPos += 13) >= 64)
+// {
+// xPos = ++wPos;
+// }
+// }
+ }
+
+ private static long interleave3_13to65(int x)
+ {
+ int r00 = INTERLEAVE5_TABLE[x & 0x7F];
+ int r35 = INTERLEAVE5_TABLE[x >>> 7];
+ return (r35 & 0xFFFFFFFFL) << 35 | (r00 & 0xFFFFFFFFL);
+ }
+
+ private static void interleave7(long[] x, int xOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave7(x[xOff + i]);
+ }
+ }
+
+ private static long interleave7(long x)
+ {
+ long z = x & (1L << 63);
+ return z
+ | INTERLEAVE7_TABLE[(int)x & 0x1FF]
+ | INTERLEAVE7_TABLE[(int)(x >>> 9) & 0x1FF] << 1
+ | INTERLEAVE7_TABLE[(int)(x >>> 18) & 0x1FF] << 2
+ | INTERLEAVE7_TABLE[(int)(x >>> 27) & 0x1FF] << 3
+ | INTERLEAVE7_TABLE[(int)(x >>> 36) & 0x1FF] << 4
+ | INTERLEAVE7_TABLE[(int)(x >>> 45) & 0x1FF] << 5
+ | INTERLEAVE7_TABLE[(int)(x >>> 54) & 0x1FF] << 6;
+
+// int zPos = 0, wPos = 0, xPos = 0;
+// for (;;)
+// {
+// z |= ((x >>> xPos) & 1L) << zPos;
+// if (++zPos == 63)
+// {
+// return z;
+// }
+// if ((xPos += 9) >= 63)
+// {
+// xPos = ++wPos;
+// }
+// }
+ }
+
+ private static void interleave2_n(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave2_n(x[xOff + i], rounds);
+ }
+ }
+
+ private static long interleave2_n(long x, int rounds)
+ {
+ while (rounds > 1)
+ {
+ rounds -= 2;
+ x = interleave4_16to64((int)x & 0xFFFF)
+ | interleave4_16to64((int)(x >>> 16) & 0xFFFF) << 1
+ | interleave4_16to64((int)(x >>> 32) & 0xFFFF) << 2
+ | interleave4_16to64((int)(x >>> 48) & 0xFFFF) << 3;
+ }
+ if (rounds > 0)
+ {
+ x = interleave2_32to64((int)x) | interleave2_32to64((int)(x >>> 32)) << 1;
+ }
+ return x;
+ }
+
+ private static long interleave4_16to64(int x)
+ {
+ int r00 = INTERLEAVE4_TABLE[x & 0xFF];
+ int r32 = INTERLEAVE4_TABLE[x >>> 8];
+ return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+ }
+
+ private static long interleave2_32to64(int x)
+ {
+ int r00 = INTERLEAVE2_TABLE[x & 0xFF] | INTERLEAVE2_TABLE[(x >>> 8) & 0xFF] << 16;
+ int r32 = INTERLEAVE2_TABLE[(x >>> 16) & 0xFF] | INTERLEAVE2_TABLE[x >>> 24] << 16;
+ return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+ }
+
+// private static LongArray expItohTsujii2(LongArray B, int n, int m, int[] ks)
+// {
+// LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+// int scale = 1;
+//
+// int numTerms = n;
+// while (numTerms > 1)
+// {
+// if ((numTerms & 1) != 0)
+// {
+// t3 = t3.modMultiply(t1, m, ks);
+// t1 = t1.modSquareN(scale, m, ks);
+// }
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// numTerms >>>= 1; scale <<= 1;
+// }
+//
+// return t3.modMultiply(t1, m, ks);
+// }
+//
+// private static LongArray expItohTsujii23(LongArray B, int n, int m, int[] ks)
+// {
+// LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+// int scale = 1;
+//
+// int numTerms = n;
+// while (numTerms > 1)
+// {
+// boolean m03 = numTerms % 3 == 0;
+// boolean m14 = !m03 && (numTerms & 1) != 0;
+//
+// if (m14)
+// {
+// t3 = t3.modMultiply(t1, m, ks);
+// t1 = t1.modSquareN(scale, m, ks);
+// }
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+//
+// if (m03)
+// {
+// t2 = t2.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// numTerms /= 3; scale *= 3;
+// }
+// else
+// {
+// numTerms >>>= 1; scale <<= 1;
+// }
+// }
+//
+// return t3.modMultiply(t1, m, ks);
+// }
+//
+// private static LongArray expItohTsujii235(LongArray B, int n, int m, int[] ks)
+// {
+// LongArray t1 = B, t4 = new LongArray(new long[]{ 1L });
+// int scale = 1;
+//
+// int numTerms = n;
+// while (numTerms > 1)
+// {
+// if (numTerms % 5 == 0)
+// {
+//// t1 = expItohTsujii23(t1, 5, m, ks);
+//
+// LongArray t3 = t1;
+// t1 = t1.modSquareN(scale, m, ks);
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// t2 = t1.modSquareN(scale << 1, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+//
+// t1 = t1.modMultiply(t3, m, ks);
+//
+// numTerms /= 5; scale *= 5;
+// continue;
+// }
+//
+// boolean m03 = numTerms % 3 == 0;
+// boolean m14 = !m03 && (numTerms & 1) != 0;
+//
+// if (m14)
+// {
+// t4 = t4.modMultiply(t1, m, ks);
+// t1 = t1.modSquareN(scale, m, ks);
+// }
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+//
+// if (m03)
+// {
+// t2 = t2.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// numTerms /= 3; scale *= 3;
+// }
+// else
+// {
+// numTerms >>>= 1; scale <<= 1;
+// }
+// }
+//
+// return t4.modMultiply(t1, m, ks);
+// }
+
+ public LongArray modInverse(int m, int[] ks)
+ {
+ /*
+ * Fermat's Little Theorem
+ */
+// LongArray A = this;
+// LongArray B = A.modSquare(m, ks);
+// LongArray R0 = B, R1 = B;
+// for (int i = 2; i < m; ++i)
+// {
+// R1 = R1.modSquare(m, ks);
+// R0 = R0.modMultiply(R1, m, ks);
+// }
+//
+// return R0;
+
+ /*
+ * Itoh-Tsujii
+ */
+// LongArray B = modSquare(m, ks);
+// switch (m)
+// {
+// case 409:
+// return expItohTsujii23(B, m - 1, m, ks);
+// case 571:
+// return expItohTsujii235(B, m - 1, m, ks);
+// case 163:
+// case 233:
+// case 283:
+// default:
+// return expItohTsujii2(B, m - 1, m, ks);
+// }
+
+ /*
+ * Inversion in F2m using the extended Euclidean algorithm
+ *
+ * Input: A nonzero polynomial a(z) of degree at most m-1
+ * Output: a(z)^(-1) mod f(z)
+ */
+ int uzDegree = degree();
+ if (uzDegree == 1)
+ {
+ return this;
+ }
+
+ // u(z) := a(z)
+ LongArray uz = (LongArray)clone();
+
+ int t = (m + 63) >>> 6;
+
+ // v(z) := f(z)
+ LongArray vz = new LongArray(t);
+ reduceBit(vz.m_ints, 0, m, m, ks);
+
+ // g1(z) := 1, g2(z) := 0
+ LongArray g1z = new LongArray(t);
+ g1z.m_ints[0] = 1L;
+ LongArray g2z = new LongArray(t);
+
+ int[] uvDeg = new int[]{ uzDegree, m + 1 };
+ LongArray[] uv = new LongArray[]{ uz, vz };
+
+ int[] ggDeg = new int[]{ 1, 0 };
+ LongArray[] gg = new LongArray[]{ g1z, g2z };
+
+ int b = 1;
+ int duv1 = uvDeg[b];
+ int dgg1 = ggDeg[b];
+ int j = duv1 - uvDeg[1 - b];
+
+ for (;;)
+ {
+ if (j < 0)
+ {
+ j = -j;
+ uvDeg[b] = duv1;
+ ggDeg[b] = dgg1;
+ b = 1 - b;
+ duv1 = uvDeg[b];
+ dgg1 = ggDeg[b];
+ }
+
+ uv[b].addShiftedByBitsSafe(uv[1 - b], uvDeg[1 - b], j);
+
+ int duv2 = uv[b].degreeFrom(duv1);
+ if (duv2 == 0)
+ {
+ return gg[1 - b];
+ }
+
+ {
+ int dgg2 = ggDeg[1 - b];
+ gg[b].addShiftedByBitsSafe(gg[1 - b], dgg2, j);
+ dgg2 += j;
+
+ if (dgg2 > dgg1)
+ {
+ dgg1 = dgg2;
+ }
+ else if (dgg2 == dgg1)
+ {
+ dgg1 = gg[b].degreeFrom(dgg1);
+ }
+ }
+
+ j += (duv2 - duv1);
+ duv1 = duv2;
+ }
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof LongArray))
+ {
+ return false;
+ }
+ LongArray other = (LongArray) o;
+ int usedLen = getUsedLength();
+ if (other.getUsedLength() != usedLen)
+ {
+ return false;
+ }
+ for (int i = 0; i < usedLen; i++)
+ {
+ if (m_ints[i] != other.m_ints[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int usedLen = getUsedLength();
+ int hash = 1;
+ for (int i = 0; i < usedLen; i++)
+ {
+ long mi = m_ints[i];
+ hash *= 31;
+ hash ^= (int)mi;
+ hash *= 31;
+ hash ^= (int)(mi >>> 32);
+ }
+ return hash;
+ }
+
+ public Object clone()
+ {
+ return new LongArray(Arrays.clone(m_ints));
+ }
+
+ public String toString()
+ {
+ int i = getUsedLength();
+ if (i == 0)
+ {
+ return "0";
+ }
+
+ StringBuffer sb = new StringBuffer(Long.toBinaryString(m_ints[--i]));
+ while (--i >= 0)
+ {
+ String s = Long.toBinaryString(m_ints[i]);
+
+ // Add leading zeroes, except for highest significant word
+ int len = s.length();
+ if (len < 64)
+ {
+ sb.append(ZEROES.substring(len));
+ }
+
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java
new file mode 100644
index 000000000..e626103ae
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java
@@ -0,0 +1,77 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left) using
+ * mixed coordinates.
+ */
+public class MixedNafR2LMultiplier extends AbstractECMultiplier
+{
+ protected int additionCoord, doublingCoord;
+
+ /**
+ * By default, addition will be done in Jacobian coordinates, and doubling will be done in
+ * Modified Jacobian coordinates (independent of the original coordinate system of each point).
+ */
+ public MixedNafR2LMultiplier()
+ {
+ this(ECCurve.COORD_JACOBIAN, ECCurve.COORD_JACOBIAN_MODIFIED);
+ }
+
+ public MixedNafR2LMultiplier(int additionCoord, int doublingCoord)
+ {
+ this.additionCoord = additionCoord;
+ this.doublingCoord = doublingCoord;
+ }
+
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECCurve curveOrig = p.getCurve();
+
+ ECCurve curveAdd = configureCurve(curveOrig, additionCoord);
+ ECCurve curveDouble = configureCurve(curveOrig, doublingCoord);
+
+ int[] naf = WNafUtil.generateCompactNaf(k);
+
+ ECPoint Ra = curveAdd.getInfinity();
+ ECPoint Td = curveDouble.importPoint(p);
+
+ int zeroes = 0;
+ for (int i = 0; i < naf.length; ++i)
+ {
+ int ni = naf[i];
+ int digit = ni >> 16;
+ zeroes += ni & 0xFFFF;
+
+ Td = Td.timesPow2(zeroes);
+
+ ECPoint Tj = curveAdd.importPoint(Td);
+ if (digit < 0)
+ {
+ Tj = Tj.negate();
+ }
+
+ Ra = Ra.add(Tj);
+
+ zeroes = 1;
+ }
+
+ return curveOrig.importPoint(Ra);
+ }
+
+ protected ECCurve configureCurve(ECCurve c, int coord)
+ {
+ if (c.getCoordinateSystem() == coord)
+ {
+ return c;
+ }
+
+ if (!c.supportsCoordinateSystem(coord))
+ {
+ throw new IllegalArgumentException("Coordinate system " + coord + " not supported by this curve");
+ }
+
+ return c.configure().setCoordinateSystem(coord).create();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java
new file mode 100644
index 000000000..bc4f7a005
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java
@@ -0,0 +1,25 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class MontgomeryLadderMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Montgomery ladder.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint[] R = new ECPoint[]{ p.getCurve().getInfinity(), p };
+
+ int n = k.bitLength();
+ int i = n;
+ while (--i >= 0)
+ {
+ int b = k.testBit(i) ? 1 : 0;
+ int bp = 1 - b;
+ R[bp] = R[bp].add(R[b]);
+ R[b] = R[b].twice();
+ }
+ return R[0];
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java
new file mode 100644
index 000000000..b2aac42c1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java
@@ -0,0 +1,30 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (left-to-right).
+ */
+public class NafL2RMultiplier extends AbstractECMultiplier
+{
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ int[] naf = WNafUtil.generateCompactNaf(k);
+
+ ECPoint addP = p.normalize(), subP = addP.negate();
+
+ ECPoint R = p.getCurve().getInfinity();
+
+ int i = naf.length;
+ while (--i >= 0)
+ {
+ int ni = naf[i];
+ int digit = ni >> 16, zeroes = ni & 0xFFFF;
+
+ R = R.twicePlus(digit < 0 ? subP : addP);
+ R = R.timesPow2(zeroes);
+ }
+
+ return R;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java
new file mode 100644
index 000000000..78aecb025
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java
@@ -0,0 +1,31 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left).
+ */
+public class NafR2LMultiplier extends AbstractECMultiplier
+{
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ int[] naf = WNafUtil.generateCompactNaf(k);
+
+ ECPoint R0 = p.getCurve().getInfinity(), R1 = p;
+
+ int zeroes = 0;
+ for (int i = 0; i < naf.length; ++i)
+ {
+ int ni = naf[i];
+ int digit = ni >> 16;
+ zeroes += ni & 0xFFFF;
+
+ R1 = R1.timesPow2(zeroes);
+ R0 = R0.add(digit < 0 ? R1.negate() : R1);
+
+ zeroes = 1;
+ }
+
+ return R0;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java
new file mode 100644
index 000000000..72af12c46
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java
@@ -0,0 +1,10 @@
+package org.spongycastle.math.ec;
+
+/**
+ * Interface for classes storing precomputation data for multiplication
+ * algorithms. Used as a Memento (see GOF patterns) by e.g.
+ * WNafL2RMultiplier
.
+ */
+public interface PreCompInfo
+{
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java
new file mode 100644
index 000000000..6c03173f9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java
@@ -0,0 +1,37 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ReferenceMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Simple shift-and-add multiplication. Serves as reference implementation
+ * to verify (possibly faster) implementations in
+ * {@link org.spongycastle.math.ec.ECPoint ECPoint}.
+ *
+ * @param p The point to multiply.
+ * @param k The factor by which to multiply.
+ * @return The result of the point multiplication k * p
.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint q = p.getCurve().getInfinity();
+ int t = k.bitLength();
+ if (t > 0)
+ {
+ if (k.testBit(0))
+ {
+ q = p;
+ }
+ for (int i = 1; i < t; i++)
+ {
+ p = p.twice();
+ if (k.testBit(i))
+ {
+ q = q.add(p);
+ }
+ }
+ }
+ return q;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java
new file mode 100644
index 000000000..f882abc63
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java
@@ -0,0 +1,253 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class representing a simple version of a big decimal. A
+ * SimpleBigDecimal
is basically a
+ * {@link java.math.BigInteger BigInteger} with a few digits on the right of
+ * the decimal point. The number of (binary) digits on the right of the decimal
+ * point is called the scale
of the SimpleBigDecimal
.
+ * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
+ * automatically, but must be set manually. All SimpleBigDecimal
s
+ * taking part in the same arithmetic operation must have equal scale. The
+ * result of a multiplication of two SimpleBigDecimal
s returns a
+ * SimpleBigDecimal
with double scale.
+ */
+class SimpleBigDecimal
+ //extends Number // not in J2ME - add compatibility class?
+{
+ private static final long serialVersionUID = 1L;
+
+ private final BigInteger bigInt;
+ private final int scale;
+
+ /**
+ * Returns a SimpleBigDecimal
representing the same numerical
+ * value as value
.
+ * @param value The value of the SimpleBigDecimal
to be
+ * created.
+ * @param scale The scale of the SimpleBigDecimal
to be
+ * created.
+ * @return The such created SimpleBigDecimal
.
+ */
+ public static SimpleBigDecimal getInstance(BigInteger value, int scale)
+ {
+ return new SimpleBigDecimal(value.shiftLeft(scale), scale);
+ }
+
+ /**
+ * Constructor for SimpleBigDecimal
. The value of the
+ * constructed SimpleBigDecimal
equals bigInt /
+ * 2scale
.
+ * @param bigInt The bigInt
value parameter.
+ * @param scale The scale of the constructed SimpleBigDecimal
.
+ */
+ public SimpleBigDecimal(BigInteger bigInt, int scale)
+ {
+ if (scale < 0)
+ {
+ throw new IllegalArgumentException("scale may not be negative");
+ }
+
+ this.bigInt = bigInt;
+ this.scale = scale;
+ }
+
+ private SimpleBigDecimal(SimpleBigDecimal limBigDec)
+ {
+ bigInt = limBigDec.bigInt;
+ scale = limBigDec.scale;
+ }
+
+ private void checkScale(SimpleBigDecimal b)
+ {
+ if (scale != b.scale)
+ {
+ throw new IllegalArgumentException("Only SimpleBigDecimal of " +
+ "same scale allowed in arithmetic operations");
+ }
+ }
+
+ public SimpleBigDecimal adjustScale(int newScale)
+ {
+ if (newScale < 0)
+ {
+ throw new IllegalArgumentException("scale may not be negative");
+ }
+
+ if (newScale == scale)
+ {
+ return new SimpleBigDecimal(this);
+ }
+
+ return new SimpleBigDecimal(bigInt.shiftLeft(newScale - scale),
+ newScale);
+ }
+
+ public SimpleBigDecimal add(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ return new SimpleBigDecimal(bigInt.add(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal add(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.add(b.shiftLeft(scale)), scale);
+ }
+
+ public SimpleBigDecimal negate()
+ {
+ return new SimpleBigDecimal(bigInt.negate(), scale);
+ }
+
+ public SimpleBigDecimal subtract(SimpleBigDecimal b)
+ {
+ return add(b.negate());
+ }
+
+ public SimpleBigDecimal subtract(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.subtract(b.shiftLeft(scale)),
+ scale);
+ }
+
+ public SimpleBigDecimal multiply(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ return new SimpleBigDecimal(bigInt.multiply(b.bigInt), scale + scale);
+ }
+
+ public SimpleBigDecimal multiply(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.multiply(b), scale);
+ }
+
+ public SimpleBigDecimal divide(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ BigInteger dividend = bigInt.shiftLeft(scale);
+ return new SimpleBigDecimal(dividend.divide(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal divide(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.divide(b), scale);
+ }
+
+ public SimpleBigDecimal shiftLeft(int n)
+ {
+ return new SimpleBigDecimal(bigInt.shiftLeft(n), scale);
+ }
+
+ public int compareTo(SimpleBigDecimal val)
+ {
+ checkScale(val);
+ return bigInt.compareTo(val.bigInt);
+ }
+
+ public int compareTo(BigInteger val)
+ {
+ return bigInt.compareTo(val.shiftLeft(scale));
+ }
+
+ public BigInteger floor()
+ {
+ return bigInt.shiftRight(scale);
+ }
+
+ public BigInteger round()
+ {
+ SimpleBigDecimal oneHalf = new SimpleBigDecimal(ECConstants.ONE, 1);
+ return add(oneHalf.adjustScale(scale)).floor();
+ }
+
+ public int intValue()
+ {
+ return floor().intValue();
+ }
+
+ public long longValue()
+ {
+ return floor().longValue();
+ }
+ /* NON-J2ME compliant.
+ public double doubleValue()
+ {
+ return Double.valueOf(toString()).doubleValue();
+ }
+
+ public float floatValue()
+ {
+ return Float.valueOf(toString()).floatValue();
+ }
+ */
+ public int getScale()
+ {
+ return scale;
+ }
+
+ public String toString()
+ {
+ if (scale == 0)
+ {
+ return bigInt.toString();
+ }
+
+ BigInteger floorBigInt = floor();
+
+ BigInteger fract = bigInt.subtract(floorBigInt.shiftLeft(scale));
+ if (bigInt.signum() == -1)
+ {
+ fract = ECConstants.ONE.shiftLeft(scale).subtract(fract);
+ }
+
+ if ((floorBigInt.signum() == -1) && (!(fract.equals(ECConstants.ZERO))))
+ {
+ floorBigInt = floorBigInt.add(ECConstants.ONE);
+ }
+ String leftOfPoint = floorBigInt.toString();
+
+ char[] fractCharArr = new char[scale];
+ String fractStr = fract.toString(2);
+ int fractLen = fractStr.length();
+ int zeroes = scale - fractLen;
+ for (int i = 0; i < zeroes; i++)
+ {
+ fractCharArr[i] = '0';
+ }
+ for (int j = 0; j < fractLen; j++)
+ {
+ fractCharArr[zeroes + j] = fractStr.charAt(j);
+ }
+ String rightOfPoint = new String(fractCharArr);
+
+ StringBuffer sb = new StringBuffer(leftOfPoint);
+ sb.append(".");
+ sb.append(rightOfPoint);
+
+ return sb.toString();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if (!(o instanceof SimpleBigDecimal))
+ {
+ return false;
+ }
+
+ SimpleBigDecimal other = (SimpleBigDecimal)o;
+ return ((bigInt.equals(other.bigInt)) && (scale == other.scale));
+ }
+
+ public int hashCode()
+ {
+ return bigInt.hashCode() ^ scale;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/Tnaf.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/Tnaf.java
new file mode 100644
index 000000000..6c5a0db56
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/Tnaf.java
@@ -0,0 +1,832 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class holding methods for point multiplication based on the window
+ * τ-adic nonadjacent form (WTNAF). The algorithms are based on the
+ * paper "Improved Algorithms for Arithmetic on Anomalous Binary Curves"
+ * by Jerome A. Solinas. The paper first appeared in the Proceedings of
+ * Crypto 1997.
+ */
+class Tnaf
+{
+ private static final BigInteger MINUS_ONE = ECConstants.ONE.negate();
+ private static final BigInteger MINUS_TWO = ECConstants.TWO.negate();
+ private static final BigInteger MINUS_THREE = ECConstants.THREE.negate();
+
+ /**
+ * The window width of WTNAF. The standard value of 4 is slightly less
+ * than optimal for running time, but keeps space requirements for
+ * precomputation low. For typical curves, a value of 5 or 6 results in
+ * a better running time. When changing this value, the
+ * αu
's must be computed differently, see
+ * e.g. "Guide to Elliptic Curve Cryptography", Darrel Hankerson,
+ * Alfred Menezes, Scott Vanstone, Springer-Verlag New York Inc., 2004,
+ * p. 121-122
+ */
+ public static final byte WIDTH = 4;
+
+ /**
+ * 24
+ */
+ public static final byte POW_2_WIDTH = 16;
+
+ /**
+ * The αu
's for a=0
as an array
+ * of ZTauElement
s.
+ */
+ public static final ZTauElement[] alpha0 = {
+ null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
+ new ZTauElement(MINUS_THREE, MINUS_ONE), null,
+ new ZTauElement(MINUS_ONE, MINUS_ONE), null,
+ new ZTauElement(ECConstants.ONE, MINUS_ONE), null
+ };
+
+ /**
+ * The αu
's for a=0
as an array
+ * of TNAFs.
+ */
+ public static final byte[][] alpha0Tnaf = {
+ null, {1}, null, {-1, 0, 1}, null, {1, 0, 1}, null, {-1, 0, 0, 1}
+ };
+
+ /**
+ * The αu
's for a=1
as an array
+ * of ZTauElement
s.
+ */
+ public static final ZTauElement[] alpha1 = {null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
+ new ZTauElement(MINUS_THREE, ECConstants.ONE), null,
+ new ZTauElement(MINUS_ONE, ECConstants.ONE), null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ONE), null
+ };
+
+ /**
+ * The αu
's for a=1
as an array
+ * of TNAFs.
+ */
+ public static final byte[][] alpha1Tnaf = {
+ null, {1}, null, {-1, 0, 1}, null, {1, 0, 1}, null, {-1, 0, 0, -1}
+ };
+
+ /**
+ * Computes the norm of an element λ
of
+ * Z[τ]
.
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param lambda The element λ
of
+ * Z[τ]
.
+ * @return The norm of λ
.
+ */
+ public static BigInteger norm(final byte mu, ZTauElement lambda)
+ {
+ BigInteger norm;
+
+ // s1 = u^2
+ BigInteger s1 = lambda.u.multiply(lambda.u);
+
+ // s2 = u * v
+ BigInteger s2 = lambda.u.multiply(lambda.v);
+
+ // s3 = 2 * v^2
+ BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.add(s2).add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.subtract(s2).add(s3);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Computes the norm of an element λ
of
+ * R[τ]
, where λ = u + vτ
+ * and u
and u
are real numbers (elements of
+ * R
).
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param u The real part of the element λ
of
+ * R[τ]
.
+ * @param v The τ
-adic part of the element
+ * λ
of R[τ]
.
+ * @return The norm of λ
.
+ */
+ public static SimpleBigDecimal norm(final byte mu, SimpleBigDecimal u,
+ SimpleBigDecimal v)
+ {
+ SimpleBigDecimal norm;
+
+ // s1 = u^2
+ SimpleBigDecimal s1 = u.multiply(u);
+
+ // s2 = u * v
+ SimpleBigDecimal s2 = u.multiply(v);
+
+ // s3 = 2 * v^2
+ SimpleBigDecimal s3 = v.multiply(v).shiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.add(s2).add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.subtract(s2).add(s3);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Rounds an element λ
of R[τ]
+ * to an element of Z[τ]
, such that their difference
+ * has minimal norm. λ
is given as
+ * λ = λ0 + λ1τ
.
+ * @param lambda0 The component λ0
.
+ * @param lambda1 The component λ1
.
+ * @param mu The parameter μ
of the elliptic curve. Must
+ * equal 1 or -1.
+ * @return The rounded element of Z[τ]
.
+ * @throws IllegalArgumentException if lambda0
and
+ * lambda1
do not have same scale.
+ */
+ public static ZTauElement round(SimpleBigDecimal lambda0,
+ SimpleBigDecimal lambda1, byte mu)
+ {
+ int scale = lambda0.getScale();
+ if (lambda1.getScale() != scale)
+ {
+ throw new IllegalArgumentException("lambda0 and lambda1 do not " +
+ "have same scale");
+ }
+
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger f0 = lambda0.round();
+ BigInteger f1 = lambda1.round();
+
+ SimpleBigDecimal eta0 = lambda0.subtract(f0);
+ SimpleBigDecimal eta1 = lambda1.subtract(f1);
+
+ // eta = 2*eta0 + mu*eta1
+ SimpleBigDecimal eta = eta0.add(eta0);
+ if (mu == 1)
+ {
+ eta = eta.add(eta1);
+ }
+ else
+ {
+ // mu == -1
+ eta = eta.subtract(eta1);
+ }
+
+ // check1 = eta0 - 3*mu*eta1
+ // check2 = eta0 + 4*mu*eta1
+ SimpleBigDecimal threeEta1 = eta1.add(eta1).add(eta1);
+ SimpleBigDecimal fourEta1 = threeEta1.add(eta1);
+ SimpleBigDecimal check1;
+ SimpleBigDecimal check2;
+ if (mu == 1)
+ {
+ check1 = eta0.subtract(threeEta1);
+ check2 = eta0.add(fourEta1);
+ }
+ else
+ {
+ // mu == -1
+ check1 = eta0.add(threeEta1);
+ check2 = eta0.subtract(fourEta1);
+ }
+
+ byte h0 = 0;
+ byte h1 = 0;
+
+ // if eta >= 1
+ if (eta.compareTo(ECConstants.ONE) >= 0)
+ {
+ if (check1.compareTo(MINUS_ONE) < 0)
+ {
+ h1 = mu;
+ }
+ else
+ {
+ h0 = 1;
+ }
+ }
+ else
+ {
+ // eta < 1
+ if (check2.compareTo(ECConstants.TWO) >= 0)
+ {
+ h1 = mu;
+ }
+ }
+
+ // if eta < -1
+ if (eta.compareTo(MINUS_ONE) < 0)
+ {
+ if (check1.compareTo(ECConstants.ONE) >= 0)
+ {
+ h1 = (byte)-mu;
+ }
+ else
+ {
+ h0 = -1;
+ }
+ }
+ else
+ {
+ // eta >= -1
+ if (check2.compareTo(MINUS_TWO) < 0)
+ {
+ h1 = (byte)-mu;
+ }
+ }
+
+ BigInteger q0 = f0.add(BigInteger.valueOf(h0));
+ BigInteger q1 = f1.add(BigInteger.valueOf(h1));
+ return new ZTauElement(q0, q1);
+ }
+
+ /**
+ * Approximate division by n
. For an integer
+ * k
, the value λ = s k / n
is
+ * computed to c
bits of accuracy.
+ * @param k The parameter k
.
+ * @param s The curve parameter s0
or
+ * s1
.
+ * @param vm The Lucas Sequence element Vm
.
+ * @param a The parameter a
of the elliptic curve.
+ * @param m The bit length of the finite field
+ * Fm
.
+ * @param c The number of bits of accuracy, i.e. the scale of the returned
+ * SimpleBigDecimal
.
+ * @return The value λ = s k / n
computed to
+ * c
bits of accuracy.
+ */
+ public static SimpleBigDecimal approximateDivisionByN(BigInteger k,
+ BigInteger s, BigInteger vm, byte a, int m, int c)
+ {
+ int _k = (m + 5)/2 + c;
+ BigInteger ns = k.shiftRight(m - _k - 2 + a);
+
+ BigInteger gs = s.multiply(ns);
+
+ BigInteger hs = gs.shiftRight(m);
+
+ BigInteger js = vm.multiply(hs);
+
+ BigInteger gsPlusJs = gs.add(js);
+ BigInteger ls = gsPlusJs.shiftRight(_k-c);
+ if (gsPlusJs.testBit(_k-c-1))
+ {
+ // round up
+ ls = ls.add(ECConstants.ONE);
+ }
+
+ return new SimpleBigDecimal(ls, c);
+ }
+
+ /**
+ * Computes the τ
-adic NAF (non-adjacent form) of an
+ * element λ
of Z[τ]
.
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param lambda The element λ
of
+ * Z[τ]
.
+ * @return The τ
-adic NAF of λ
.
+ */
+ public static byte[] tauAdicNaf(byte mu, ZTauElement lambda)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger norm = norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.bitLength();
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 : 34;
+
+ // The array holding the TNAF
+ byte[] u = new byte[maxLength];
+ int i = 0;
+
+ // The actual length of the TNAF
+ int length = 0;
+
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+
+ while(!((r0.equals(ECConstants.ZERO)) && (r1.equals(ECConstants.ZERO))))
+ {
+ // If r0 is odd
+ if (r0.testBit(0))
+ {
+ u[i] = (byte) ECConstants.TWO.subtract((r0.subtract(r1.shiftLeft(1))).mod(ECConstants.FOUR)).intValue();
+
+ // r0 = r0 - u[i]
+ if (u[i] == 1)
+ {
+ r0 = r0.clearBit(0);
+ }
+ else
+ {
+ // u[i] == -1
+ r0 = r0.add(ECConstants.ONE);
+ }
+ length = i;
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+ BigInteger s = r0.shiftRight(1);
+ if (mu == 1)
+ {
+ r0 = r1.add(s);
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.subtract(s);
+ }
+
+ r1 = t.shiftRight(1).negate();
+ i++;
+ }
+
+ length++;
+
+ // Reduce the TNAF array to its actual length
+ byte[] tnaf = new byte[length];
+ System.arraycopy(u, 0, tnaf, 0, length);
+ return tnaf;
+ }
+
+ /**
+ * Applies the operation τ()
to an
+ * ECPoint.F2m
.
+ * @param p The ECPoint.F2m to which τ()
is applied.
+ * @return τ(p)
+ */
+ public static ECPoint.F2m tau(ECPoint.F2m p)
+ {
+ return p.tau();
+ }
+
+ /**
+ * Returns the parameter μ
of the elliptic curve.
+ * @param curve The elliptic curve from which to obtain μ
.
+ * The curve must be a Koblitz curve, i.e. a
equals
+ * 0
or 1
and b
equals
+ * 1
.
+ * @return μ
of the elliptic curve.
+ * @throws IllegalArgumentException if the given ECCurve is not a Koblitz
+ * curve.
+ */
+ public static byte getMu(ECCurve.F2m curve)
+ {
+ if (!curve.isKoblitz())
+ {
+ throw new IllegalArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible");
+ }
+
+ if (curve.getA().isZero())
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Calculates the Lucas Sequence elements Uk-1
and
+ * Uk
or Vk-1
and
+ * Vk
.
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param k The index of the second element of the Lucas Sequence to be
+ * returned.
+ * @param doV If set to true, computes Vk-1
and
+ * Vk
, otherwise Uk-1
and
+ * Uk
.
+ * @return An array with 2 elements, containing Uk-1
+ * and Uk
or Vk-1
+ * and Vk
.
+ */
+ public static BigInteger[] getLucas(byte mu, int k, boolean doV)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger u0;
+ BigInteger u1;
+ BigInteger u2;
+
+ if (doV)
+ {
+ u0 = ECConstants.TWO;
+ u1 = BigInteger.valueOf(mu);
+ }
+ else
+ {
+ u0 = ECConstants.ZERO;
+ u1 = ECConstants.ONE;
+ }
+
+ for (int i = 1; i < k; i++)
+ {
+ // u2 = mu*u1 - 2*u0;
+ BigInteger s = null;
+ if (mu == 1)
+ {
+ s = u1;
+ }
+ else
+ {
+ // mu == -1
+ s = u1.negate();
+ }
+
+ u2 = s.subtract(u0.shiftLeft(1));
+ u0 = u1;
+ u1 = u2;
+// System.out.println(i + ": " + u2);
+// System.out.println();
+ }
+
+ BigInteger[] retVal = {u0, u1};
+ return retVal;
+ }
+
+ /**
+ * Computes the auxiliary value tw
. If the width is
+ * 4, then for mu = 1
, tw = 6
and for
+ * mu = -1
, tw = 10
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param w The window width of the WTNAF.
+ * @return the auxiliary value tw
+ */
+ public static BigInteger getTw(byte mu, int w)
+ {
+ if (w == 4)
+ {
+ if (mu == 1)
+ {
+ return BigInteger.valueOf(6);
+ }
+ else
+ {
+ // mu == -1
+ return BigInteger.valueOf(10);
+ }
+ }
+ else
+ {
+ // For w <> 4, the values must be computed
+ BigInteger[] us = getLucas(mu, w, false);
+ BigInteger twoToW = ECConstants.ZERO.setBit(w);
+ BigInteger u1invert = us[1].modInverse(twoToW);
+ BigInteger tw;
+ tw = ECConstants.TWO.multiply(us[0]).multiply(u1invert).mod(twoToW);
+// System.out.println("mu = " + mu);
+// System.out.println("tw = " + tw);
+ return tw;
+ }
+ }
+
+ /**
+ * Computes the auxiliary values s0
and
+ * s1
used for partial modular reduction.
+ * @param curve The elliptic curve for which to compute
+ * s0
and s1
.
+ * @throws IllegalArgumentException if curve
is not a
+ * Koblitz curve (Anomalous Binary Curve, ABC).
+ */
+ public static BigInteger[] getSi(ECCurve.F2m curve)
+ {
+ if (!curve.isKoblitz())
+ {
+ throw new IllegalArgumentException("si is defined for Koblitz curves only");
+ }
+
+ int m = curve.getM();
+ int a = curve.getA().toBigInteger().intValue();
+ byte mu = curve.getMu();
+ int h = curve.getH().intValue();
+ int index = m + 3 - a;
+ BigInteger[] ui = getLucas(mu, index, false);
+
+ BigInteger dividend0;
+ BigInteger dividend1;
+ if (mu == 1)
+ {
+ dividend0 = ECConstants.ONE.subtract(ui[1]);
+ dividend1 = ECConstants.ONE.subtract(ui[0]);
+ }
+ else if (mu == -1)
+ {
+ dividend0 = ECConstants.ONE.add(ui[1]);
+ dividend1 = ECConstants.ONE.add(ui[0]);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger[] si = new BigInteger[2];
+
+ if (h == 2)
+ {
+ si[0] = dividend0.shiftRight(1);
+ si[1] = dividend1.shiftRight(1).negate();
+ }
+ else if (h == 4)
+ {
+ si[0] = dividend0.shiftRight(2);
+ si[1] = dividend1.shiftRight(2).negate();
+ }
+ else
+ {
+ throw new IllegalArgumentException("h (Cofactor) must be 2 or 4");
+ }
+
+ return si;
+ }
+
+ /**
+ * Partial modular reduction modulo
+ * (τm - 1)/(τ - 1)
.
+ * @param k The integer to be reduced.
+ * @param m The bitlength of the underlying finite field.
+ * @param a The parameter a
of the elliptic curve.
+ * @param s The auxiliary values s0
and
+ * s1
.
+ * @param mu The parameter μ of the elliptic curve.
+ * @param c The precision (number of bits of accuracy) of the partial
+ * modular reduction.
+ * @return ρ := k partmod (τm - 1)/(τ - 1)
+ */
+ public static ZTauElement partModReduction(BigInteger k, int m, byte a,
+ BigInteger[] s, byte mu, byte c)
+ {
+ // d0 = s[0] + mu*s[1]; mu is either 1 or -1
+ BigInteger d0;
+ if (mu == 1)
+ {
+ d0 = s[0].add(s[1]);
+ }
+ else
+ {
+ d0 = s[0].subtract(s[1]);
+ }
+
+ BigInteger[] v = getLucas(mu, m, true);
+ BigInteger vm = v[1];
+
+ SimpleBigDecimal lambda0 = approximateDivisionByN(
+ k, s[0], vm, a, m, c);
+
+ SimpleBigDecimal lambda1 = approximateDivisionByN(
+ k, s[1], vm, a, m, c);
+
+ ZTauElement q = round(lambda0, lambda1, mu);
+
+ // r0 = n - d0*q0 - 2*s1*q1
+ BigInteger r0 = k.subtract(d0.multiply(q.u)).subtract(
+ BigInteger.valueOf(2).multiply(s[1]).multiply(q.v));
+
+ // r1 = s1*q0 - s0*q1
+ BigInteger r1 = s[1].multiply(q.u).subtract(s[0].multiply(q.v));
+
+ return new ZTauElement(r0, r1);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by a BigInteger
using the reduced τ
-adic
+ * NAF (RTNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param k The BigInteger
by which to multiply p
.
+ * @return k * p
+ */
+ public static ECPoint.F2m multiplyRTnaf(ECPoint.F2m p, BigInteger k)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m) p.getCurve();
+ int m = curve.getM();
+ byte a = (byte) curve.getA().toBigInteger().intValue();
+ byte mu = curve.getMu();
+ BigInteger[] s = curve.getSi();
+ ZTauElement rho = partModReduction(k, m, a, s, mu, (byte)10);
+
+ return multiplyTnaf(p, rho);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element λ
of Z[τ]
+ * using the τ
-adic NAF (TNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param lambda The element λ
of
+ * Z[τ]
.
+ * @return λ * p
+ */
+ public static ECPoint.F2m multiplyTnaf(ECPoint.F2m p, ZTauElement lambda)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ byte mu = curve.getMu();
+ byte[] u = tauAdicNaf(mu, lambda);
+
+ ECPoint.F2m q = multiplyFromTnaf(p, u);
+
+ return q;
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element λ
of Z[τ]
+ * using the τ
-adic NAF (TNAF) method, given the TNAF
+ * of λ
.
+ * @param p The ECPoint.F2m to multiply.
+ * @param u The the TNAF of λ
..
+ * @return λ * p
+ */
+ public static ECPoint.F2m multiplyFromTnaf(ECPoint.F2m p, byte[] u)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ ECPoint.F2m q = (ECPoint.F2m) curve.getInfinity();
+ for (int i = u.length - 1; i >= 0; i--)
+ {
+ q = tau(q);
+ if (u[i] == 1)
+ {
+ q = (ECPoint.F2m)q.addSimple(p);
+ }
+ else if (u[i] == -1)
+ {
+ q = (ECPoint.F2m)q.subtractSimple(p);
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Computes the [τ]
-adic window NAF of an element
+ * λ
of Z[τ]
.
+ * @param mu The parameter μ of the elliptic curve.
+ * @param lambda The element λ
of
+ * Z[τ]
of which to compute the
+ * [τ]
-adic NAF.
+ * @param width The window width of the resulting WNAF.
+ * @param pow2w 2width.
+ * @param tw The auxiliary value tw
.
+ * @param alpha The αu
's for the window width.
+ * @return The [τ]
-adic window NAF of
+ * λ
.
+ */
+ public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda,
+ byte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger norm = norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.bitLength();
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 + width : 34 + width;
+
+ // The array holding the TNAF
+ byte[] u = new byte[maxLength];
+
+ // 2^(width - 1)
+ BigInteger pow2wMin1 = pow2w.shiftRight(1);
+
+ // Split lambda into two BigIntegers to simplify calculations
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+ int i = 0;
+
+ // while lambda <> (0, 0)
+ while (!((r0.equals(ECConstants.ZERO))&&(r1.equals(ECConstants.ZERO))))
+ {
+ // if r0 is odd
+ if (r0.testBit(0))
+ {
+ // uUnMod = r0 + r1*tw mod 2^width
+ BigInteger uUnMod
+ = r0.add(r1.multiply(tw)).mod(pow2w);
+
+ byte uLocal;
+ // if uUnMod >= 2^(width - 1)
+ if (uUnMod.compareTo(pow2wMin1) >= 0)
+ {
+ uLocal = (byte) uUnMod.subtract(pow2w).intValue();
+ }
+ else
+ {
+ uLocal = (byte) uUnMod.intValue();
+ }
+ // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+
+ u[i] = uLocal;
+ boolean s = true;
+ if (uLocal < 0)
+ {
+ s = false;
+ uLocal = (byte)-uLocal;
+ }
+ // uLocal is now >= 0
+
+ if (s)
+ {
+ r0 = r0.subtract(alpha[uLocal].u);
+ r1 = r1.subtract(alpha[uLocal].v);
+ }
+ else
+ {
+ r0 = r0.add(alpha[uLocal].u);
+ r1 = r1.add(alpha[uLocal].v);
+ }
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+
+ if (mu == 1)
+ {
+ r0 = r1.add(r0.shiftRight(1));
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.subtract(r0.shiftRight(1));
+ }
+ r1 = t.shiftRight(1).negate();
+ i++;
+ }
+ return u;
+ }
+
+ /**
+ * Does the precomputation for WTNAF multiplication.
+ * @param p The ECPoint
for which to do the precomputation.
+ * @param a The parameter a
of the elliptic curve.
+ * @return The precomputation array for p
.
+ */
+ public static ECPoint.F2m[] getPreComp(ECPoint.F2m p, byte a)
+ {
+ ECPoint.F2m[] pu;
+ pu = new ECPoint.F2m[16];
+ pu[1] = p;
+ byte[][] alphaTnaf;
+ if (a == 0)
+ {
+ alphaTnaf = Tnaf.alpha0Tnaf;
+ }
+ else
+ {
+ // a == 1
+ alphaTnaf = Tnaf.alpha1Tnaf;
+ }
+
+ int precompLen = alphaTnaf.length;
+ for (int i = 3; i < precompLen; i = i + 2)
+ {
+ pu[i] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
+ }
+
+ p.getCurve().normalizeAll(pu);
+
+ return pu;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java
new file mode 100644
index 000000000..0f64450b7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java
@@ -0,0 +1,101 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
+ * algorithm.
+ */
+public class WNafL2RMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Multiplies this
by an integer k
using the
+ * Window NAF method.
+ * @param k The integer by which this
is multiplied.
+ * @return A new ECPoint
which equals this
+ * multiplied by k
.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ // Clamp the window width in the range [2, 16]
+ int width = Math.max(2, Math.min(16, getWindowSize(k.bitLength())));
+
+ WNafPreCompInfo wnafPreCompInfo = WNafUtil.precompute(p, width, true);
+ ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+ ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
+
+ int[] wnaf = WNafUtil.generateCompactWindowNaf(width, k);
+
+ ECPoint R = p.getCurve().getInfinity();
+
+ int i = wnaf.length;
+
+ /*
+ * NOTE This code optimizes the first window using the precomputed points to substitute an
+ * addition for 2 or more doublings.
+ */
+ if (i > 1)
+ {
+ int wi = wnaf[--i];
+ int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+ int n = Math.abs(digit);
+ ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+
+ /*
+ * NOTE: We use this optimization conservatively, since some coordinate systems have
+ * significantly cheaper doubling relative to addition.
+ *
+ * (n << 2) selects precomputed values in the lower half of the table
+ * (n << 3) selects precomputed values in the lower quarter of the table
+ */
+ //if ((n << 2) < (1 << width))
+ if ((n << 3) < (1 << width))
+ {
+ int highest = LongArray.bitLengths[n];
+ int lowBits = n ^ (1 << (highest - 1));
+ int scale = width - highest;
+
+ int i1 = ((1 << (width - 1)) - 1);
+ int i2 = (lowBits << scale) + 1;
+ R = table[i1 >>> 1].add(table[i2 >>> 1]);
+
+ zeroes -= scale;
+
+// System.out.println("Optimized: 2^" + scale + " * " + n + " = " + i1 + " + " + i2);
+ }
+ else
+ {
+ R = table[n >>> 1];
+ }
+
+ R = R.timesPow2(zeroes);
+ }
+
+ while (i > 0)
+ {
+ int wi = wnaf[--i];
+ int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+ int n = Math.abs(digit);
+ ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+ ECPoint r = table[n >>> 1];
+
+ R = R.twicePlus(r);
+ R = R.timesPow2(zeroes);
+ }
+
+ return R;
+ }
+
+ /**
+ * Determine window width to use for a scalar multiplication of the given size.
+ *
+ * @param bits the bit-length of the scalar to multiply by
+ * @return the window size to use
+ */
+ protected int getWindowSize(int bits)
+ {
+ return WNafUtil.getWindowSize(bits);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java
new file mode 100644
index 000000000..4a7b7e31c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java
@@ -0,0 +1,56 @@
+package org.spongycastle.math.ec;
+
+/**
+ * Class holding precomputation data for the WNAF (Window Non-Adjacent Form)
+ * algorithm.
+ */
+public class WNafPreCompInfo implements PreCompInfo
+{
+ /**
+ * Array holding the precomputed ECPoint
s used for a Window
+ * NAF multiplication.
+ */
+ private ECPoint[] preComp = null;
+
+ /**
+ * Array holding the negations of the precomputed ECPoint
s used
+ * for a Window NAF multiplication.
+ */
+ private ECPoint[] preCompNeg = null;
+
+ /**
+ * Holds an ECPoint
representing twice(this). Used for the
+ * Window NAF multiplication to create or extend the precomputed values.
+ */
+ private ECPoint twiceP = null;
+
+ protected ECPoint[] getPreComp()
+ {
+ return preComp;
+ }
+
+ protected ECPoint[] getPreCompNeg()
+ {
+ return preCompNeg;
+ }
+
+ protected void setPreComp(ECPoint[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ protected void setPreCompNeg(ECPoint[] preCompNeg)
+ {
+ this.preCompNeg = preCompNeg;
+ }
+
+ protected ECPoint getTwiceP()
+ {
+ return twiceP;
+ }
+
+ protected void setTwiceP(ECPoint twiceP)
+ {
+ this.twiceP = twiceP;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafUtil.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafUtil.java
new file mode 100644
index 000000000..9215f9490
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WNafUtil.java
@@ -0,0 +1,393 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public abstract class WNafUtil
+{
+ private static int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 };
+
+ public static int[] generateCompactNaf(BigInteger k)
+ {
+ if ((k.bitLength() >>> 16) != 0)
+ {
+ throw new IllegalArgumentException("'k' must have bitlength < 2^16");
+ }
+
+ BigInteger _3k = k.shiftLeft(1).add(k);
+
+ int digits = _3k.bitLength() - 1;
+ int[] naf = new int[(digits + 1) >> 1];
+
+ int length = 0, zeroes = 0;
+ for (int i = 1; i <= digits; ++i)
+ {
+ boolean _3kBit = _3k.testBit(i);
+ boolean kBit = k.testBit(i);
+
+ if (_3kBit == kBit)
+ {
+ ++zeroes;
+ }
+ else
+ {
+ int digit = kBit ? -1 : 1;
+ naf[length++] = (digit << 16) | zeroes;
+ zeroes = 0;
+ }
+ }
+
+ if (naf.length > length)
+ {
+ naf = trim(naf, length);
+ }
+
+ return naf;
+ }
+
+ public static int[] generateCompactWindowNaf(int width, BigInteger k)
+ {
+ if (width == 2)
+ {
+ return generateCompactNaf(k);
+ }
+
+ if (width < 2 || width > 16)
+ {
+ throw new IllegalArgumentException("'width' must be in the range [2, 16]");
+ }
+ if ((k.bitLength() >>> 16) != 0)
+ {
+ throw new IllegalArgumentException("'k' must have bitlength < 2^16");
+ }
+
+ int[] wnaf = new int[k.bitLength() / width + 1];
+
+ // 2^width and a mask and sign bit set accordingly
+ int pow2 = 1 << width;
+ int mask = pow2 - 1;
+ int sign = pow2 >>> 1;
+
+ boolean carry = false;
+ int length = 0, pos = 0;
+
+ while (pos <= k.bitLength())
+ {
+ if (k.testBit(pos) == carry)
+ {
+ ++pos;
+ continue;
+ }
+
+ k = k.shiftRight(pos);
+
+ int digit = k.intValue() & mask;
+ if (carry)
+ {
+ ++digit;
+ }
+
+ carry = (digit & sign) != 0;
+ if (carry)
+ {
+ digit -= pow2;
+ }
+
+ int zeroes = length > 0 ? pos - 1 : pos;
+ wnaf[length++] = (digit << 16) | zeroes;
+ pos = width;
+ }
+
+ // Reduce the WNAF array to its actual length
+ if (wnaf.length > length)
+ {
+ wnaf = trim(wnaf, length);
+ }
+
+ return wnaf;
+ }
+
+ public static byte[] generateJSF(BigInteger g, BigInteger h)
+ {
+ int digits = Math.max(g.bitLength(), h.bitLength()) + 1;
+ byte[] jsf = new byte[digits];
+
+ BigInteger k0 = g, k1 = h;
+ int j = 0, d0 = 0, d1 = 0;
+
+ while (k0.signum() > 0 || k1.signum() > 0 || d0 > 0 || d1 > 0)
+ {
+ int n0 = (k0.intValue() + d0) & 7, n1 = (k1.intValue() + d1) & 7;
+
+ int u0 = n0 & 1;
+ if (u0 != 0)
+ {
+ u0 -= (n0 & 2);
+ if ((n0 + u0) == 4 && (n1 & 3) == 2)
+ {
+ u0 = -u0;
+ }
+ }
+
+ int u1 = n1 & 1;
+ if (u1 != 0)
+ {
+ u1 -= (n1 & 2);
+ if ((n1 + u1) == 4 && (n0 & 3) == 2)
+ {
+ u1 = -u1;
+ }
+ }
+
+ if ((d0 << 1) == 1 + u0)
+ {
+ d0 = 1 - d0;
+ }
+ if ((d1 << 1) == 1 + u1)
+ {
+ d1 = 1 - d1;
+ }
+
+ k0 = k0.shiftRight(1);
+ k1 = k1.shiftRight(1);
+
+ jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF));
+ }
+
+ // Reduce the JSF array to its actual length
+ if (jsf.length > j)
+ {
+ jsf = trim(jsf, j);
+ }
+
+ return jsf;
+ }
+
+ public static byte[] generateNaf(BigInteger k)
+ {
+ BigInteger _3k = k.shiftLeft(1).add(k);
+
+ int digits = _3k.bitLength() - 1;
+ byte[] naf = new byte[digits];
+
+ for (int i = 1; i <= digits; ++i)
+ {
+ boolean _3kBit = _3k.testBit(i);
+ boolean kBit = k.testBit(i);
+
+ naf[i - 1] = (byte)(_3kBit == kBit ? 0 : kBit ? -1 : 1);
+ }
+
+ return naf;
+ }
+
+ /**
+ * Computes the Window NAF (non-adjacent Form) of an integer.
+ * @param width The width w
of the Window NAF. The width is
+ * defined as the minimal number w
, such that for any
+ * w
consecutive digits in the resulting representation, at
+ * most one is non-zero.
+ * @param k The integer of which the Window NAF is computed.
+ * @return The Window NAF of the given width, such that the following holds:
+ * k = ∑i=0l-1 ki2i
+ *
, where the ki
denote the elements of the
+ * returned byte[]
.
+ */
+ public static byte[] generateWindowNaf(int width, BigInteger k)
+ {
+ if (width == 2)
+ {
+ return generateNaf(k);
+ }
+
+ if (width < 2 || width > 8)
+ {
+ throw new IllegalArgumentException("'width' must be in the range [2, 8]");
+ }
+
+ byte[] wnaf = new byte[k.bitLength() + 1];
+
+ // 2^width and a mask and sign bit set accordingly
+ int pow2 = 1 << width;
+ int mask = pow2 - 1;
+ int sign = pow2 >>> 1;
+
+ boolean carry = false;
+ int length = 0, pos = 0;
+
+ while (pos <= k.bitLength())
+ {
+ if (k.testBit(pos) == carry)
+ {
+ ++pos;
+ continue;
+ }
+
+ k = k.shiftRight(pos);
+
+ int digit = k.intValue() & mask;
+ if (carry)
+ {
+ ++digit;
+ }
+
+ carry = (digit & sign) != 0;
+ if (carry)
+ {
+ digit -= pow2;
+ }
+
+ length += (length > 0) ? pos - 1 : pos;
+ wnaf[length++] = (byte)digit;
+ pos = width;
+ }
+
+ // Reduce the WNAF array to its actual length
+ if (wnaf.length > length)
+ {
+ wnaf = trim(wnaf, length);
+ }
+
+ return wnaf;
+ }
+
+ public static WNafPreCompInfo getWNafPreCompInfo(PreCompInfo preCompInfo)
+ {
+ if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo))
+ {
+ return (WNafPreCompInfo)preCompInfo;
+ }
+
+ return new WNafPreCompInfo();
+ }
+
+ /**
+ * Determine window width to use for a scalar multiplication of the given size.
+ *
+ * @param bits the bit-length of the scalar to multiply by
+ * @return the window size to use
+ */
+ public static int getWindowSize(int bits)
+ {
+ return getWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS);
+ }
+
+ /**
+ * Determine window width to use for a scalar multiplication of the given size.
+ *
+ * @param bits the bit-length of the scalar to multiply by
+ * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width
+ * @return the window size to use
+ */
+ public static int getWindowSize(int bits, int[] windowSizeCutoffs)
+ {
+ int w = 0;
+ for (; w < windowSizeCutoffs.length; ++w)
+ {
+ if (bits < windowSizeCutoffs[w])
+ {
+ break;
+ }
+ }
+ return w + 2;
+ }
+
+ public static WNafPreCompInfo precompute(ECPoint p, int width, boolean includeNegated)
+ {
+ ECCurve c = p.getCurve();
+ WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p));
+
+ ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+ if (preComp == null)
+ {
+ preComp = new ECPoint[]{ p };
+ }
+
+ int preCompLen = preComp.length;
+ int reqPreCompLen = 1 << Math.max(0, width - 2);
+
+ if (preCompLen < reqPreCompLen)
+ {
+ ECPoint twiceP = wnafPreCompInfo.getTwiceP();
+ if (twiceP == null)
+ {
+ twiceP = preComp[0].twice().normalize();
+ wnafPreCompInfo.setTwiceP(twiceP);
+ }
+
+ preComp = resizeTable(preComp, reqPreCompLen);
+
+ /*
+ * TODO Okeya/Sakurai paper has precomputation trick and "Montgomery's Trick" to speed this up.
+ * Also, co-Z arithmetic could avoid the subsequent normalization too.
+ */
+ for (int i = preCompLen; i < reqPreCompLen; i++)
+ {
+ /*
+ * Compute the new ECPoints for the precomputation array. The values 1, 3, 5, ...,
+ * 2^(width-1)-1 times p are computed
+ */
+ preComp[i] = twiceP.add(preComp[i - 1]);
+ }
+
+ /*
+ * Having oft-used operands in affine form makes operations faster.
+ */
+ c.normalizeAll(preComp);
+ }
+
+ wnafPreCompInfo.setPreComp(preComp);
+
+ if (includeNegated)
+ {
+ ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
+
+ int pos;
+ if (preCompNeg == null)
+ {
+ pos = 0;
+ preCompNeg = new ECPoint[reqPreCompLen];
+ }
+ else
+ {
+ pos = preCompNeg.length;
+ if (pos < reqPreCompLen)
+ {
+ preCompNeg = resizeTable(preCompNeg, reqPreCompLen);
+ }
+ }
+
+ while (pos < reqPreCompLen)
+ {
+ preCompNeg[pos] = preComp[pos].negate();
+ ++pos;
+ }
+
+ wnafPreCompInfo.setPreCompNeg(preCompNeg);
+ }
+
+ c.setPreCompInfo(p, wnafPreCompInfo);
+
+ return wnafPreCompInfo;
+ }
+
+ private static byte[] trim(byte[] a, int length)
+ {
+ byte[] result = new byte[length];
+ System.arraycopy(a, 0, result, 0, result.length);
+ return result;
+ }
+
+ private static int[] trim(int[] a, int length)
+ {
+ int[] result = new int[length];
+ System.arraycopy(a, 0, result, 0, result.length);
+ return result;
+ }
+
+ private static ECPoint[] resizeTable(ECPoint[] a, int length)
+ {
+ ECPoint[] result = new ECPoint[length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ return result;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java
new file mode 100644
index 000000000..351cfa19b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java
@@ -0,0 +1,118 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WTNAF (Window
+ * τ
-adic Non-Adjacent Form) algorithm.
+ */
+public class WTauNafMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by k
using the reduced τ
-adic NAF (RTNAF)
+ * method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param k The integer by which to multiply k
.
+ * @return p
multiplied by k
.
+ */
+ protected ECPoint multiplyPositive(ECPoint point, BigInteger k)
+ {
+ if (!(point instanceof ECPoint.F2m))
+ {
+ throw new IllegalArgumentException("Only ECPoint.F2m can be " +
+ "used in WTauNafMultiplier");
+ }
+
+ ECPoint.F2m p = (ECPoint.F2m)point;
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ int m = curve.getM();
+ byte a = curve.getA().toBigInteger().byteValue();
+ byte mu = curve.getMu();
+ BigInteger[] s = curve.getSi();
+
+ ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
+
+ return multiplyWTnaf(p, rho, curve.getPreCompInfo(p), a, mu);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element λ
of Z[τ]
using
+ * the τ
-adic NAF (TNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param lambda The element λ
of
+ * Z[τ]
of which to compute the
+ * [τ]
-adic NAF.
+ * @return p
multiplied by λ
.
+ */
+ private ECPoint.F2m multiplyWTnaf(ECPoint.F2m p, ZTauElement lambda,
+ PreCompInfo preCompInfo, byte a, byte mu)
+ {
+ ZTauElement[] alpha;
+ if (a == 0)
+ {
+ alpha = Tnaf.alpha0;
+ }
+ else
+ {
+ // a == 1
+ alpha = Tnaf.alpha1;
+ }
+
+ BigInteger tw = Tnaf.getTw(mu, Tnaf.WIDTH);
+
+ byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH,
+ BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha);
+
+ return multiplyFromWTnaf(p, u, preCompInfo);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element λ
of Z[τ]
+ * using the window τ
-adic NAF (TNAF) method, given the
+ * WTNAF of λ
.
+ * @param p The ECPoint.F2m to multiply.
+ * @param u The the WTNAF of λ
..
+ * @return λ * p
+ */
+ private static ECPoint.F2m multiplyFromWTnaf(ECPoint.F2m p, byte[] u,
+ PreCompInfo preCompInfo)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ byte a = curve.getA().toBigInteger().byteValue();
+
+ ECPoint.F2m[] pu;
+ if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo))
+ {
+ pu = Tnaf.getPreComp(p, a);
+ curve.setPreCompInfo(p, new WTauNafPreCompInfo(pu));
+ }
+ else
+ {
+ pu = ((WTauNafPreCompInfo)preCompInfo).getPreComp();
+ }
+
+ // q = infinity
+ ECPoint.F2m q = (ECPoint.F2m) p.getCurve().getInfinity();
+ for (int i = u.length - 1; i >= 0; i--)
+ {
+ q = Tnaf.tau(q);
+ if (u[i] != 0)
+ {
+ if (u[i] > 0)
+ {
+ q = q.addSimple(pu[u[i]]);
+ }
+ else
+ {
+ // u[i] < 0
+ q = q.subtractSimple(pu[-u[i]]);
+ }
+ }
+ }
+
+ return q;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java
new file mode 100644
index 000000000..2373ff7a9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java
@@ -0,0 +1,39 @@
+package org.spongycastle.math.ec;
+
+/**
+ * Class holding precomputation data for the WTNAF (Window
+ * τ
-adic Non-Adjacent Form) algorithm.
+ */
+class WTauNafPreCompInfo implements PreCompInfo
+{
+ /**
+ * Array holding the precomputed ECPoint.F2m
s used for the
+ * WTNAF multiplication in
+ * {@link org.spongycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}
.
+ */
+ private ECPoint.F2m[] preComp = null;
+
+ /**
+ * Constructor for WTauNafPreCompInfo
+ * @param preComp Array holding the precomputed ECPoint.F2m
s
+ * used for the WTNAF multiplication in
+ * {@link org.spongycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}
.
+ */
+ WTauNafPreCompInfo(ECPoint.F2m[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ /**
+ * @return the array holding the precomputed ECPoint.F2m
s
+ * used for the WTNAF multiplication in
+ * {@link org.spongycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}
.
+ */
+ protected ECPoint.F2m[] getPreComp()
+ {
+ return preComp;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java
new file mode 100644
index 000000000..543bb581f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java
@@ -0,0 +1,29 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ZSignedDigitL2RMultiplier extends AbstractECMultiplier
+{
+ /**
+ * 'Zeroless' Signed Digit Left-to-Right.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint addP = p.normalize(), subP = addP.negate();
+
+ ECPoint R0 = addP;
+
+ int n = k.bitLength();
+ int s = k.getLowestSetBit();
+
+ int i = n;
+ while (--i > s)
+ {
+ R0 = R0.twicePlus(k.testBit(i) ? addP : subP);
+ }
+
+ R0 = R0.timesPow2(s);
+
+ return R0;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java
new file mode 100644
index 000000000..e9147ea7d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java
@@ -0,0 +1,30 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ZSignedDigitR2LMultiplier extends AbstractECMultiplier
+{
+ /**
+ * 'Zeroless' Signed Digit Right-to-Left.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint R0 = p.getCurve().getInfinity(), R1 = p;
+
+ int n = k.bitLength();
+ int s = k.getLowestSetBit();
+
+ R1 = R1.timesPow2(s);
+
+ int i = s;
+ while (++i < n)
+ {
+ R0 = R0.add(k.testBit(i) ? R1 : R1.negate());
+ R1 = R1.twice();
+ }
+
+ R0 = R0.add(R1);
+
+ return R0;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZTauElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZTauElement.java
new file mode 100644
index 000000000..3fd5e21f8
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/math/ec/ZTauElement.java
@@ -0,0 +1,37 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class representing an element of Z[τ]
. Let
+ * λ
be an element of Z[τ]
. Then
+ * λ
is given as λ = u + vτ
. The
+ * components u
and v
may be used directly, there
+ * are no accessor methods.
+ * Immutable class.
+ */
+class ZTauElement
+{
+ /**
+ * The "real" part of λ
.
+ */
+ public final BigInteger u;
+
+ /**
+ * The "τ
-adic" part of λ
.
+ */
+ public final BigInteger v;
+
+ /**
+ * Constructor for an element λ
of
+ * Z[τ]
.
+ * @param u The "real" part of λ
.
+ * @param v The "τ
-adic" part of
+ * λ
.
+ */
+ public ZTauElement(BigInteger u, BigInteger v)
+ {
+ this.u = u;
+ this.v = v;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java
new file mode 100644
index 000000000..b56974ad4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java
@@ -0,0 +1,1312 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.pqc.crypto.gmss.GMSSLeaf;
+import org.spongycastle.pqc.crypto.gmss.GMSSParameters;
+import org.spongycastle.pqc.crypto.gmss.GMSSRootCalc;
+import org.spongycastle.pqc.crypto.gmss.GMSSRootSig;
+import org.spongycastle.pqc.crypto.gmss.Treehash;
+
+public class GMSSPrivateKey
+ extends ASN1Object
+{
+ private ASN1Primitive primitive;
+
+ private GMSSPrivateKey(ASN1Sequence mtsPrivateKey)
+ {
+ // --- Decode + * GMSSPublicKey ::= SEQUENCE{ + * version INTEGER + * publicKey OCTET STRING + * } + *+ */ +public class GMSSPublicKey + extends ASN1Object +{ + private ASN1Integer version; + private byte[] publicKey; + + private GMSSPublicKey(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("size of seq = " + seq.size()); + } + + this.version = ASN1Integer.getInstance(seq.getObjectAt(0)); + this.publicKey = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets(); + } + + public GMSSPublicKey(byte[] publicKeyBytes) + { + this.version = new ASN1Integer(0); + this.publicKey = publicKeyBytes; + } + + public static GMSSPublicKey getInstance(Object o) + { + if (o instanceof GMSSPublicKey) + { + return (GMSSPublicKey)o; + } + else if (o != null) + { + return new GMSSPublicKey(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public byte[] getPublicKey() + { + return Arrays.clone(publicKey); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(new DEROctetString(publicKey)); + + return new DERSequence(v); + } +} diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java new file mode 100644 index 000000000..5f6a8b9d1 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java @@ -0,0 +1,173 @@ +package org.spongycastle.pqc.asn1; + +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; + +import org.spongycastle.pqc.math.linearalgebra.GF2Matrix; +import org.spongycastle.pqc.math.linearalgebra.GF2mField; +import org.spongycastle.pqc.math.linearalgebra.Permutation; +import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM; + +public class McElieceCCA2PrivateKey + extends ASN1Object +{ + private ASN1ObjectIdentifier oid; + private int n; + private int k; + private byte[] encField; + private byte[] encGp; + private byte[] encP; + private byte[] encH; + private byte[][] encqInv; + + + public McElieceCCA2PrivateKey(ASN1ObjectIdentifier oid, int n, int k, GF2mField field, PolynomialGF2mSmallM goppaPoly, Permutation p, GF2Matrix h, PolynomialGF2mSmallM[] qInv) + { + this.oid = oid; + this.n = n; + this.k = k; + this.encField = field.getEncoded(); + this.encGp = goppaPoly.getEncoded(); + this.encP = p.getEncoded(); + this.encH = h.getEncoded(); + this.encqInv = new byte[qInv.length][]; + + for (int i = 0; i != qInv.length; i++) + { + encqInv[i] = qInv[i].getEncoded(); + } + } + + private McElieceCCA2PrivateKey(ASN1Sequence seq) + { + oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0)); + + BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue(); + n = bigN.intValue(); + + BigInteger bigK = ((ASN1Integer)seq.getObjectAt(2)).getValue(); + k = bigK.intValue(); + + encField = ((ASN1OctetString)seq.getObjectAt(3)).getOctets(); + + encGp = ((ASN1OctetString)seq.getObjectAt(4)).getOctets(); + + encP = ((ASN1OctetString)seq.getObjectAt(5)).getOctets(); + + encH = ((ASN1OctetString)seq.getObjectAt(6)).getOctets(); + + ASN1Sequence asnQInv = (ASN1Sequence)seq.getObjectAt(7); + encqInv = new byte[asnQInv.size()][]; + for (int i = 0; i < asnQInv.size(); i++) + { + encqInv[i] = ((ASN1OctetString)asnQInv.getObjectAt(i)).getOctets(); + } + } + + public ASN1ObjectIdentifier getOID() + { + return oid; + } + + public int getN() + { + return n; + } + + public int getK() + { + return k; + } + + public GF2mField getField() + { + return new GF2mField(encField); + } + + public PolynomialGF2mSmallM getGoppaPoly() + { + return new PolynomialGF2mSmallM(this.getField(), encGp); + } + + public Permutation getP() + { + return new Permutation(encP); + } + + public GF2Matrix getH() + { + return new GF2Matrix(encH); + } + + public PolynomialGF2mSmallM[] getQInv() + { + PolynomialGF2mSmallM[] qInv = new PolynomialGF2mSmallM[encqInv.length]; + GF2mField field = this.getField(); + + for (int i = 0; i < encqInv.length; i++) + { + qInv[i] = new PolynomialGF2mSmallM(field, encqInv[i]); + } + + return qInv; + } + + public ASN1Primitive toASN1Primitive() + { + + ASN1EncodableVector v = new ASN1EncodableVector(); + // encode
+ v.add(new DEROctetString(encP));
+
+ // encode
+ * { iso(1) identifier-organization(3) dod(6) internet(1) private(4) 1 8301 3 1 3 5 3 ... }
+ */
+public interface PQCObjectIdentifiers
+{
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2 */
+ public static final ASN1ObjectIdentifier rainbow = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.5.3.2");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.1 */
+ public static final ASN1ObjectIdentifier rainbowWithSha1 = rainbow.branch("1");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.2 */
+ public static final ASN1ObjectIdentifier rainbowWithSha224 = rainbow.branch("2");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.3 */
+ public static final ASN1ObjectIdentifier rainbowWithSha256 = rainbow.branch("3");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.4 */
+ public static final ASN1ObjectIdentifier rainbowWithSha384 = rainbow.branch("4");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.5 */
+ public static final ASN1ObjectIdentifier rainbowWithSha512 = rainbow.branch("5");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.3 */
+ public static final ASN1ObjectIdentifier gmss = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.3");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.3.1 */
+ public static final ASN1ObjectIdentifier gmssWithSha1 = gmss.branch("1");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.2 */
+ public static final ASN1ObjectIdentifier gmssWithSha224 = gmss.branch("2");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.3 */
+ public static final ASN1ObjectIdentifier gmssWithSha256 = gmss.branch("3");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.4 */
+ public static final ASN1ObjectIdentifier gmssWithSha384 = gmss.branch("4");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.5 */
+ public static final ASN1ObjectIdentifier gmssWithSha512 = gmss.branch("5");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.4.1 */
+ public static final ASN1ObjectIdentifier mcEliece = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.1");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.4.2 */
+ public static final ASN1ObjectIdentifier mcElieceCca2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.2");
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java
new file mode 100644
index 000000000..d5110a2c4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java
@@ -0,0 +1,140 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.util.Arrays;
+
+/**
+ *
+ * The exception extends ClassCastException to enable users to have a single handling case,
+ * only introducing specific handling of this one if required.
+ *
+ * The purpose of UrlBase64 encoding is to provide a compact encoding of binary
+ * data that is safe for use as an URL parameter. Base64 encoding does not
+ * produce encoded values that are safe for use in URLs, since "/" can be
+ * interpreted as a path delimiter; "+" is the encoded form of a space; and
+ * "=" is used to separate a name from the corresponding value in an URL
+ * parameter.
+ */
+public class UrlBase64
+{
+ private static final Encoder encoder = new UrlBase64Encoder();
+
+ /**
+ * Encode the input data producing a URL safe base 64 encoded byte array.
+ *
+ * @return a byte array containing the URL safe base 64 encoded data.
+ */
+ public static byte[] encode(
+ byte[] data)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ encoder.encode(data, 0, data.length, bOut);
+ }
+ catch (Exception e)
+ {
+ throw new EncoderException("exception encoding URL safe base64 data: " + e.getMessage(), e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * Encode the byte data writing it to the given output stream.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int encode(
+ byte[] data,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.encode(data, 0, data.length, out);
+ }
+
+ /**
+ * Decode the URL safe base 64 encoded input data - white space will be ignored.
+ *
+ * @return a byte array representing the decoded data.
+ */
+ public static byte[] decode(
+ byte[] data)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ encoder.decode(data, 0, data.length, bOut);
+ }
+ catch (Exception e)
+ {
+ throw new DecoderException("exception decoding URL safe base64 string: " + e.getMessage(), e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * decode the URL safe base 64 encoded byte data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int decode(
+ byte[] data,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.decode(data, 0, data.length, out);
+ }
+
+ /**
+ * decode the URL safe base 64 encoded String data - whitespace will be ignored.
+ *
+ * @return a byte array representing the decoded data.
+ */
+ public static byte[] decode(
+ String data)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ encoder.decode(data, bOut);
+ }
+ catch (Exception e)
+ {
+ throw new DecoderException("exception decoding URL safe base64 string: " + e.getMessage(), e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * Decode the URL safe base 64 encoded String data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int decode(
+ String data,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.decode(data, out);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/encoders/UrlBase64Encoder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/encoders/UrlBase64Encoder.java
new file mode 100644
index 000000000..dc6f1ed77
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/encoders/UrlBase64Encoder.java
@@ -0,0 +1,25 @@
+package org.spongycastle.util.encoders;
+
+/**
+ * Convert binary data to and from UrlBase64 encoding. This is identical to
+ * Base64 encoding, except that the padding character is "." and the other
+ * non-alphanumeric characters are "-" and "_" instead of "+" and "/".
+ *
+ * The purpose of UrlBase64 encoding is to provide a compact encoding of binary
+ * data that is safe for use as an URL parameter. Base64 encoding does not
+ * produce encoded values that are safe for use in URLs, since "/" can be
+ * interpreted as a path delimiter; "+" is the encoded form of a space; and
+ * "=" is used to separate a name from the corresponding value in an URL
+ * parameter.
+ */
+public class UrlBase64Encoder extends Base64Encoder
+{
+ public UrlBase64Encoder()
+ {
+ encodingTable[encodingTable.length - 2] = (byte) '-';
+ encodingTable[encodingTable.length - 1] = (byte) '_';
+ padding = (byte) '.';
+ // we must re-create the decoding table with the new encoded values.
+ initialiseDecodingTable();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/io/BufferingOutputStream.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/io/BufferingOutputStream.java
new file mode 100644
index 000000000..ae4abec41
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/io/BufferingOutputStream.java
@@ -0,0 +1,108 @@
+package org.spongycastle.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * An output stream that buffers data to be feed into an encapsulated output stream.
+ *
+ * The stream zeroes out the internal buffer on each flush.
+ *
+ * The
+ * Each element of the set is a
+ * All X.509 certificate extensions that a Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+ */
+ public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
+
+ /**
+ * more from PKCS#9
+ */
+ public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
+ public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
+
+ /**
+ * email address in Verisign certificates
+ */
+ public static final ASN1ObjectIdentifier E = EmailAddress;
+
+ /*
+ * others...
+ */
+ public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+
+ /**
+ * LDAP User id.
+ */
+ public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ private static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ private static final Hashtable DefaultLookUp = new Hashtable();
+
+ static
+ {
+ DefaultSymbols.put(C, "C");
+ DefaultSymbols.put(O, "O");
+ DefaultSymbols.put(T, "T");
+ DefaultSymbols.put(OU, "OU");
+ DefaultSymbols.put(CN, "CN");
+ DefaultSymbols.put(L, "L");
+ DefaultSymbols.put(ST, "ST");
+ DefaultSymbols.put(SN, "SERIALNUMBER");
+ DefaultSymbols.put(EmailAddress, "E");
+ DefaultSymbols.put(DC, "DC");
+ DefaultSymbols.put(UID, "UID");
+ DefaultSymbols.put(STREET, "STREET");
+ DefaultSymbols.put(SURNAME, "SURNAME");
+ DefaultSymbols.put(GIVENNAME, "GIVENNAME");
+ DefaultSymbols.put(INITIALS, "INITIALS");
+ DefaultSymbols.put(GENERATION, "GENERATION");
+ DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
+ DefaultSymbols.put(UnstructuredName, "unstructuredName");
+ DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
+ DefaultSymbols.put(DN_QUALIFIER, "DN");
+ DefaultSymbols.put(PSEUDONYM, "Pseudonym");
+ DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
+ DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
+ DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
+ DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
+ DefaultSymbols.put(GENDER, "Gender");
+ DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
+ DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
+ DefaultSymbols.put(POSTAL_CODE, "PostalCode");
+ DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
+ DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
+ DefaultSymbols.put(NAME, "Name");
+
+ DefaultLookUp.put("c", C);
+ DefaultLookUp.put("o", O);
+ DefaultLookUp.put("t", T);
+ DefaultLookUp.put("ou", OU);
+ DefaultLookUp.put("cn", CN);
+ DefaultLookUp.put("l", L);
+ DefaultLookUp.put("st", ST);
+ DefaultLookUp.put("sn", SN);
+ DefaultLookUp.put("serialnumber", SN);
+ DefaultLookUp.put("street", STREET);
+ DefaultLookUp.put("emailaddress", E);
+ DefaultLookUp.put("dc", DC);
+ DefaultLookUp.put("e", E);
+ DefaultLookUp.put("uid", UID);
+ DefaultLookUp.put("surname", SURNAME);
+ DefaultLookUp.put("givenname", GIVENNAME);
+ DefaultLookUp.put("initials", INITIALS);
+ DefaultLookUp.put("generation", GENERATION);
+ DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
+ DefaultLookUp.put("unstructuredname", UnstructuredName);
+ DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
+ DefaultLookUp.put("dn", DN_QUALIFIER);
+ DefaultLookUp.put("pseudonym", PSEUDONYM);
+ DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
+ DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
+ DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
+ DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
+ DefaultLookUp.put("gender", GENDER);
+ DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
+ DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
+ DefaultLookUp.put("postalcode", POSTAL_CODE);
+ DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
+ DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
+ DefaultLookUp.put("name", NAME);
+ }
+
+ /**
+ * Singleton instance.
+ */
+ public static final X500NameStyle INSTANCE = new BCStyle();
+
+ protected Hashtable defaultLookUp;
+ protected Hashtable defaultSymbols;
+
+ protected BCStyle()
+ {
+ defaultSymbols = copyHashTable(DefaultSymbols);
+ defaultLookUp = copyHashTable(DefaultLookUp);
+ }
+
+ public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return IETFUtils.valueFromHexString(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+ else
+ {
+ if (value.length() != 0 && value.charAt(0) == '\\')
+ {
+ value = value.substring(1);
+ }
+ if (oid.equals(EmailAddress) || oid.equals(DC))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(DATE_OF_BIRTH)) // accept time string as well as # (for compatibility)
+ {
+ return new ASN1GeneralizedTime(value);
+ }
+ else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER)
+ || oid.equals(TELEPHONE_NUMBER))
+ {
+ return new DERPrintableString(value);
+ }
+ }
+
+ return new DERUTF8String(value);
+ }
+
+ public String oidToDisplayName(ASN1ObjectIdentifier oid)
+ {
+ return (String)DefaultSymbols.get(oid);
+ }
+
+ public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
+ {
+ return IETFUtils.findAttrNamesForOID(oid, defaultLookUp);
+ }
+
+ public ASN1ObjectIdentifier attrNameToOID(String attrName)
+ {
+ return IETFUtils.decodeAttrName(attrName, defaultLookUp);
+ }
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ boolean reverse = false;
+
+ if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+ {
+ reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!foundMatch(reverse, rdns1[i], rdns2))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs)
+ {
+ if (reverse)
+ {
+ for (int i = possRDNs.length - 1; i >= 0; i--)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i != possRDNs.length; i++)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean rdnAreEqual(RDN rdn1, RDN rdn2)
+ {
+ return IETFUtils.rDNAreEqual(rdn1, rdn2);
+ }
+
+ public RDN[] fromString(String dirName)
+ {
+ return IETFUtils.rDNsFromString(dirName, this);
+ }
+
+ public int calculateHashCode(X500Name name)
+ {
+ int hashCodeValue = 0;
+ RDN[] rdns = name.getRDNs();
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != rdns.length; i++)
+ {
+ if (rdns[i].isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ hashCodeValue ^= atv[j].getType().hashCode();
+ hashCodeValue ^= calcHashCode(atv[j].getValue());
+ }
+ }
+ else
+ {
+ hashCodeValue ^= rdns[i].getFirst().getType().hashCode();
+ hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue());
+ }
+ }
+
+ return hashCodeValue;
+ }
+
+ private int calcHashCode(ASN1Encodable enc)
+ {
+ String value = IETFUtils.valueToString(enc);
+
+ value = IETFUtils.canonicalize(value);
+
+ return value.hashCode();
+ }
+
+ public String toString(X500Name name)
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+
+ RDN[] rdns = name.getRDNs();
+
+ for (int i = 0; i < rdns.length; i++)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ IETFUtils.appendRDN(buf, rdns[i], defaultSymbols);
+ }
+
+ return buf.toString();
+ }
+
+ private static Hashtable copyHashTable(Hashtable paramsMap)
+ {
+ Hashtable newTable = new Hashtable();
+
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ newTable.put(key, paramsMap.get(key));
+ }
+
+ return newTable;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/asn1/x500/style/RFC4519Style.java b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/asn1/x500/style/RFC4519Style.java
new file mode 100644
index 000000000..0fd117850
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/asn1/x500/style/RFC4519Style.java
@@ -0,0 +1,380 @@
+package org.spongycastle.asn1.x500.style;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.x500.AttributeTypeAndValue;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+
+public class RFC4519Style
+ implements X500NameStyle
+{
+ public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15");
+ public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6");
+ public static final ASN1ObjectIdentifier cn = new ASN1ObjectIdentifier("2.5.4.3");
+ public static final ASN1ObjectIdentifier dc = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+ public static final ASN1ObjectIdentifier description = new ASN1ObjectIdentifier("2.5.4.13");
+ public static final ASN1ObjectIdentifier destinationIndicator = new ASN1ObjectIdentifier("2.5.4.27");
+ public static final ASN1ObjectIdentifier distinguishedName = new ASN1ObjectIdentifier("2.5.4.49");
+ public static final ASN1ObjectIdentifier dnQualifier = new ASN1ObjectIdentifier("2.5.4.46");
+ public static final ASN1ObjectIdentifier enhancedSearchGuide = new ASN1ObjectIdentifier("2.5.4.47");
+ public static final ASN1ObjectIdentifier facsimileTelephoneNumber = new ASN1ObjectIdentifier("2.5.4.23");
+ public static final ASN1ObjectIdentifier generationQualifier = new ASN1ObjectIdentifier("2.5.4.44");
+ public static final ASN1ObjectIdentifier givenName = new ASN1ObjectIdentifier("2.5.4.42");
+ public static final ASN1ObjectIdentifier houseIdentifier = new ASN1ObjectIdentifier("2.5.4.51");
+ public static final ASN1ObjectIdentifier initials = new ASN1ObjectIdentifier("2.5.4.43");
+ public static final ASN1ObjectIdentifier internationalISDNNumber = new ASN1ObjectIdentifier("2.5.4.25");
+ public static final ASN1ObjectIdentifier l = new ASN1ObjectIdentifier("2.5.4.7");
+ public static final ASN1ObjectIdentifier member = new ASN1ObjectIdentifier("2.5.4.31");
+ public static final ASN1ObjectIdentifier name = new ASN1ObjectIdentifier("2.5.4.41");
+ public static final ASN1ObjectIdentifier o = new ASN1ObjectIdentifier("2.5.4.10");
+ public static final ASN1ObjectIdentifier ou = new ASN1ObjectIdentifier("2.5.4.11");
+ public static final ASN1ObjectIdentifier owner = new ASN1ObjectIdentifier("2.5.4.32");
+ public static final ASN1ObjectIdentifier physicalDeliveryOfficeName = new ASN1ObjectIdentifier("2.5.4.19");
+ public static final ASN1ObjectIdentifier postalAddress = new ASN1ObjectIdentifier("2.5.4.16");
+ public static final ASN1ObjectIdentifier postalCode = new ASN1ObjectIdentifier("2.5.4.17");
+ public static final ASN1ObjectIdentifier postOfficeBox = new ASN1ObjectIdentifier("2.5.4.18");
+ public static final ASN1ObjectIdentifier preferredDeliveryMethod = new ASN1ObjectIdentifier("2.5.4.28");
+ public static final ASN1ObjectIdentifier registeredAddress = new ASN1ObjectIdentifier("2.5.4.26");
+ public static final ASN1ObjectIdentifier roleOccupant = new ASN1ObjectIdentifier("2.5.4.33");
+ public static final ASN1ObjectIdentifier searchGuide = new ASN1ObjectIdentifier("2.5.4.14");
+ public static final ASN1ObjectIdentifier seeAlso = new ASN1ObjectIdentifier("2.5.4.34");
+ public static final ASN1ObjectIdentifier serialNumber = new ASN1ObjectIdentifier("2.5.4.5");
+ public static final ASN1ObjectIdentifier sn = new ASN1ObjectIdentifier("2.5.4.4");
+ public static final ASN1ObjectIdentifier st = new ASN1ObjectIdentifier("2.5.4.8");
+ public static final ASN1ObjectIdentifier street = new ASN1ObjectIdentifier("2.5.4.9");
+ public static final ASN1ObjectIdentifier telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20");
+ public static final ASN1ObjectIdentifier teletexTerminalIdentifier = new ASN1ObjectIdentifier("2.5.4.22");
+ public static final ASN1ObjectIdentifier telexNumber = new ASN1ObjectIdentifier("2.5.4.21");
+ public static final ASN1ObjectIdentifier title = new ASN1ObjectIdentifier("2.5.4.12");
+ public static final ASN1ObjectIdentifier uid = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+ public static final ASN1ObjectIdentifier uniqueMember = new ASN1ObjectIdentifier("2.5.4.50");
+ public static final ASN1ObjectIdentifier userPassword = new ASN1ObjectIdentifier("2.5.4.35");
+ public static final ASN1ObjectIdentifier x121Address = new ASN1ObjectIdentifier("2.5.4.24");
+ public static final ASN1ObjectIdentifier x500UniqueIdentifier = new ASN1ObjectIdentifier("2.5.4.45");
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ private static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ private static final Hashtable DefaultLookUp = new Hashtable();
+
+ static
+ {
+ DefaultSymbols.put(businessCategory, "businessCategory");
+ DefaultSymbols.put(c, "c");
+ DefaultSymbols.put(cn, "cn");
+ DefaultSymbols.put(dc, "dc");
+ DefaultSymbols.put(description, "description");
+ DefaultSymbols.put(destinationIndicator, "destinationIndicator");
+ DefaultSymbols.put(distinguishedName, "distinguishedName");
+ DefaultSymbols.put(dnQualifier, "dnQualifier");
+ DefaultSymbols.put(enhancedSearchGuide, "enhancedSearchGuide");
+ DefaultSymbols.put(facsimileTelephoneNumber, "facsimileTelephoneNumber");
+ DefaultSymbols.put(generationQualifier, "generationQualifier");
+ DefaultSymbols.put(givenName, "givenName");
+ DefaultSymbols.put(houseIdentifier, "houseIdentifier");
+ DefaultSymbols.put(initials, "initials");
+ DefaultSymbols.put(internationalISDNNumber, "internationalISDNNumber");
+ DefaultSymbols.put(l, "l");
+ DefaultSymbols.put(member, "member");
+ DefaultSymbols.put(name, "name");
+ DefaultSymbols.put(o, "o");
+ DefaultSymbols.put(ou, "ou");
+ DefaultSymbols.put(owner, "owner");
+ DefaultSymbols.put(physicalDeliveryOfficeName, "physicalDeliveryOfficeName");
+ DefaultSymbols.put(postalAddress, "postalAddress");
+ DefaultSymbols.put(postalCode, "postalCode");
+ DefaultSymbols.put(postOfficeBox, "postOfficeBox");
+ DefaultSymbols.put(preferredDeliveryMethod, "preferredDeliveryMethod");
+ DefaultSymbols.put(registeredAddress, "registeredAddress");
+ DefaultSymbols.put(roleOccupant, "roleOccupant");
+ DefaultSymbols.put(searchGuide, "searchGuide");
+ DefaultSymbols.put(seeAlso, "seeAlso");
+ DefaultSymbols.put(serialNumber, "serialNumber");
+ DefaultSymbols.put(sn, "sn");
+ DefaultSymbols.put(st, "st");
+ DefaultSymbols.put(street, "street");
+ DefaultSymbols.put(telephoneNumber, "telephoneNumber");
+ DefaultSymbols.put(teletexTerminalIdentifier, "teletexTerminalIdentifier");
+ DefaultSymbols.put(telexNumber, "telexNumber");
+ DefaultSymbols.put(title, "title");
+ DefaultSymbols.put(uid, "uid");
+ DefaultSymbols.put(uniqueMember, "uniqueMember");
+ DefaultSymbols.put(userPassword, "userPassword");
+ DefaultSymbols.put(x121Address, "x121Address");
+ DefaultSymbols.put(x500UniqueIdentifier, "x500UniqueIdentifier");
+
+ DefaultLookUp.put("businesscategory", businessCategory);
+ DefaultLookUp.put("c", c);
+ DefaultLookUp.put("cn", cn);
+ DefaultLookUp.put("dc", dc);
+ DefaultLookUp.put("description", description);
+ DefaultLookUp.put("destinationindicator", destinationIndicator);
+ DefaultLookUp.put("distinguishedname", distinguishedName);
+ DefaultLookUp.put("dnqualifier", dnQualifier);
+ DefaultLookUp.put("enhancedsearchguide", enhancedSearchGuide);
+ DefaultLookUp.put("facsimiletelephonenumber", facsimileTelephoneNumber);
+ DefaultLookUp.put("generationqualifier", generationQualifier);
+ DefaultLookUp.put("givenname", givenName);
+ DefaultLookUp.put("houseidentifier", houseIdentifier);
+ DefaultLookUp.put("initials", initials);
+ DefaultLookUp.put("internationalisdnnumber", internationalISDNNumber);
+ DefaultLookUp.put("l", l);
+ DefaultLookUp.put("member", member);
+ DefaultLookUp.put("name", name);
+ DefaultLookUp.put("o", o);
+ DefaultLookUp.put("ou", ou);
+ DefaultLookUp.put("owner", owner);
+ DefaultLookUp.put("physicaldeliveryofficename", physicalDeliveryOfficeName);
+ DefaultLookUp.put("postaladdress", postalAddress);
+ DefaultLookUp.put("postalcode", postalCode);
+ DefaultLookUp.put("postofficebox", postOfficeBox);
+ DefaultLookUp.put("preferreddeliverymethod", preferredDeliveryMethod);
+ DefaultLookUp.put("registeredaddress", registeredAddress);
+ DefaultLookUp.put("roleoccupant", roleOccupant);
+ DefaultLookUp.put("searchguide", searchGuide);
+ DefaultLookUp.put("seealso", seeAlso);
+ DefaultLookUp.put("serialnumber", serialNumber);
+ DefaultLookUp.put("sn", sn);
+ DefaultLookUp.put("st", st);
+ DefaultLookUp.put("street", street);
+ DefaultLookUp.put("telephonenumber", telephoneNumber);
+ DefaultLookUp.put("teletexterminalidentifier", teletexTerminalIdentifier);
+ DefaultLookUp.put("telexnumber", telexNumber);
+ DefaultLookUp.put("title", title);
+ DefaultLookUp.put("uid", uid);
+ DefaultLookUp.put("uniquemember", uniqueMember);
+ DefaultLookUp.put("userpassword", userPassword);
+ DefaultLookUp.put("x121address", x121Address);
+ DefaultLookUp.put("x500uniqueidentifier", x500UniqueIdentifier);
+
+ // TODO: need to add correct matching for equality comparisons.
+ }
+
+ /**
+ * Singleton instance.
+ */
+ public static final X500NameStyle INSTANCE = new RFC4519Style();
+
+ protected Hashtable defaultLookUp;
+ protected Hashtable defaultSymbols;
+
+ protected RFC4519Style()
+ {
+ defaultSymbols = copyHashTable(DefaultSymbols);
+ defaultLookUp = copyHashTable(DefaultLookUp);
+ }
+
+ public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return IETFUtils.valueFromHexString(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+ else
+ {
+ if (value.length() != 0 && value.charAt(0) == '\\')
+ {
+ value = value.substring(1);
+ }
+ if (oid.equals(dc))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(c) || oid.equals(serialNumber) || oid.equals(dnQualifier)
+ || oid.equals(telephoneNumber))
+ {
+ return new DERPrintableString(value);
+ }
+ }
+
+ return new DERUTF8String(value);
+ }
+
+ public String oidToDisplayName(ASN1ObjectIdentifier oid)
+ {
+ return (String)DefaultSymbols.get(oid);
+ }
+
+ public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
+ {
+ return IETFUtils.findAttrNamesForOID(oid, defaultLookUp);
+ }
+
+ public ASN1ObjectIdentifier attrNameToOID(String attrName)
+ {
+ return IETFUtils.decodeAttrName(attrName, defaultLookUp);
+ }
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ boolean reverse = false;
+
+ if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+ {
+ reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!foundMatch(reverse, rdns1[i], rdns2))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs)
+ {
+ if (reverse)
+ {
+ for (int i = possRDNs.length - 1; i >= 0; i--)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i != possRDNs.length; i++)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean rdnAreEqual(RDN rdn1, RDN rdn2)
+ {
+ return IETFUtils.rDNAreEqual(rdn1, rdn2);
+ }
+
+ // parse backwards
+ public RDN[] fromString(String dirName)
+ {
+ RDN[] tmp = IETFUtils.rDNsFromString(dirName, this);
+ RDN[] res = new RDN[tmp.length];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ res[res.length - i - 1] = tmp[i];
+ }
+
+ return res;
+ }
+
+ public int calculateHashCode(X500Name name)
+ {
+ int hashCodeValue = 0;
+ RDN[] rdns = name.getRDNs();
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != rdns.length; i++)
+ {
+ if (rdns[i].isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ hashCodeValue ^= atv[j].getType().hashCode();
+ hashCodeValue ^= calcHashCode(atv[j].getValue());
+ }
+ }
+ else
+ {
+ hashCodeValue ^= rdns[i].getFirst().getType().hashCode();
+ hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue());
+ }
+ }
+
+ return hashCodeValue;
+ }
+
+ private int calcHashCode(ASN1Encodable enc)
+ {
+ String value = IETFUtils.valueToString(enc);
+
+ value = IETFUtils.canonicalize(value);
+
+ return value.hashCode();
+ }
+
+ // convert in reverse
+ public String toString(X500Name name)
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+
+ RDN[] rdns = name.getRDNs();
+
+ for (int i = rdns.length - 1; i >= 0; i--)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ IETFUtils.appendRDN(buf, rdns[i], defaultSymbols);
+ }
+
+ return buf.toString();
+ }
+
+ private static Hashtable copyHashTable(Hashtable paramsMap)
+ {
+ Hashtable newTable = new Hashtable();
+
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ newTable.put(key, paramsMap.get(key));
+ }
+
+ return newTable;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/agreement/jpake/JPAKEParticipant.java b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/agreement/jpake/JPAKEParticipant.java
new file mode 100644
index 000000000..17554b960
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/agreement/jpake/JPAKEParticipant.java
@@ -0,0 +1,573 @@
+package org.spongycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A participant in a Password Authenticated Key Exchange by Juggling (J-PAKE) exchange.
+ *
+ * The system property is checked during construction of the encoding object, it is set to
+ * true by default.
+ *
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the default SecureRandom does for its generateSeed() call.
+ *
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the passed in SecureRandom does for its generateSeed() call.
+ *
+ * Note: If this constructor is used any calls to setSeed() in the resulting SecureRandom will be ignored.
+ *
+ * Test cases generated using ref version of ChaCha20 in estreambench-20080905.
+ */
+public class ChaChaTest
+ extends SimpleTest
+{
+ byte[] zeroes = Hex.decode(
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000");
+
+ String set1v0_0 = "FBB87FBB8395E05DAA3B1D683C422046"
+ + "F913985C2AD9B23CFC06C1D8D04FF213"
+ + "D44A7A7CDB84929F915420A8A3DC58BF"
+ + "0F7ECB4B1F167BB1A5E6153FDAF4493D";
+
+ String set1v0_192 = "D9485D55B8B82D792ED1EEA8E93E9BC1"
+ + "E2834AD0D9B11F3477F6E106A2F6A5F2"
+ + "EA8244D5B925B8050EAB038F58D4DF57"
+ + "7FAFD1B89359DAE508B2B10CBD6B488E";
+
+ String set1v0_256 = "08661A35D6F02D3D9ACA8087F421F7C8"
+ + "A42579047D6955D937925BA21396DDD4"
+ + "74B1FC4ACCDCAA33025B4BCE817A4FBF"
+ + "3E5D07D151D7E6FE04934ED466BA4779";
+
+ String set1v0_448 = "A7E16DD38BA48CCB130E5BE9740CE359"
+ + "D631E91600F85C8A5D0785A612D1D987"
+ + "90780ACDDC26B69AB106CCF6D866411D"
+ + "10637483DBF08CC5591FD8B3C87A3AE0";
+
+ String set1v9_0 = "A276339F99316A913885A0A4BE870F06"
+ + "91E72B00F1B3F2239F714FE81E88E00C"
+ + "BBE52B4EBBE1EA15894E29658C4CB145"
+ + "E6F89EE4ABB045A78514482CE75AFB7C";
+
+ String set1v9_192 = "0DFB9BD4F87F68DE54FBC1C6428FDEB0"
+ + "63E997BE8490C9B7A4694025D6EBA2B1"
+ + "5FE429DB82A7CAE6AAB22918E8D00449"
+ + "6FB6291467B5AE81D4E85E81D8795EBB";
+
+ String set1v9_256 = "546F5BB315E7F71A46E56D4580F90889"
+ + "639A2BA528F757CF3B048738BA141AF3"
+ + "B31607CB21561BAD94721048930364F4"
+ + "B1227CFEB7CDECBA881FB44903550E68";
+
+ String set1v9_448 = "6F813586E76691305A0CF048C0D8586D"
+ + "C89460207D8B230CD172398AA33D19E9"
+ + "2D24883C3A9B0BB7CD8C6B2668DB142E"
+ + "37A97948A7A01498A21110297984CD20";
+
+ String set6v0_0 = "57459975BC46799394788DE80B928387"
+ + "862985A269B9E8E77801DE9D874B3F51"
+ + "AC4610B9F9BEE8CF8CACD8B5AD0BF17D"
+ + "3DDF23FD7424887EB3F81405BD498CC3";
+
+ String set6v0_65472 = "EF9AEC58ACE7DB427DF012B2B91A0C1E"
+ + "8E4759DCE9CDB00A2BD59207357BA06C"
+ + "E02D327C7719E83D6348A6104B081DB0"
+ + "3908E5186986AE41E3AE95298BB7B713";
+
+ String set6v0_65536 = "17EF5FF454D85ABBBA280F3A94F1D26E"
+ + "950C7D5B05C4BB3A78326E0DC5731F83"
+ + "84205C32DB867D1B476CE121A0D7074B"
+ + "AA7EE90525D15300F48EC0A6624BD0AF";
+
+ String set6v1_0 = "92A2508E2C4084567195F2A1005E552B"
+ + "4874EC0504A9CD5E4DAF739AB553D2E7"
+ + "83D79C5BA11E0653BEBB5C116651302E"
+ + "8D381CB728CA627B0B246E83942A2B99";
+
+ String set6v1_65472 = "E1974EC3063F7BD0CBA58B1CE34BC874"
+ + "67AAF5759B05EA46682A5D4306E5A76B"
+ + "D99A448DB8DE73AF97A73F5FBAE2C776"
+ + "35040464524CF14D7F08D4CE1220FD84";
+
+ String set6v1_65536 = "BE3436141CFD62D12FF7D852F80C1344"
+ + "81F152AD0235ECF8CA172C55CA8C031B"
+ + "2E785D773A988CA8D4BDA6FAE0E493AA"
+ + "71DCCC4C894D1F106CAC62A9FC0A9607";
+
+ // ChaCha12
+ String chacha12_set1v0_0 = "36CF0D56E9F7FBF287BC5460D95FBA94"
+ + "AA6CBF17D74E7C784DDCF7E0E882DDAE"
+ + "3B5A58243EF32B79A04575A8E2C2B73D"
+ + "C64A52AA15B9F88305A8F0CA0B5A1A25";
+
+ String chacha12_set1v0_192 = "83496792AB68FEC75ADB16D3044420A4"
+ + "A00A6E9ADC41C3A63DBBF317A8258C85"
+ + "A9BC08B4F76B413A4837324AEDF8BC2A"
+ + "67D53C9AB9E1C5BC5F379D48DF9AF730";
+
+ String chacha12_set1v0_256 = "BAA28ED593690FD760ADA07C95E3B888"
+ + "4B4B64E488CA7A2D9BDC262243AB9251"
+ + "394C5037E255F8BCCDCD31306C508FFB"
+ + "C9E0161380F7911FCB137D46D9269250";
+
+ String chacha12_set1v0_448 = "B7ECFB6AE0B51915762FE1FD03A14D0C"
+ + "9E54DA5DC76EB16EBA5313BC535DE63D"
+ + "C72D7F9F1874E301E99C8531819F4E37"
+ + "75793F6A5D19C717FA5C78A39EB804A6";
+
+ // ChaCha8
+ String chacha8_set1v0_0 = "BEB1E81E0F747E43EE51922B3E87FB38"
+ + "D0163907B4ED49336032AB78B67C2457"
+ + "9FE28F751BD3703E51D876C017FAA435"
+ + "89E63593E03355A7D57B2366F30047C5";
+
+ String chacha8_set1v0_192 = "33B8B7CA8F8E89F0095ACE75A379C651"
+ + "FD6BDD55703C90672E44C6BAB6AACDD8"
+ + "7C976A87FD264B906E749429284134C2"
+ + "38E3B88CF74A68245B860D119A8BDF43";
+
+ String chacha8_set1v0_256 = "F7CA95BF08688BD3BE8A27724210F9DC"
+ + "16F32AF974FBFB09E9F757C577A245AB"
+ + "F35F824B70A4C02CB4A8D7191FA8A5AD"
+ + "6A84568743844703D353B7F00A8601F4";
+
+ String chacha8_set1v0_448 = "7B4117E8BFFD595CD8482270B08920FB"
+ + "C9B97794E1809E07BB271BF07C861003"
+ + "4C38DBA6ECA04E5474F399A284CBF6E2"
+ + "7F70142E604D0977797DE5B58B6B25E0";
+
+
+
+ public String getName()
+ {
+ return "ChaCha";
+ }
+
+ public void performTest()
+ {
+ chachaTest1(20, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v0_0, set1v0_192, set1v0_256, set1v0_448);
+ chachaTest1(20, new ParametersWithIV(new KeyParameter(Hex.decode("00400000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v9_0, set1v9_192, set1v9_256, set1v9_448);
+ chachaTest1(12, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ chacha12_set1v0_0, chacha12_set1v0_192, chacha12_set1v0_256, chacha12_set1v0_448);
+ chachaTest1(8, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ chacha8_set1v0_0, chacha8_set1v0_192, chacha8_set1v0_256, chacha8_set1v0_448);
+ chachaTest2(new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE")),
+ set6v0_0, set6v0_65472, set6v0_65536);
+ chachaTest2(new ParametersWithIV(new KeyParameter(Hex.decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.decode("167DE44BB21980E7")),
+ set6v1_0, set6v1_65472, set6v1_65536);
+ reinitBug();
+ }
+
+ private void chachaTest1(int rounds, CipherParameters params, String v0, String v192, String v256, String v448)
+ {
+ StreamCipher chaCha = new ChaChaEngine(rounds);
+ byte[] buf = new byte[64];
+
+ chaCha.init(true, params);
+
+ for (int i = 0; i != 7; i++)
+ {
+ chaCha.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0/" + rounds, v0, buf);
+ }
+ break;
+ case 3:
+ if (!areEqual(buf, Hex.decode(v192)))
+ {
+ mismatch("v192/" + rounds, v192, buf);
+ }
+ break;
+ case 4:
+ if (!areEqual(buf, Hex.decode(v256)))
+ {
+ mismatch("v256/" + rounds, v256, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ buf[i] = chaCha.returnByte(zeroes[i]);
+ }
+
+ if (!areEqual(buf, Hex.decode(v448)))
+ {
+ mismatch("v448", v448, buf);
+ }
+ }
+
+ private void chachaTest2(CipherParameters params, String v0, String v65472, String v65536)
+ {
+ StreamCipher chaCha = new ChaChaEngine();
+ byte[] buf = new byte[64];
+
+ chaCha.init(true, params);
+
+ for (int i = 0; i != 1025; i++)
+ {
+ chaCha.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0", v0, buf);
+ }
+ break;
+ case 1023:
+ if (!areEqual(buf, Hex.decode(v65472)))
+ {
+ mismatch("v65472", v65472, buf);
+ }
+ break;
+ case 1024:
+ if (!areEqual(buf, Hex.decode(v65536)))
+ {
+ mismatch("v65536", v65536, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+
+ private void reinitBug()
+ {
+ KeyParameter key = new KeyParameter(Hex.decode("80000000000000000000000000000000"));
+ ParametersWithIV parameters = new ParametersWithIV(key, Hex.decode("0000000000000000"));
+
+ StreamCipher salsa = new ChaChaEngine();
+
+ salsa.init(true, parameters);
+
+ try
+ {
+ salsa.init(true, key);
+ fail("Salsa20 should throw exception if no IV in Init");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ChaChaTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CipherStreamTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CipherStreamTest.java
new file mode 100644
index 000000000..c7527db1c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CipherStreamTest.java
@@ -0,0 +1,542 @@
+package org.spongycastle.crypto.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.BlowfishEngine;
+import org.spongycastle.crypto.engines.CAST5Engine;
+import org.spongycastle.crypto.engines.CAST6Engine;
+import org.spongycastle.crypto.engines.CamelliaEngine;
+import org.spongycastle.crypto.engines.ChaChaEngine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.Grain128Engine;
+import org.spongycastle.crypto.engines.Grainv1Engine;
+import org.spongycastle.crypto.engines.HC128Engine;
+import org.spongycastle.crypto.engines.HC256Engine;
+import org.spongycastle.crypto.engines.NoekeonEngine;
+import org.spongycastle.crypto.engines.RC2Engine;
+import org.spongycastle.crypto.engines.RC4Engine;
+import org.spongycastle.crypto.engines.RC6Engine;
+import org.spongycastle.crypto.engines.SEEDEngine;
+import org.spongycastle.crypto.engines.Salsa20Engine;
+import org.spongycastle.crypto.engines.SerpentEngine;
+import org.spongycastle.crypto.engines.TEAEngine;
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.engines.TwofishEngine;
+import org.spongycastle.crypto.engines.XSalsa20Engine;
+import org.spongycastle.crypto.engines.XTEAEngine;
+import org.spongycastle.crypto.io.CipherInputStream;
+import org.spongycastle.crypto.io.CipherOutputStream;
+import org.spongycastle.crypto.io.InvalidCipherTextIOException;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CCMBlockCipher;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.CTSBlockCipher;
+import org.spongycastle.crypto.modes.EAXBlockCipher;
+import org.spongycastle.crypto.modes.GCMBlockCipher;
+import org.spongycastle.crypto.modes.OCBBlockCipher;
+import org.spongycastle.crypto.modes.OFBBlockCipher;
+import org.spongycastle.crypto.modes.SICBlockCipher;
+import org.spongycastle.crypto.paddings.PKCS7Padding;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.test.SimpleTest;
+
+public class CipherStreamTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "CipherStreamTest";
+ }
+
+ private void testMode(Object cipher, CipherParameters params)
+ throws Exception
+ {
+ testWriteRead(cipher, params, false);
+ testWriteRead(cipher, params, true);
+ testReadWrite(cipher, params, false);
+ testReadWrite(cipher, params, true);
+
+ if (!(cipher instanceof CTSBlockCipher))
+ {
+ testWriteReadEmpty(cipher, params, false);
+ testWriteReadEmpty(cipher, params, true);
+ }
+
+ if (cipher instanceof AEADBlockCipher)
+ {
+ testTamperedRead((AEADBlockCipher)cipher, params);
+ testTruncatedRead((AEADBlockCipher)cipher, params);
+ testTamperedWrite((AEADBlockCipher)cipher, params);
+ }
+ }
+
+ private OutputStream createCipherOutputStream(OutputStream output, Object cipher)
+ {
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ return new CipherOutputStream(output, (BufferedBlockCipher)cipher);
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return new CipherOutputStream(output, (AEADBlockCipher)cipher);
+ }
+ else
+ {
+ return new CipherOutputStream(output, (StreamCipher)cipher);
+ }
+ }
+
+ private InputStream createCipherInputStream(byte[] data, Object cipher)
+ {
+ ByteArrayInputStream input = new ByteArrayInputStream(data);
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ return new CipherInputStream(input, (BufferedBlockCipher)cipher);
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return new CipherInputStream(input, (AEADBlockCipher)cipher);
+ }
+ else
+ {
+ return new CipherInputStream(input, (StreamCipher)cipher);
+ }
+ }
+
+ /**
+ * Test tampering of ciphertext followed by read from decrypting CipherInputStream
+ */
+ private void testTamperedRead(AEADBlockCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(1000)];
+ cipher.doFinal(ciphertext, cipher.processBytes(new byte[1000], 0, 1000, ciphertext, 0));
+
+ // Tamper
+ ciphertext[0] += 1;
+
+ cipher.init(false, params);
+ InputStream input = createCipherInputStream(ciphertext, cipher);
+ try
+ {
+ while (input.read() >= 0)
+ {
+ }
+ fail("Expected invalid ciphertext after tamper and read : " + cipher.getAlgorithmName());
+ }
+ catch (InvalidCipherTextIOException e)
+ {
+ // Expected
+ }
+ try
+ {
+ input.close();
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception after tamper and read : " + cipher.getAlgorithmName());
+ }
+ }
+
+ /**
+ * Test truncation of ciphertext to make tag calculation impossible, followed by read from
+ * decrypting CipherInputStream
+ */
+ private void testTruncatedRead(AEADBlockCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(1000)];
+ cipher.doFinal(ciphertext, cipher.processBytes(new byte[1000], 0, 1000, ciphertext, 0));
+
+ // Truncate to just smaller than complete tag
+ byte[] truncated = new byte[ciphertext.length - 1000 - 1];
+ System.arraycopy(ciphertext, 0, truncated, 0, truncated.length);
+
+ cipher.init(false, params);
+ InputStream input = createCipherInputStream(truncated, cipher);
+ while (true)
+ {
+ int read = 0;
+ try
+ {
+ read = input.read();
+ }
+ catch (InvalidCipherTextIOException e)
+ {
+ // Expected
+ break;
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception on truncated read : " + cipher.getAlgorithmName());
+ break;
+ }
+ if (read < 0)
+ {
+ fail("Expected invalid ciphertext after truncate and read : " + cipher.getAlgorithmName());
+ break;
+ }
+ }
+ try
+ {
+ input.close();
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception after truncate and read : " + cipher.getAlgorithmName());
+ }
+ }
+
+ /**
+ * Test tampering of ciphertext followed by write to decrypting CipherOutputStream
+ */
+ private void testTamperedWrite(AEADBlockCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(1000)];
+ cipher.doFinal(ciphertext, cipher.processBytes(new byte[1000], 0, 1000, ciphertext, 0));
+
+ // Tamper
+ ciphertext[0] += 1;
+
+ cipher.init(false, params);
+ ByteArrayOutputStream plaintext = new ByteArrayOutputStream();
+ OutputStream output = createCipherOutputStream(plaintext, cipher);
+
+ for (int i = 0; i < ciphertext.length; i++)
+ {
+ output.write(ciphertext[i]);
+ }
+ try
+ {
+ output.close();
+ fail("Expected invalid ciphertext after tamper and write : " + cipher.getAlgorithmName());
+ }
+ catch (InvalidCipherTextIOException e)
+ {
+ // Expected
+ }
+ }
+
+ /**
+ * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
+ */
+ private void testWriteRead(Object cipher, CipherParameters params, boolean blocks)
+ throws Exception
+ {
+ byte[] data = new byte[1000];
+ for (int i = 0; i < data.length; i++)
+ {
+ data[i] = (byte)(i % 255);
+ }
+
+ testWriteRead(cipher, params, blocks, data);
+ }
+
+ /**
+ * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
+ */
+ private void testWriteReadEmpty(Object cipher, CipherParameters params, boolean blocks)
+ throws Exception
+ {
+ byte[] data = new byte[0];
+
+ testWriteRead(cipher, params, blocks, data);
+ }
+
+ private void testWriteRead(Object cipher, CipherParameters params, boolean blocks, byte[] data)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ init(cipher, true, params);
+
+ OutputStream cOut = createCipherOutputStream(bOut, cipher);
+ if (blocks)
+ {
+ int chunkSize = data.length / 8;
+ for (int i = 0; i < data.length; i += chunkSize)
+ {
+ cOut.write(data, i, chunkSize);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ cOut.write(data[i]);
+ }
+ }
+ cOut.close();
+
+ byte[] cipherText = bOut.toByteArray();
+ bOut.reset();
+ init(cipher, false, params);
+ InputStream cIn = createCipherInputStream(cipherText, cipher);
+
+ if (blocks)
+ {
+ byte[] block = new byte[getBlockSize(cipher) + 1];
+ int c;
+ while ((c = cIn.read(block)) >= 0)
+ {
+ bOut.write(block, 0, c);
+ }
+ }
+ else
+ {
+ int c;
+ while ((c = cIn.read()) >= 0)
+ {
+ bOut.write(c);
+ }
+
+ }
+ cIn.close();
+
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception " + getName(cipher), e);
+ }
+
+ byte[] decrypted = bOut.toByteArray();
+ if (!Arrays.areEqual(data, decrypted))
+ {
+ fail("Failed - decrypted data doesn't match: " + getName(cipher));
+ }
+ }
+
+ private String getName(Object cipher)
+ {
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ return ((BufferedBlockCipher)cipher).getUnderlyingCipher().getAlgorithmName();
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return ((AEADBlockCipher)cipher).getUnderlyingCipher().getAlgorithmName();
+ }
+ else if (cipher instanceof StreamCipher)
+ {
+ return ((StreamCipher)cipher).getAlgorithmName();
+ }
+ return null;
+ }
+
+ private int getBlockSize(Object cipher)
+ {
+ if (cipher instanceof BlockCipher)
+ {
+ return ((BlockCipher)cipher).getBlockSize();
+ }
+ else if (cipher instanceof BufferedBlockCipher)
+ {
+ return ((BufferedBlockCipher)cipher).getBlockSize();
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return ((AEADBlockCipher)cipher).getUnderlyingCipher().getBlockSize();
+ }
+ else if (cipher instanceof StreamCipher)
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ private void init(Object cipher, boolean forEncrypt, CipherParameters params)
+ {
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ ((BufferedBlockCipher)cipher).init(forEncrypt, params);
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ ((AEADBlockCipher)cipher).init(forEncrypt, params);
+ }
+ else if (cipher instanceof StreamCipher)
+ {
+ ((StreamCipher)cipher).init(forEncrypt, params);
+ }
+ }
+
+ protected void fail(String message, boolean authenticated, boolean bc)
+ {
+ if (bc || !authenticated)
+ {
+ super.fail(message);
+ }
+ else
+ {
+ // javax.crypto.CipherInputStream/CipherOutputStream
+ // are broken wrt handling AEAD failures
+ System.err.println("Broken JCE Streams: " + message);
+ }
+ }
+
+ /**
+ * Test CipherInputStream in ENCRYPT_MODE, CipherOutputStream in DECRYPT_MODE
+ */
+ private void testReadWrite(Object cipher, CipherParameters params, boolean blocks)
+ throws Exception
+ {
+ String lCode = "ABCDEFGHIJKLMNOPQRSTU";
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ init(cipher, true, params);
+
+ InputStream cIn = createCipherInputStream(lCode.getBytes(), cipher);
+ ByteArrayOutputStream ct = new ByteArrayOutputStream();
+
+ if (blocks)
+ {
+ byte[] block = new byte[getBlockSize(cipher) + 1];
+ int c;
+ while ((c = cIn.read(block)) >= 0)
+ {
+ ct.write(block, 0, c);
+ }
+ }
+ else
+ {
+ int c;
+ while ((c = cIn.read()) >= 0)
+ {
+ ct.write(c);
+ }
+ }
+ cIn.close();
+
+ init(cipher, false, params);
+ ByteArrayInputStream dataIn = new ByteArrayInputStream(ct.toByteArray());
+ OutputStream cOut = createCipherOutputStream(bOut, cipher);
+
+ if (blocks)
+ {
+ byte[] block = new byte[getBlockSize(cipher) + 1];
+ int c;
+ while ((c = dataIn.read(block)) >= 0)
+ {
+ cOut.write(block, 0, c);
+ }
+ }
+ else
+ {
+ int c;
+ while ((c = dataIn.read()) >= 0)
+ {
+ cOut.write(c);
+ }
+ }
+ cOut.flush();
+ cOut.close();
+
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception " + getName(cipher), e);
+ }
+
+ String res = new String(bOut.toByteArray());
+ if (!res.equals(lCode))
+ {
+ fail("Failed read/write - decrypted data doesn't match: " + getName(cipher), lCode, res);
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testModes(new BlowfishEngine(), new BlowfishEngine(), 16);
+ testModes(new DESEngine(), new DESEngine(), 8);
+ testModes(new DESedeEngine(), new DESedeEngine(), 24);
+ testModes(new TEAEngine(), new TEAEngine(), 16);
+ testModes(new CAST5Engine(), new CAST5Engine(), 16);
+ testModes(new RC2Engine(), new RC2Engine(), 16);
+ testModes(new XTEAEngine(), new XTEAEngine(), 16);
+
+ testModes(new AESEngine(), new AESEngine(), 16);
+ testModes(new NoekeonEngine(), new NoekeonEngine(), 16);
+ testModes(new TwofishEngine(), new TwofishEngine(), 16);
+ testModes(new CAST6Engine(), new CAST6Engine(), 16);
+ testModes(new SEEDEngine(), new SEEDEngine(), 16);
+ testModes(new SerpentEngine(), new SerpentEngine(), 16);
+ testModes(new RC6Engine(), new RC6Engine(), 16);
+ testModes(new CamelliaEngine(), new CamelliaEngine(), 16);
+ testModes(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), 64);
+
+ testMode(new RC4Engine(), new KeyParameter(new byte[16]));
+ testMode(new Salsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testMode(new XSalsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[32]), new byte[24]));
+ testMode(new ChaChaEngine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testMode(new Grainv1Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testMode(new Grain128Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[12]));
+ testMode(new HC128Engine(), new KeyParameter(new byte[16]));
+ testMode(new HC256Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+
+ }
+
+ private void testModes(BlockCipher cipher1, BlockCipher cipher2, int keySize)
+ throws Exception
+ {
+ final KeyParameter key = new KeyParameter(new byte[keySize]);
+ final int blockSize = getBlockSize(cipher1);
+ final CipherParameters withIv = new ParametersWithIV(key, new byte[blockSize]);
+
+ if (blockSize > 1)
+ {
+ testMode(new PaddedBufferedBlockCipher(cipher1, new PKCS7Padding()), key);
+
+ testMode(new PaddedBufferedBlockCipher(new CBCBlockCipher(cipher1), new PKCS7Padding()), withIv);
+
+ testMode(new BufferedBlockCipher(new OFBBlockCipher(cipher1, blockSize)), withIv);
+ testMode(new BufferedBlockCipher(new CFBBlockCipher(cipher1, blockSize)), withIv);
+ testMode(new BufferedBlockCipher(new SICBlockCipher(cipher1)), withIv);
+ }
+ if (blockSize <= 16)
+ {
+ testMode(new CTSBlockCipher(cipher1), key);
+ }
+ if (blockSize == 8 || blockSize == 16)
+ {
+ testMode(new EAXBlockCipher(cipher1), withIv);
+ }
+ if (blockSize == 16)
+ {
+ testMode(new CCMBlockCipher(cipher1), new ParametersWithIV(key, new byte[7]));
+ testMode(new GCMBlockCipher(cipher1), withIv);
+ testMode(new OCBBlockCipher(cipher1, cipher2), new ParametersWithIV(key, new byte[15]));
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new CipherStreamTest());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CipherTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CipherTest.java
new file mode 100644
index 000000000..6bd4c21f4
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CipherTest.java
@@ -0,0 +1,117 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.test.SimpleTest;
+
+public abstract class CipherTest
+ extends SimpleTest
+{
+ private SimpleTest[] _tests;
+ private BlockCipher _engine;
+ private KeyParameter _validKey;
+
+// protected CipherTest(
+// SimpleTest[] tests)
+// {
+// _tests = tests;
+// }
+
+ protected CipherTest(
+ SimpleTest[] tests,
+ BlockCipher engine,
+ KeyParameter validKey)
+ {
+ _tests = tests;
+ _engine = engine;
+ _validKey = validKey;
+ }
+
+ public abstract String getName();
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i != _tests.length; i++)
+ {
+ _tests[i].performTest();
+ }
+
+ if (_engine != null)
+ {
+ //
+ // state tests
+ //
+ byte[] buf = new byte[128];
+
+ try
+ {
+ _engine.processBlock(buf, 0, buf, 0);
+
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ bufferSizeCheck((_engine));
+ }
+ }
+
+ private void bufferSizeCheck(
+ BlockCipher engine)
+ {
+ byte[] correctBuf = new byte[engine.getBlockSize()];
+ byte[] shortBuf = new byte[correctBuf.length / 2];
+
+ engine.init(true, _validKey);
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, correctBuf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(correctBuf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ engine.init(false, _validKey);
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, correctBuf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(correctBuf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DESTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DESTest.java
new file mode 100644
index 000000000..aec63ef2e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DESTest.java
@@ -0,0 +1,206 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.generators.DESKeyGenerator;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.OFBBlockCipher;
+import org.spongycastle.crypto.params.DESParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.security.SecureRandom;
+
+class DESParityTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "DESParityTest";
+ }
+
+ public void performTest()
+ {
+ byte[] k1In = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
+ (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
+ byte[] k1Out = { (byte)0xfe, (byte)0xfe, (byte)0xfe, (byte)0xfe,
+ (byte)0xfe, (byte)0xfe, (byte)0xfe, (byte)0xfe };
+
+ byte[] k2In = { (byte)0xef, (byte)0xcb, (byte)0xda, (byte)0x4f,
+ (byte)0xaa, (byte)0x99, (byte)0x7f, (byte)0x63 };
+ byte[] k2Out = { (byte)0xef, (byte)0xcb, (byte)0xda, (byte)0x4f,
+ (byte)0xab, (byte)0x98, (byte)0x7f, (byte)0x62 };
+
+ DESParameters.setOddParity(k1In);
+
+ for (int i = 0; i != k1In.length; i++)
+ {
+ if (k1In[i] != k1Out[i])
+ {
+ fail("Failed "
+ + "got " + new String(Hex.encode(k1In))
+ + " expected " + new String(Hex.encode(k1Out)));
+ }
+ }
+
+ DESParameters.setOddParity(k2In);
+
+ for (int i = 0; i != k2In.length; i++)
+ {
+ if (k2In[i] != k2Out[i])
+ {
+ fail("Failed "
+ + "got " + new String(Hex.encode(k2In))
+ + " expected " + new String(Hex.encode(k2Out)));
+ }
+ }
+ }
+}
+
+class KeyGenTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "KeyGenTest";
+ }
+
+ public void performTest()
+ {
+ DESKeyGenerator keyGen = new DESKeyGenerator();
+
+ keyGen.init(new KeyGenerationParameters(new SecureRandom(), 56));
+
+ byte[] kB = keyGen.generateKey();
+
+ if (kB.length != 8)
+ {
+ fail("DES bit key wrong length.");
+ }
+ }
+}
+
+class DESParametersTest
+ extends SimpleTest
+{
+ static private byte[] weakKeys =
+ {
+ (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
+ (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
+ (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
+ (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe,
+ /* semi-weak keys */
+ (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe,
+ (byte)0x1f,(byte)0xe0,(byte)0x1f,(byte)0xe0, (byte)0x0e,(byte)0xf1,(byte)0x0e,(byte)0xf1,
+ (byte)0x01,(byte)0xe0,(byte)0x01,(byte)0xe0, (byte)0x01,(byte)0xf1,(byte)0x01,(byte)0xf1,
+ (byte)0x1f,(byte)0xfe,(byte)0x1f,(byte)0xfe, (byte)0x0e,(byte)0xfe,(byte)0x0e,(byte)0xfe,
+ (byte)0x01,(byte)0x1f,(byte)0x01,(byte)0x1f, (byte)0x01,(byte)0x0e,(byte)0x01,(byte)0x0e,
+ (byte)0xe0,(byte)0xfe,(byte)0xe0,(byte)0xfe, (byte)0xf1,(byte)0xfe,(byte)0xf1,(byte)0xfe,
+ (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01,
+ (byte)0xe0,(byte)0x1f,(byte)0xe0,(byte)0x1f, (byte)0xf1,(byte)0x0e,(byte)0xf1,(byte)0x0e,
+ (byte)0xe0,(byte)0x01,(byte)0xe0,(byte)0x01, (byte)0xf1,(byte)0x01,(byte)0xf1,(byte)0x01,
+ (byte)0xfe,(byte)0x1f,(byte)0xfe,(byte)0x1f, (byte)0xfe,(byte)0x0e,(byte)0xfe,(byte)0x0e,
+ (byte)0x1f,(byte)0x01,(byte)0x1f,(byte)0x01, (byte)0x0e,(byte)0x01,(byte)0x0e,(byte)0x01,
+ (byte)0xfe,(byte)0xe0,(byte)0xfe,(byte)0xe0, (byte)0xfe,(byte)0xf1,(byte)0xfe,(byte)0xf1
+ };
+
+ public String getName()
+ {
+ return "DESParameters";
+ }
+
+ public void performTest() throws Exception
+ {
+ try
+ {
+ DESParameters.isWeakKey(new byte[4], 0);
+ fail("no exception on small key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("key material too short."))
+ {
+ fail("wrong exception");
+ }
+ }
+
+ try
+ {
+ new DESParameters(weakKeys);
+ fail("no exception on weak key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("attempt to create weak DES key"))
+ {
+ fail("wrong exception");
+ }
+ }
+
+ for (int i = 0; i != weakKeys.length; i += 8)
+ {
+ if (!DESParameters.isWeakKey(weakKeys, i))
+ {
+ fail("weakKey test failed");
+ }
+ }
+ }
+}
+
+/**
+ * DES tester - vectors from FIPS 81
+ */
+public class DESTest
+ extends CipherTest
+{
+ static String input1 = "4e6f77206973207468652074696d6520666f7220616c6c20";
+ static String input2 = "4e6f7720697320746865";
+ static String input3 = "4e6f7720697320746865aabbcc";
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new DESEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdef")),
+ input1, "3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53"),
+ new BlockCipherVectorTest(1, new CBCBlockCipher(new DESEngine()),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input1, "e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6"),
+ new BlockCipherVectorTest(2, new CFBBlockCipher(new DESEngine(), 8),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input2, "f31fda07011462ee187f"),
+ new BlockCipherVectorTest(3, new CFBBlockCipher(new DESEngine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input1, "f3096249c7f46e51a69e839b1a92f78403467133898ea622"),
+ new BlockCipherVectorTest(4, new OFBBlockCipher(new DESEngine(), 8),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input2, "f34a2850c9c64985d684"),
+ new BlockCipherVectorTest(5, new CFBBlockCipher(new DESEngine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input3, "f3096249c7f46e51a69e0954bf"),
+ new BlockCipherVectorTest(6, new OFBBlockCipher(new DESEngine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input3, "f3096249c7f46e5135f2c0eb8b"),
+ new DESParityTest(),
+ new DESParametersTest(),
+ new KeyGenTest()
+ };
+
+ public DESTest()
+ {
+ super(tests, new DESEngine(), new KeyParameter(new byte[8]));
+ }
+
+ public String getName()
+ {
+ return "DES";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DESTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DESedeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DESedeTest.java
new file mode 100644
index 000000000..6fc4d0d0d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DESedeTest.java
@@ -0,0 +1,177 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.DESedeWrapEngine;
+import org.spongycastle.crypto.generators.DESedeKeyGenerator;
+import org.spongycastle.crypto.params.DESedeParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.security.SecureRandom;
+
+/**
+ * DESede tester
+ */
+public class DESedeTest
+ extends CipherTest
+{
+ static private byte[] weakKey = // first 8 bytes non-weak
+ {
+ (byte)0x06,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
+ (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
+ (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
+ };
+
+ static String input1 = "4e6f77206973207468652074696d6520666f7220616c6c20";
+ static String input2 = "4e6f7720697320746865";
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdef0123456789abcdef")),
+ input1, "3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53"),
+ new BlockCipherVectorTest(1, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdeffedcba9876543210")),
+ input1, "d80a0d8b2bae5e4e6a0094171abcfc2775d2235a706e232c"),
+ new BlockCipherVectorTest(2, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdef0123456789abcdef0123456789abcdef")),
+ input1, "3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53"),
+ new BlockCipherVectorTest(3, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdeffedcba98765432100123456789abcdef")),
+ input1, "d80a0d8b2bae5e4e6a0094171abcfc2775d2235a706e232c")
+ };
+
+ DESedeTest()
+ {
+ super(tests, new DESedeEngine(), new KeyParameter(new byte[16]));
+ }
+
+ private void wrapTest(
+ int id,
+ byte[] kek,
+ byte[] iv,
+ byte[] in,
+ byte[] out)
+ {
+ Wrapper wrapper = new DESedeWrapEngine();
+
+ wrapper.init(true, new ParametersWithIV(new KeyParameter(kek), iv));
+
+ try
+ {
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!areEqual(cText, out))
+ {
+ fail(": failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ fail("failed wrap test exception: " + e.toString(), e);
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ try
+ {
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!areEqual(pText, in))
+ {
+ fail("failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ fail("failed unwrap test exception: " + e.toString(), e);
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] kek1 = Hex.decode("255e0d1c07b646dfb3134cc843ba8aa71f025b7c0838251f");
+ byte[] iv1 = Hex.decode("5dd4cbfc96f5453b");
+ byte[] in1 = Hex.decode("2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98");
+ byte[] out1 = Hex.decode("690107618ef092b3b48ca1796b234ae9fa33ebb4159604037db5d6a84eb3aac2768c632775a467d4");
+
+ wrapTest(1, kek1, iv1, in1, out1);
+
+ //
+ // key generation
+ //
+ SecureRandom random = new SecureRandom();
+ DESedeKeyGenerator keyGen = new DESedeKeyGenerator();
+
+ keyGen.init(new KeyGenerationParameters(random, 112));
+
+ byte[] kB = keyGen.generateKey();
+
+ if (kB.length != 16)
+ {
+ fail("112 bit key wrong length.");
+ }
+
+ keyGen.init(new KeyGenerationParameters(random, 168));
+
+ kB = keyGen.generateKey();
+
+ if (kB.length != 24)
+ {
+ fail("168 bit key wrong length.");
+ }
+
+ try
+ {
+ keyGen.init(new KeyGenerationParameters(random, 200));
+
+ fail("invalid key length not detected.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ DESedeParameters.isWeakKey(new byte[4], 0);
+ fail("no exception on small key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("key material too short."))
+ {
+ fail("wrong exception");
+ }
+ }
+
+ try
+ {
+ new DESedeParameters(weakKey);
+ fail("no exception on weak key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("attempt to create weak DESede key"))
+ {
+ fail("wrong exception");
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "DESede";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DESedeTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DHKEKGeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DHKEKGeneratorTest.java
new file mode 100644
index 000000000..60ad4bbbe
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DHKEKGeneratorTest.java
@@ -0,0 +1,70 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.agreement.kdf.DHKDFParameters;
+import org.spongycastle.crypto.agreement.kdf.DHKEKGenerator;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * DHKEK Generator tests - from RFC 2631.
+ */
+public class DHKEKGeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("000102030405060708090a0b0c0d0e0f10111213");
+ private DERObjectIdentifier alg1 = PKCSObjectIdentifiers.id_alg_CMS3DESwrap;
+ private byte[] result1 = Hex.decode("a09661392376f7044d9052a397883246b67f5f1ef63eb5fb");
+
+ private byte[] seed2 = Hex.decode("000102030405060708090a0b0c0d0e0f10111213");
+ private DERObjectIdentifier alg2 = PKCSObjectIdentifiers.id_alg_CMSRC2wrap;
+ private byte[] partyAInfo = Hex.decode(
+ "0123456789abcdeffedcba9876543201"
+ + "0123456789abcdeffedcba9876543201"
+ + "0123456789abcdeffedcba9876543201"
+ + "0123456789abcdeffedcba9876543201");
+ private byte[] result2 = Hex.decode("48950c46e0530075403cce72889604e0");
+
+ public DHKEKGeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new DHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg1, 192, seed1), result1);
+ checkMask(2, new DHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg2, 128, seed2, partyAInfo), result2);
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ DerivationParameters params,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(params);
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("DHKEKGenerator failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "DHKEKGenerator";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DHKEKGeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DHTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DHTest.java
new file mode 100644
index 000000000..ce78961fa
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DHTest.java
@@ -0,0 +1,414 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.agreement.DHAgreement;
+import org.spongycastle.crypto.agreement.DHBasicAgreement;
+import org.spongycastle.crypto.generators.DHBasicKeyPairGenerator;
+import org.spongycastle.crypto.generators.DHKeyPairGenerator;
+import org.spongycastle.crypto.generators.DHParametersGenerator;
+import org.spongycastle.crypto.params.DHKeyGenerationParameters;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.crypto.params.DHPrivateKeyParameters;
+import org.spongycastle.crypto.params.DHPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.test.SimpleTest;
+
+public class DHTest
+ extends SimpleTest
+{
+ private BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ private BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ private BigInteger g768 = new BigInteger("7c240073c1316c621df461b71ebb0cdcc90a6e5527e5e126633d131f87461c4dc4afc60c2cb0f053b6758871489a69613e2a8b4c8acde23954c08c81cbd36132cfd64d69e4ed9f8e51ed6e516297206672d5c0a69135df0a5dcf010d289a9ca1", 16);
+ private BigInteger p768 = new BigInteger("8c9dd223debed1b80103b8b309715be009d48860ed5ae9b9d5d8159508efd802e3ad4501a7f7e1cfec78844489148cd72da24b21eddd01aa624291c48393e277cfc529e37075eccef957f3616f962d15b44aeab4039d01b817fde9eaa12fd73f", 16);
+
+ private BigInteger g1024 = new BigInteger("1db17639cdf96bc4eabba19454f0b7e5bd4e14862889a725c96eb61048dcd676ceb303d586e30f060dbafd8a571a39c4d823982117da5cc4e0f89c77388b7a08896362429b94a18a327604eb7ff227bffbc83459ade299e57b5f77b50fb045250934938efa145511166e3197373e1b5b1e52de713eb49792bedde722c6717abf", 16);
+ private BigInteger p1024 = new BigInteger("a00e283b3c624e5b2b4d9fbc2653b5185d99499b00fd1bf244c6f0bb817b4d1c451b2958d62a0f8a38caef059fb5ecd25d75ed9af403f5b5bdab97a642902f824e3c13789fed95fa106ddfe0ff4a707c85e2eb77d49e68f2808bcea18ce128b178cd287c6bc00efa9a1ad2a673fe0dceace53166f75b81d6709d5f8af7c66bb7", 16);
+
+ public String getName()
+ {
+ return "DH";
+ }
+
+ private void testDH(
+ int size,
+ BigInteger g,
+ BigInteger p)
+ {
+ DHKeyPairGenerator kpGen = getDHKeyPairGenerator(g, p);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHAgreement e1 = new DHAgreement();
+ DHAgreement e2 = new DHAgreement();
+
+ e1.init(pv1);
+ e2.init(pv2);
+
+ BigInteger m1 = e1.calculateMessage();
+ BigInteger m2 = e2.calculateMessage();
+
+ BigInteger k1 = e1.calculateAgreement(pu2, m2);
+ BigInteger k2 = e2.calculateAgreement(pu1, m1);
+
+ if (!k1.equals(k2))
+ {
+ fail(size + " bit 2-way test failed");
+ }
+ }
+
+ private void testDHBasic(
+ int size,
+ int privateValueSize,
+ BigInteger g,
+ BigInteger p)
+ {
+ DHBasicKeyPairGenerator kpGen = getDHBasicKeyPairGenerator(g, p, privateValueSize);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ checkKeySize(privateValueSize, pv1);
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ checkKeySize(privateValueSize, pv2);
+ //
+ // two way
+ //
+ DHBasicAgreement e1 = new DHBasicAgreement();
+ DHBasicAgreement e2 = new DHBasicAgreement();
+
+ e1.init(pv1);
+ e2.init(pv2);
+
+ BigInteger k1 = e1.calculateAgreement(pu2);
+ BigInteger k2 = e2.calculateAgreement(pu1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic " + size + " bit 2-way test failed");
+ }
+ }
+
+ private void checkKeySize(
+ int privateValueSize,
+ DHPrivateKeyParameters priv)
+ {
+ if (privateValueSize != 0)
+ {
+ if (priv.getX().bitLength() != privateValueSize)
+ {
+ fail("limited key check failed for key size " + privateValueSize);
+ }
+ }
+ }
+
+ private void testGPWithRandom(
+ DHKeyPairGenerator kpGen)
+ {
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHAgreement e1 = new DHAgreement();
+ DHAgreement e2 = new DHAgreement();
+
+ e1.init(new ParametersWithRandom(pv1, new SecureRandom()));
+ e2.init(new ParametersWithRandom(pv2, new SecureRandom()));
+
+ BigInteger m1 = e1.calculateMessage();
+ BigInteger m2 = e2.calculateMessage();
+
+ BigInteger k1 = e1.calculateAgreement(pu2, m2);
+ BigInteger k2 = e2.calculateAgreement(pu1, m1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic with random 2-way test failed");
+ }
+ }
+
+ private void testSimpleWithRandom(
+ DHBasicKeyPairGenerator kpGen)
+ {
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHBasicAgreement e1 = new DHBasicAgreement();
+ DHBasicAgreement e2 = new DHBasicAgreement();
+
+ e1.init(new ParametersWithRandom(pv1, new SecureRandom()));
+ e2.init(new ParametersWithRandom(pv2, new SecureRandom()));
+
+ BigInteger k1 = e1.calculateAgreement(pu2);
+ BigInteger k2 = e2.calculateAgreement(pu1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic with random 2-way test failed");
+ }
+ }
+
+ private DHBasicKeyPairGenerator getDHBasicKeyPairGenerator(
+ BigInteger g,
+ BigInteger p,
+ int privateValueSize)
+ {
+ DHParameters dhParams = new DHParameters(p, g, null, privateValueSize);
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
+ DHBasicKeyPairGenerator kpGen = new DHBasicKeyPairGenerator();
+
+ kpGen.init(params);
+
+ return kpGen;
+ }
+
+ private DHKeyPairGenerator getDHKeyPairGenerator(
+ BigInteger g,
+ BigInteger p)
+ {
+ DHParameters dhParams = new DHParameters(p, g);
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
+ DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
+
+ kpGen.init(params);
+
+ return kpGen;
+ }
+
+ /**
+ * this test is can take quiet a while
+ */
+ private void testGeneration(
+ int size)
+ {
+ DHParametersGenerator pGen = new DHParametersGenerator();
+
+ pGen.init(size, 10, new SecureRandom());
+
+ DHParameters dhParams = pGen.generateParameters();
+
+ if (dhParams.getL() != 0)
+ {
+ fail("DHParametersGenerator failed to set J to 0 in generated DHParameters");
+ }
+
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
+
+ DHBasicKeyPairGenerator kpGen = new DHBasicKeyPairGenerator();
+
+ kpGen.init(params);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // generate second pair
+ //
+ params = new DHKeyGenerationParameters(new SecureRandom(), pu1.getParameters());
+
+ kpGen.init(params);
+
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHBasicAgreement e1 = new DHBasicAgreement();
+ DHBasicAgreement e2 = new DHBasicAgreement();
+
+ e1.init(new ParametersWithRandom(pv1, new SecureRandom()));
+ e2.init(new ParametersWithRandom(pv2, new SecureRandom()));
+
+ BigInteger k1 = e1.calculateAgreement(pu2);
+ BigInteger k2 = e2.calculateAgreement(pu1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic with " + size + " bit 2-way test failed");
+ }
+ }
+ private void testBounds()
+ {
+ BigInteger p1 = new BigInteger("00C8028E9151C6B51BCDB35C1F6B2527986A72D8546AE7A4BF41DC4289FF9837EE01592D36C324A0F066149B8B940C86C87D194206A39038AE3396F8E12435BB74449B70222D117B8A2BB77CB0D67A5D664DDE7B75E0FEC13CE0CAF258DAF3ADA0773F6FF0F2051D1859929AAA53B07809E496B582A89C3D7DA8B6E38305626621", 16);
+ BigInteger g1 = new BigInteger("1F869713181464577FE4026B47102FA0D7675503A4FCDA810881FAEC3524E6DBAEA9B96561EF7F8BEA76466DF11C2F3EB1A90CC5851735BF860606481257EECE6418C0204E61004E85D7131CE54BCBC7AD67E53C79DCB715E7C8D083DCD85D728283EC8F96839B4C9FA7C0727C472BEB94E4613CAFA8D580119C0AF4BF8AF252", 16);
+ int l1 = 1023;
+
+ BigInteger p2 = new BigInteger("00B333C98720220CC3946F494E25231B3E19F9AD5F6B19F4E7ABF80D8826C491C3224D4F7415A14A7C11D1BE584405FED12C3554F103E56A72D986CA5E325BB9DE07AC37D1EAE5E5AC724D32EF638F0E4462D4C1FC7A45B9FD3A5DF5EC36A1FA4DAA3FBB66AA42B1B71DF416AB547E987513426C7BB8634F5F4D37705514FDC1E1", 16);
+ BigInteger g2 = new BigInteger("2592F5A99FE46313650CCE66C94C15DBED9F4A45BD05C329986CF5D3E12139F0405A47C6385FEA27BFFEDC4CBABC5BB151F3BEE7CC3D51567F1E2B12A975AA9F48A70BDAAE7F5B87E70ADCF902490A3CBEFEDA41EBA8E12E02B56120B5FDEFBED07F5EAD3AE020DF3C8233216F8F0D35E13A7AE4DA5CBCC0D91EADBF20C281C6", 16);
+ int l2 = 1024;
+
+ DHKeyGenerationParameters params1 = new DHKeyGenerationParameters(new SecureRandom(), new DHParameters(p1, g1, null, l1));
+ DHKeyGenerationParameters params2 = new DHKeyGenerationParameters(new SecureRandom(), new DHParameters(p2, g2, null, l2));
+
+ DHBasicKeyPairGenerator kpGen = new DHBasicKeyPairGenerator();
+
+ kpGen.init(params1);
+ kpGen.init(params2);
+ }
+
+ public void performTest()
+ {
+ testDHBasic(512, 0, g512, p512);
+ testDHBasic(768, 0, g768, p768);
+ testDHBasic(1024, 0, g1024, p1024);
+
+ testDHBasic(512, 64, g512, p512);
+ testDHBasic(768, 128, g768, p768);
+ testDHBasic(1024, 256, g1024, p1024);
+
+ testDH(512, g512, p512);
+ testDH(768, g768, p768);
+ testDH(1024, g1024, p1024);
+
+ testBounds();
+
+ //
+ // generation test.
+ //
+ testGeneration(256);
+
+ //
+ // with random test
+ //
+ DHBasicKeyPairGenerator kpBasicGen = getDHBasicKeyPairGenerator(g512, p512, 0);
+
+ testSimpleWithRandom(kpBasicGen);
+
+ DHKeyPairGenerator kpGen = getDHKeyPairGenerator(g512, p512);
+
+ testGPWithRandom(kpGen);
+
+ //
+ // parameter tests
+ //
+ DHAgreement dh = new DHAgreement();
+ AsymmetricCipherKeyPair dhPair = kpGen.generateKeyPair();
+
+ try
+ {
+ dh.init(dhPair.getPublic());
+ fail("DHAgreement key check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // ignore
+ }
+
+ DHKeyPairGenerator kpGen768 = getDHKeyPairGenerator(g768, p768);
+
+ try
+ {
+ dh.init(dhPair.getPrivate());
+
+ dh.calculateAgreement((DHPublicKeyParameters)kpGen768.generateKeyPair().getPublic(), BigInteger.valueOf(100));
+
+ fail("DHAgreement agreement check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // ignore
+ }
+
+ DHBasicAgreement dhBasic = new DHBasicAgreement();
+ AsymmetricCipherKeyPair dhBasicPair = kpBasicGen.generateKeyPair();
+
+ try
+ {
+ dhBasic.init(dhBasicPair.getPublic());
+ fail("DHBasicAgreement key check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ DHBasicKeyPairGenerator kpBasicGen768 = getDHBasicKeyPairGenerator(g768, p768, 0);
+
+ try
+ {
+ dhBasic.init(dhPair.getPrivate());
+
+ dhBasic.calculateAgreement((DHPublicKeyParameters)kpBasicGen768.generateKeyPair().getPublic());
+
+ fail("DHBasicAgreement agreement check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DHTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DSATest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DSATest.java
new file mode 100644
index 000000000..3d91dee90
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DSATest.java
@@ -0,0 +1,602 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.digests.SHA224Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.generators.DSAKeyPairGenerator;
+import org.spongycastle.crypto.generators.DSAParametersGenerator;
+import org.spongycastle.crypto.params.DSAKeyGenerationParameters;
+import org.spongycastle.crypto.params.DSAParameterGenerationParameters;
+import org.spongycastle.crypto.params.DSAParameters;
+import org.spongycastle.crypto.params.DSAPrivateKeyParameters;
+import org.spongycastle.crypto.params.DSAPublicKeyParameters;
+import org.spongycastle.crypto.params.DSAValidationParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.DSASigner;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.FixedSecureRandom;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test based on FIPS 186-2, Appendix 5, an example of DSA, and FIPS 168-3 test vectors.
+ */
+public class DSATest
+ extends SimpleTest
+{
+ byte[] k1 = Hex.decode("d5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
+ byte[] k2 = Hex.decode("345e8d05c075c3a508df729a1685690e68fcfb8c8117847e89063bca1f85d968fd281540b6e13bd1af989a1fbf17e06462bf511f9d0b140fb48ac1b1baa5bded");
+
+ SecureRandom random = new FixedSecureRandom(new byte[][] { k1, k2});
+
+ byte[] keyData = Hex.decode("b5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
+
+ SecureRandom keyRandom = new FixedSecureRandom(new byte[][] { keyData, keyData });
+
+ BigInteger pValue = new BigInteger("8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac49693dfbf83724c2ec0736ee31c80291", 16);
+ BigInteger qValue = new BigInteger("c773218c737ec8ee993b4f2ded30f48edace915f", 16);
+
+ public String getName()
+ {
+ return "DSA";
+ }
+
+ public void performTest()
+ {
+ BigInteger r = new BigInteger("68076202252361894315274692543577577550894681403");
+ BigInteger s = new BigInteger("1089214853334067536215539335472893651470583479365");
+ DSAParametersGenerator pGen = new DSAParametersGenerator();
+
+ pGen.init(512, 80, random);
+
+ DSAParameters params = pGen.generateParameters();
+ DSAValidationParameters pValid = params.getValidationParameters();
+
+ if (pValid.getCounter() != 105)
+ {
+ fail("Counter wrong");
+ }
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ fail("p or q wrong");
+ }
+
+ DSAKeyPairGenerator dsaKeyGen = new DSAKeyPairGenerator();
+ DSAKeyGenerationParameters genParam = new DSAKeyGenerationParameters(keyRandom, params);
+
+ dsaKeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = dsaKeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), keyRandom);
+
+ DSASigner dsa = new DSASigner();
+
+ dsa.init(true, param);
+
+ byte[] message = BigIntegers.asUnsignedByteArray(new BigInteger("968236873715988614170569073515315707566766479517"));
+ BigInteger[] sig = dsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong.", r, sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong.", s, sig[1]);
+ }
+
+ dsa.init(false, pair.getPublic());
+
+ if (!dsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("verification fails");
+ }
+
+ dsa2Test1();
+ dsa2Test2();
+ dsa2Test3();
+ dsa2Test4();
+ }
+
+ private void dsa2Test1()
+ {
+ byte[] seed = Hex.decode("ED8BEE8D1CB89229D2903CBF0E51EE7377F48698");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator();
+
+ pGen.init(new DSAParameterGenerationParameters(1024, 160, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 5)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("E950511EAB424B9A19A2AEB4E159B7844C589C4F", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "E0A67598CD1B763B" +
+ "C98C8ABB333E5DDA0CD3AA0E5E1FB5BA8A7B4EABC10BA338" +
+ "FAE06DD4B90FDA70D7CF0CB0C638BE3341BEC0AF8A7330A3" +
+ "307DED2299A0EE606DF035177A239C34A912C202AA5F83B9" +
+ "C4A7CF0235B5316BFC6EFB9A248411258B30B839AF172440" +
+ "F32563056CB67A861158DDD90E6A894C72A5BBEF9E286C6B", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "D29D5121B0423C27" +
+ "69AB21843E5A3240FF19CACC792264E3BB6BE4F78EDD1B15" +
+ "C4DFF7F1D905431F0AB16790E1F773B5CE01C804E509066A" +
+ "9919F5195F4ABC58189FD9FF987389CB5BEDF21B4DAB4F8B" +
+ "76A055FFE2770988FE2EC2DE11AD92219F0B351869AC24DA" +
+ "3D7BA87011A701CE8EE7BFE49486ED4527B7186CA4610A75", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("D0EC4E50BB290A42E9E355C73D8809345DE2E139")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "25282217F5730501" +
+ "DD8DBA3EDFCF349AAFFEC20921128D70FAC44110332201BB" +
+ "A3F10986140CBB97C726938060473C8EC97B4731DB004293" +
+ "B5E730363609DF9780F8D883D8C4D41DED6A2F1E1BBBDC97" +
+ "9E1B9D6D3C940301F4E978D65B19041FCF1E8B518F5C0576" +
+ "C770FE5A7A485D8329EE2914A2DE1B5DA4A6128CEAB70F79", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("D0EC4E50BB290A42E9E355C73D8809345DE2E139", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("349C55648DCF992F3F33E8026CFAC87C1D2BA075"))));
+
+ byte[] msg = Hex.decode("A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("636155AC9A4633B4665D179F9E4117DF68601F34", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("6C540B02D9D4852F89DF8CFC99963204F4347704", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+
+ }
+
+ private void dsa2Test2()
+ {
+ byte[] seed = Hex.decode("5AFCC1EFFC079A9CCA6ECA86D6E3CC3B18642D9BE1CC6207C84002A9");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA224Digest());
+
+ pGen.init(new DSAParameterGenerationParameters(2048, 224, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 21)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("90EAF4D1AF0708B1B612FF35E0A2997EB9E9D263C9CE659528945C0D", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "C196BA05AC29E1F9C3C72D56DFFC6154" +
+ "A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A06" +
+ "7CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE4" +
+ "28782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE6" +
+ "19ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1" +
+ "E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD9" +
+ "2D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BF" +
+ "FAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E" +
+ "5320121496DC65B3930E38047294FF877831A16D5228418D" +
+ "E8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A040" +
+ "2A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "A59A749A11242C58C894E9E5A91804E8"+
+ "FA0AC64B56288F8D47D51B1EDC4D65444FECA0111D78F35F"+
+ "C9FDD4CB1F1B79A3BA9CBEE83A3F811012503C8117F98E50"+
+ "48B089E387AF6949BF8784EBD9EF45876F2E6A5A495BE64B"+
+ "6E770409494B7FEE1DBB1E4B2BC2A53D4F893D418B715959"+
+ "2E4FFFDF6969E91D770DAEBD0B5CB14C00AD68EC7DC1E574"+
+ "5EA55C706C4A1C5C88964E34D09DEB753AD418C1AD0F4FDF"+
+ "D049A955E5D78491C0B7A2F1575A008CCD727AB376DB6E69"+
+ "5515B05BD412F5B8C2F4C77EE10DA48ABD53F5DD498927EE"+
+ "7B692BBBCDA2FB23A516C5B4533D73980B2A3B60E384ED20"+
+ "0AE21B40D273651AD6060C13D97FD69AA13C5611A51B9085", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("00D0F09ED3E2568F6CADF9224117DA2AEC5A4300E009DE1366023E17")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "70035C9A3B225B258F16741F3941FBF0" +
+ "6F3D056CD7BD864604CBB5EE9DD85304EE8E8E4ABD5E9032" +
+ "11DDF25CE149075510ACE166970AFDC7DF552B7244F342FA" +
+ "02F7A621405B754909D757F97290E1FE5036E904CF593446" +
+ "0C046D95659821E1597ED9F2B1F0E20863A6BBD0CE74DACB" +
+ "A5D8C68A90B29C2157CDEDB82EC12B81EE3068F9BF5F7F34" +
+ "6ECA41ED174CCCD7D154FA4F42F80FFE1BF46AE9D8125DEB" +
+ "5B4BA08A72BDD86596DBEDDC9550FDD650C58F5AE5133509" +
+ "A702F79A31ECB490F7A3C5581631F7C5BE4FF7F9E9F27FA3" +
+ "90E47347AD1183509FED6FCF198BA9A71AB3335B4F38BE8D" +
+ "15496A00B6DC2263E20A5F6B662320A3A1EC033AA61E3B68", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("00D0F09ED3E2568F6CADF9224117DA2AEC5A4300E009DE1366023E17", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("735959CC4463B8B440E407EECA8A473BF6A6D1FE657546F67D401F05"))));
+
+ byte[] msg = Hex.decode("23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("4400138D05F9639CAF54A583CAAF25D2B76D0C3EAD752CE17DBC85FE", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("874D4F12CB13B61732D398445698CFA9D92381D938AA57EE2C9327B3", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+ }
+
+ private void dsa2Test3()
+ {
+ byte[] seed = Hex.decode("4783081972865EA95D43318AB2EAF9C61A2FC7BBF1B772A09017BDF5A58F4FF0");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA256Digest());
+
+ pGen.init(new DSAParameterGenerationParameters(2048, 256, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 12)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("C24ED361870B61E0D367F008F99F8A1F75525889C89DB1B673C45AF5867CB467", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "F56C2A7D366E3EBDEAA1891FD2A0D099" +
+ "436438A673FED4D75F594959CFFEBCA7BE0FC72E4FE67D91" +
+ "D801CBA0693AC4ED9E411B41D19E2FD1699C4390AD27D94C" +
+ "69C0B143F1DC88932CFE2310C886412047BD9B1C7A67F8A2" +
+ "5909132627F51A0C866877E672E555342BDF9355347DBD43" +
+ "B47156B2C20BAD9D2B071BC2FDCF9757F75C168C5D9FC431" +
+ "31BE162A0756D1BDEC2CA0EB0E3B018A8B38D3EF2487782A" +
+ "EB9FBF99D8B30499C55E4F61E5C7DCEE2A2BB55BD7F75FCD" +
+ "F00E48F2E8356BDB59D86114028F67B8E07B127744778AFF" +
+ "1CF1399A4D679D92FDE7D941C5C85C5D7BFF91BA69F9489D" +
+ "531D1EBFA727CFDA651390F8021719FA9F7216CEB177BD75", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "8DC6CC814CAE4A1C05A3E186A6FE27EA" +
+ "BA8CDB133FDCE14A963A92E809790CBA096EAA26140550C1" +
+ "29FA2B98C16E84236AA33BF919CD6F587E048C52666576DB" +
+ "6E925C6CBE9B9EC5C16020F9A44C9F1C8F7A8E611C1F6EC2" +
+ "513EA6AA0B8D0F72FED73CA37DF240DB57BBB27431D61869" +
+ "7B9E771B0B301D5DF05955425061A30DC6D33BB6D2A32BD0" +
+ "A75A0A71D2184F506372ABF84A56AEEEA8EB693BF29A6403" +
+ "45FA1298A16E85421B2208D00068A5A42915F82CF0B858C8" +
+ "FA39D43D704B6927E0B2F916304E86FB6A1B487F07D8139E" +
+ "428BB096C6D67A76EC0B8D4EF274B8A2CF556D279AD267CC" +
+ "EF5AF477AFED029F485B5597739F5D0240F67C2D948A6279", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "2828003D7C747199143C370FDD07A286" +
+ "1524514ACC57F63F80C38C2087C6B795B62DE1C224BF8D1D" +
+ "1424E60CE3F5AE3F76C754A2464AF292286D873A7A30B7EA" +
+ "CBBC75AAFDE7191D9157598CDB0B60E0C5AA3F6EBE425500" +
+ "C611957DBF5ED35490714A42811FDCDEB19AF2AB30BEADFF" +
+ "2907931CEE7F3B55532CFFAEB371F84F01347630EB227A41" +
+ "9B1F3F558BC8A509D64A765D8987D493B007C4412C297CAF" +
+ "41566E26FAEE475137EC781A0DC088A26C8804A98C23140E" +
+ "7C936281864B99571EE95C416AA38CEEBB41FDBFF1EB1D1D" +
+ "C97B63CE1355257627C8B0FD840DDB20ED35BE92F08C49AE" +
+ "A5613957D7E5C7A6D5A5834B4CB069E0831753ECF65BA02B", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C"))));
+
+ byte[] msg = Hex.decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("315C875DCD4850E948B8AC42824E9483A32D5BA5ABE0681B9B9448D444F2BE3C", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("89718D12E54A8D9ED066E4A55F7ED5A2229CD23B9A3CEE78F83ED6AA61F6BCB9", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+ }
+
+ private void dsa2Test4()
+ {
+ byte[] seed = Hex.decode("193AFCA7C1E77B3C1ECC618C81322E47B8B8B997C9C83515C59CC446C2D9BD47");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA256Digest());
+
+ pGen.init(new DSAParameterGenerationParameters(3072, 256, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 20)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD610" +
+ "37E56258A7795A1C7AD46076982CE6BB956936C6AB4DCFE0" +
+ "5E6784586940CA544B9B2140E1EB523F009D20A7E7880E4E" +
+ "5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1" +
+ "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D" +
+ "3485261CD068699B6BA58A1DDBBEF6DB51E8FE34E8A78E54" +
+ "2D7BA351C21EA8D8F1D29F5D5D15939487E27F4416B0CA63" +
+ "2C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0" +
+ "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0E" +
+ "E6F29AF7F642773EBE8CD5402415A01451A840476B2FCEB0" +
+ "E388D30D4B376C37FE401C2A2C2F941DAD179C540C1C8CE0" +
+ "30D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F" +
+ "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C56" +
+ "0EA878DE87C11E3D597F1FEA742D73EEC7F37BE43949EF1A" +
+ "0D15C3F3E3FC0A8335617055AC91328EC22B50FC15B941D3" +
+ "D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE" +
+ "3B7ACCC54D521E37F84A4BDD5B06B0970CC2D2BBB715F7B8" +
+ "2846F9A0C393914C792E6A923E2117AB805276A975AADB52" +
+ "61D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1" +
+ "F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A" +
+ "60126FEB2CF05DB8A7F0F09B3397F3937F2E90B9E5B9C9B6" +
+ "EFEF642BC48351C46FB171B9BFA9EF17A961CE96C7E7A7CC" +
+ "3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C" +
+ "4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B6" +
+ "7299E231F8BD90B39AC3AE3BE0C6B6CACEF8289A2E2873D5" +
+ "8E51E029CAFBD55E6841489AB66B5B4B9BA6E2F784660896" +
+ "AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8" +
+ "E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B98856" +
+ "7A88126B914D7828E2B63A6D7ED0747EC59E0E0A23CE7D8A" +
+ "74C1D2C2A7AFB6A29799620F00E11C33787F7DED3B30E1A2" +
+ "2D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("3ABC1587297CE7B9EA1AD6651CF2BC4D7F92ED25CABC8553F567D1B40EBB8764")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "8B891C8692D3DE875879390F2698B26FBECCA6B075535DCE" +
+ "6B0C862577F9FA0DEF6074E7A7624121224A595896ABD4CD" +
+ "A56B2CEFB942E025D2A4282FFAA98A48CDB47E1A6FCB5CFB" +
+ "393EF35AF9DF913102BB303C2B5C36C3F8FC04ED7B8B69FE" +
+ "FE0CF3E1FC05CFA713B3435B2656E913BA8874AEA9F93600" +
+ "6AEB448BCD005D18EC3562A33D04CF25C8D3D69844343442" +
+ "FA3DB7DE618C5E2DA064573E61E6D5581BFB694A23AC87FD" +
+ "5B52D62E954E1376DB8DDB524FFC0D469DF978792EE44173" +
+ "8E5DB05A7DC43E94C11A2E7A4FBE383071FA36D2A7EC8A93" +
+ "88FE1C4F79888A99D3B6105697C2556B79BB4D7E781CEBB3" +
+ "D4866AD825A5E830846072289FDBC941FA679CA82F5F78B7" +
+ "461B2404DB883D215F4E0676CF5493950AC5591697BFEA8D" +
+ "1EE6EC016B89BA51CAFB5F9C84C989FA117375E94578F28B" +
+ "E0B34CE0545DA46266FD77F62D8F2CEE92AB77012AFEBC11" +
+ "008985A821CD2D978C7E6FE7499D1AAF8DE632C21BB48CA5" +
+ "CBF9F31098FD3FD3854C49A65D9201744AACE540354974F9", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("3ABC1587297CE7B9EA1AD6651CF2BC4D7F92ED25CABC8553F567D1B40EBB8764", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("A6902C1E6E3943C5628061588A8B007BCCEA91DBF12915483F04B24AB0678BEE"))));
+
+ byte[] msg = Hex.decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("5F184E645A38BE8FB4A6871B6503A9D12924C7ABE04B71410066C2ECA6E3BE3E", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("91EB0C7BA3D4B9B60B825C3D9F2CADA8A2C9D7723267B033CBCDCF8803DB9C18", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DSATest());
+ }
+
+ private class DSATestSecureRandom
+ extends FixedSecureRandom
+ {
+ private boolean first = true;
+
+ public DSATestSecureRandom(byte[] value)
+ {
+ super(value);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ if (first)
+ {
+ super.nextBytes(bytes);
+ first = false;
+ }
+ else
+ {
+ bytes[bytes.length - 1] = 2;
+ }
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DSTU4145Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DSTU4145Test.java
new file mode 100644
index 000000000..43d27f09d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DSTU4145Test.java
@@ -0,0 +1,278 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.DSTU4145Signer;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.FixedSecureRandom;
+import org.spongycastle.util.test.SimpleTest;
+
+public class DSTU4145Test
+ extends SimpleTest
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ public static void main(String[] args)
+ {
+ runTest(new DSTU4145Test());
+ }
+
+ public String getName()
+ {
+ return "DSTU4145";
+ }
+
+ private void test163()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("01025e40bd97db012b7a1d79de8e12932d247f61c6"));
+
+ byte[] hash = Hex.decode("09c9c44277910c9aaee486883a2eb95b7180166ddf73532eeb76edaef52247ff");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("274ea2c0caa014a0d80a424f59ade7a93068d08a7", 16);
+ BigInteger s = new BigInteger("2100d86957331832b8e8c230f5bd6a332b3615aca", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(163, 3, 6, 7, ONE, new BigInteger("5FF6108462A2DC8210AB403925E638A19C1455D21", 16));
+ ECPoint P = curve.createPoint(new BigInteger("72d867f93a93ac27df9ff01affe74885c8c540420", 16), new BigInteger("0224a9c3947852b97c5599d5f4ab81122adc3fd9b", 16));
+ BigInteger n = new BigInteger("400000000000000000002BEC12BE2262D39BCF14D", 16);
+
+ BigInteger d = new BigInteger("183f60fdf7951ff47d67193f8d073790c1c9b5a3e", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void test173()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("0000137449348C1249971759D99C252FFE1E14D8B31F"));
+
+ byte[] hash = Hex.decode("0137187EA862117EF1484289470ECAC802C5A651FDA8");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("13ae89746386709cdbd237cc5ec20ca30004a82ead8", 16);
+ BigInteger s = new BigInteger("3597912cdd093b3e711ccb74a79d3c4ab4c7cccdc60", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(173, 1, 2, 10, ZERO, new BigInteger("108576C80499DB2FC16EDDF6853BBB278F6B6FB437D9", 16));
+ ECPoint P = curve.createPoint(new BigInteger("BE6628EC3E67A91A4E470894FBA72B52C515F8AEE9", 16), new BigInteger("D9DEEDF655CF5412313C11CA566CDC71F4DA57DB45C", 16));
+ BigInteger n = new BigInteger("800000000000000000000189B4E67606E3825BB2831", 16);
+
+ BigInteger d = new BigInteger("955CD7E344303D1034E66933DC21C8044D42ADB8", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void test283()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("00000000245383CB3AD41BF30F5F7E8FBA858509B2D5558C92D539A6D994BFA98BC6940E"));
+
+ byte[] hash = Hex.decode("0137187EA862117EF1484289470ECAC802C5A651FDA8");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("12a5edcc38d92208ff23036d75b000c7e4bc0f9af2d40b35f15d6fd15e01234e67781a8", 16);
+ BigInteger s = new BigInteger("2de0775577f75b643cf5afc80d4fe10b21100690f17e2cab7bdc9b50ec87c5727aeb515", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(283, 5, 7, 12, ONE, new BigInteger("27B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5", 16));
+ ECPoint P = curve.createPoint(new BigInteger("4D95820ACE761110824CE425C8089129487389B7F0E0A9D043DDC0BB0A4CC9EB25", 16), new BigInteger("954C9C4029B2C62DE35C2B9C2A164984BF1101951E3A68ED03DF234DDE5BB2013152F2", 16));
+ BigInteger n = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307", 16);
+
+ BigInteger d = new BigInteger("B844EEAF15213E4BAD4FB84796D68F2448DB8EB7B4621EC0D51929874892C43E", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void test431()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("0000C4224DBBD800988DBAA39DE838294C345CDA5F5929D1174AA8D9340A5E79D10ACADE6B53CF873E7301A3871C2073AD75AB530457"));
+
+ byte[] hash = Hex.decode("0137187EA862117EF1484289470ECAC802C5A651FDA8");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("1911fefb1f494bebcf8dffdf5276946ff9c9f662192ee18c718db47310a439c784fe07577b16e1edbe16179876e0792a634f1c9c3a2e", 16);
+ BigInteger s = new BigInteger("3852170ee801c2083c52f1ea77b987a5432acecd9c654f064e87bf179e0a397151edbca430082e43bd38a67b55424b5bbc7f2713f620", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(431, 1, 3, 5, ONE, new BigInteger("3CE10490F6A708FC26DFE8C3D27C4F94E690134D5BFF988D8D28AAEAEDE975936C66BAC536B18AE2DC312CA493117DAA469C640CAF3", 16));
+ ECPoint P = curve.createPoint(new BigInteger("9548BCDF314CEEEAF099C780FFEFBF93F9FE5B5F55547603C9C8FC1A2774170882B3BE35E892C6D4296B8DEA282EC30FB344272791", 16), new BigInteger("4C6CBD7C62A8EEEFDE17A8B5E196E49A22CE6DE128ABD9FBD81FA4411AD5A38E2A810BEDE09A7C6226BCDCB4A4A5DA37B4725E00AA74", 16));
+ BigInteger n = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA3175458009A8C0A724F02F81AA8A1FCBAF80D90C7A95110504CF", 16);
+
+ BigInteger d = new BigInteger("D0F97354E314191FD773E2404F478C8AEE0FF5109F39E6F37D1FEEC8B2ED1691D84C9882CC729E716A71CC013F66CAC60E29E22C", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void testTruncation()
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("0000C4224DBBD800988DBAA39DE838294C345CDA5F5929D1174AA8D9340A5E79D10ACADE6B53CF873E7301A3871C2073AD75AB530457"));
+
+ // use extra long "hash" with set bits...
+ byte[] hash = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+ ECCurve.F2m curve = new ECCurve.F2m(173, 1, 2, 10, ZERO, new BigInteger("108576C80499DB2FC16EDDF6853BBB278F6B6FB437D9", 16));
+ ECPoint P = curve.createPoint(new BigInteger("BE6628EC3E67A91A4E470894FBA72B52C515F8AEE9", 16), new BigInteger("D9DEEDF655CF5412313C11CA566CDC71F4DA57DB45C", 16));
+ BigInteger n = new BigInteger("800000000000000000000189B4E67606E3825BB2831", 16);
+
+ BigInteger d = new BigInteger("955CD7E344303D1034E66933DC21C8044D42ADB8", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ BigInteger r = new BigInteger("6bb5c0cb82e5067485458ebfe81025f03b687c63a27", 16);
+ BigInteger s = new BigInteger("34d6b1868969b86ecf934167c8fe352c63d1074bd", 16);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, rs[0], rs[1]))
+ {
+ fail("verification fails");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ test163();
+ test173();
+ test283();
+ test431();
+ testTruncation();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DeterministicDSATest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DeterministicDSATest.java
new file mode 100644
index 000000000..e5f61ec2f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DeterministicDSATest.java
@@ -0,0 +1,513 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DSA;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA224Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.params.DSAParameters;
+import org.spongycastle.crypto.params.DSAPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.signers.DSASigner;
+import org.spongycastle.crypto.signers.ECDSASigner;
+import org.spongycastle.crypto.signers.HMacDSAKCalculator;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Tests are taken from RFC 6979 - "Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)"
+ */
+public class DeterministicDSATest
+ extends SimpleTest
+{
+
+ public static final byte[] SAMPLE = Hex.decode("73616d706c65"); // "sample"
+ public static final byte[] TEST = Hex.decode("74657374"); // "test"
+
+ // test vectors from appendix in RFC 6979
+ private void testHMacDeterministic()
+ {
+ DSAParameters dsaParameters = new DSAParameters(
+ new BigInteger("86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447" +
+ "E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88" +
+ "73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C" +
+ "881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779", 16),
+ new BigInteger("996F967F6C8E388D9E28D01E205FBA957A5698B1", 16),
+ new BigInteger("07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D" +
+ "89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD" +
+ "87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4" +
+ "17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD", 16));
+
+ DSAPrivateKeyParameters privKey = new DSAPrivateKeyParameters(new BigInteger("411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", 16), dsaParameters);
+
+ doTestHMACDetDSASample(new SHA1Digest(), privKey, new BigInteger("2E1A0C2562B2912CAAF89186FB0F42001585DA55", 16), new BigInteger("29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5", 16));
+ doTestHMACDetDSASample(new SHA224Digest(), privKey, new BigInteger("4BC3B686AEA70145856814A6F1BB53346F02101E", 16), new BigInteger("410697B92295D994D21EDD2F4ADA85566F6F94C1", 16));
+ doTestHMACDetDSASample(new SHA256Digest(), privKey, new BigInteger("81F2F5850BE5BC123C43F71A3033E9384611C545", 16), new BigInteger("4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89", 16));
+ doTestHMACDetDSASample(new SHA384Digest(), privKey, new BigInteger("07F2108557EE0E3921BC1774F1CA9B410B4CE65A", 16), new BigInteger("54DF70456C86FAC10FAB47C1949AB83F2C6F7595", 16));
+ doTestHMACDetDSASample(new SHA512Digest(), privKey, new BigInteger("16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B", 16), new BigInteger("02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C", 16));
+
+ doTestHMACDetDSATest(new SHA1Digest(), privKey, new BigInteger("42AB2052FD43E123F0607F115052A67DCD9C5C77", 16), new BigInteger("183916B0230D45B9931491D4C6B0BD2FB4AAF088", 16));
+ doTestHMACDetDSATest(new SHA224Digest(), privKey, new BigInteger("6868E9964E36C1689F6037F91F28D5F2C30610F2", 16), new BigInteger("49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F", 16));
+ doTestHMACDetDSATest(new SHA256Digest(), privKey, new BigInteger("22518C127299B0F6FDC9872B282B9E70D0790812", 16), new BigInteger("6837EC18F150D55DE95B5E29BE7AF5D01E4FE160", 16));
+ doTestHMACDetDSATest(new SHA384Digest(), privKey, new BigInteger("854CF929B58D73C3CBFDC421E8D5430CD6DB5E66", 16), new BigInteger("91D0E0F53E22F898D158380676A871A157CDA622", 16));
+ doTestHMACDetDSATest(new SHA512Digest(), privKey, new BigInteger("8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0", 16), new BigInteger("7C670C7AD72B6C050C109E1790008097125433E8", 16));
+
+ dsaParameters = new DSAParameters(
+ new BigInteger("9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
+ "C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
+ "FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
+ "B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
+ "35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
+ "F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
+ "92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
+ "3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", 16),
+ new BigInteger("F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F", 16),
+ new BigInteger("5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
+ "D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
+ "6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
+ "085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
+ "AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
+ "3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
+ "BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
+ "DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", 16));
+
+ privKey = new DSAPrivateKeyParameters(new BigInteger("69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC", 16), dsaParameters);
+
+ doTestHMACDetDSASample(new SHA1Digest(), privKey, new BigInteger("3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A", 16), new BigInteger("D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF", 16));
+ doTestHMACDetDSASample(new SHA224Digest(), privKey, new BigInteger("DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C", 16), new BigInteger("A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC", 16));
+ doTestHMACDetDSASample(new SHA256Digest(), privKey, new BigInteger("EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809", 16), new BigInteger("7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53", 16));
+ doTestHMACDetDSASample(new SHA384Digest(), privKey, new BigInteger("B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B", 16), new BigInteger("19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B", 16));
+ doTestHMACDetDSASample(new SHA512Digest(), privKey, new BigInteger("2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E", 16), new BigInteger("D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351", 16));
+
+ doTestHMACDetDSATest(new SHA1Digest(), privKey, new BigInteger("C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0", 16), new BigInteger("414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA", 16));
+ doTestHMACDetDSATest(new SHA224Digest(), privKey, new BigInteger("272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3", 16), new BigInteger("E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806", 16));
+ doTestHMACDetDSATest(new SHA256Digest(), privKey, new BigInteger("8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0", 16), new BigInteger("7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E", 16));
+ doTestHMACDetDSATest(new SHA384Digest(), privKey, new BigInteger("239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE", 16), new BigInteger("6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961", 16));
+ doTestHMACDetDSATest(new SHA512Digest(), privKey, new BigInteger("89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307", 16), new BigInteger("C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1", 16));
+ }
+
+ private void doTestHMACDetDSASample(Digest digest, DSAPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new DSASigner(new HMacDSAKCalculator(digest)), digest, SAMPLE, privKey, r, s);
+ }
+
+ private void doTestHMACDetDSATest(Digest digest, DSAPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new DSASigner(new HMacDSAKCalculator(digest)), digest, TEST, privKey, r, s);
+ }
+
+ // test vectors from appendix in RFC 6979
+ private void testECHMacDeterministic()
+ {
+ X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-192");
+ ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("98C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF", 16), new BigInteger("57A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("A1F00DAD97AEEC91C95585F36200C65F3C01812AA60378F5", 16), new BigInteger("E07EC1304C7C6C9DEBBE980B9692668F81D4DE7922A0F97A", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55", 16), new BigInteger("CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("DA63BF0B9ABCF948FBB1E9167F136145F7A20426DCC287D5", 16), new BigInteger("C3AA2C960972BD7A2003A57E1C4C77F0578F8AE95E31EC5E", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("4D60C5AB1996BD848343B31C00850205E2EA6922DAC2E4B8", 16), new BigInteger("3F6E837448F027A1BF4B34E796E32A811CBB4050908D8F67", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("0F2141A0EBBC44D2E1AF90A50EBCFCE5E197B3B7D4DE036D", 16), new BigInteger("EB18BC9E1F3D7387500CB99CF5F7C157070A8961E38700B7", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("6945A1C1D1B2206B8145548F633BB61CEF04891BAF26ED34", 16), new BigInteger("B7FB7FDFC339C0B9BD61A9F5A8EAF9BE58FC5CBA2CB15293", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE", 16), new BigInteger("5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("B234B60B4DB75A733E19280A7A6034BD6B1EE88AF5332367", 16), new BigInteger("7994090B2D59BB782BE57E74A44C9A1C700413F8ABEFE77A", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("FE4F4AE86A58B6507946715934FE2D8FF9D95B6B098FE739", 16), new BigInteger("74CF5605C98FBA0E1EF34D4B5A1577A7DCF59457CAE52290", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-224");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("22226F9D40A96E19C4A301CE5B74B115303C0F3A4FD30FC257FB57AC", 16), new BigInteger("66D1CDD83E3AF75605DD6E2FEFF196D30AA7ED7A2EDF7AF475403D69", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E", 16), new BigInteger("A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("61AA3DA010E8E8406C656BC477A7A7189895E7E840CDFE8FF42307BA", 16), new BigInteger("BC814050DAB5D23770879494F9E0A680DC1AF7161991BDE692B10101", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("0B115E5E36F0F9EC81F1325A5952878D745E19D7BB3EABFABA77E953", 16), new BigInteger("830F34CCDFE826CCFDC81EB4129772E20E122348A2BBD889A1B1AF1D", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("074BD1D979D5F32BF958DDC61E4FB4872ADCAFEB2256497CDAC30397", 16), new BigInteger("A4CECA196C3D5A1FF31027B33185DC8EE43F288B21AB342E5D8EB084", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("DEAA646EC2AF2EA8AD53ED66B2E2DDAA49A12EFD8356561451F3E21C", 16), new BigInteger("95987796F6CF2062AB8135271DE56AE55366C045F6D9593F53787BD2", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019", 16), new BigInteger("902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("AD04DDE87B84747A243A631EA47A1BA6D1FAA059149AD2440DE6FBA6", 16), new BigInteger("178D49B1AE90E3D8B629BE3DB5683915F4E8C99FDF6E666CF37ADCFD", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("389B92682E399B26518A95506B52C03BC9379A9DADF3391A21FB0EA4", 16), new BigInteger("414A718ED3249FF6DBC5B50C27F71F01F070944DA22AB1F78F559AAB", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("049F050477C5ADD858CAC56208394B5A55BAEBBE887FDF765047C17C", 16), new BigInteger("077EB13E7005929CEFA3CD0403C7CDCC077ADF4E44F3C41B2F60ECFF", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-256");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32", 16), new BigInteger("6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F", 16), new BigInteger("B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716", 16), new BigInteger("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719", 16), new BigInteger("4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00", 16), new BigInteger("2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89", 16), new BigInteger("01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692", 16), new BigInteger("C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367", 16), new BigInteger("019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6", 16), new BigInteger("8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04", 16), new BigInteger("39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-384");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D8" +
+ "96D5724E4C70A825F872C9EA60D2EDF5", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA3" +
+ "7B9BA002899F6FDA3A4A9386790D4EB2", 16),
+ new BigInteger("A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF" +
+ "26F49CA031D4857570CCB5CA4424A443", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366" +
+ "450F76EE3DE43F5A125333A6BE060122", 16),
+ new BigInteger("9DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E483" +
+ "4C082C03D83028EFBF93A3C23940CA8D", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33" +
+ "BDE1E888E63355D92FA2B3C36D8FB2CD", 16),
+ new BigInteger("F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEB" +
+ "EFDC63ECCD1AC42EC0CB8668A4FA0AB0", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C" +
+ "81A648152E44ACF96E36DD1E80FABE46", 16),
+ new BigInteger("99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94F" +
+ "A329C145786E679E7B82C71A38628AC8", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799C" +
+ "FE30F35CC900056D7C99CD7882433709", 16),
+ new BigInteger("512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112" +
+ "DC7CC3EF3446DEFCEB01A45C2667FDD5", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678" +
+ "ACD9D29876DAF46638645F7F404B11C7", 16),
+ new BigInteger("D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A29916" +
+ "95BA1C84541327E966FA7B50F7382282", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E624" +
+ "64A9A817C47FF78B8C11066B24080E72", 16),
+ new BigInteger("07041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C614" +
+ "1C53EA5ABEF0D8231077A04540A96B66", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559" +
+ "F918EEDAF2293BE5B475CC8F0188636B", 16),
+ new BigInteger("2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D" +
+ "51AB373F9845C0514EEFB14024787265", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB" +
+ "0542A7F0812998DA8F1DD3CA3CF023DB", 16),
+ new BigInteger("DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E0" +
+ "6A739F040649A667BF3B828246BAA5A5", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D0" +
+ "6FB6495CD21B4B6E340FC236584FB277", 16),
+ new BigInteger("976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B22463" +
+ "4A2092CD3792E0159AD9CEE37659C736", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-521");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75C" +
+ "AA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83" +
+ "538", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("0343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910" +
+ "FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D" +
+ "75D", 16),
+ new BigInteger("0E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D" +
+ "5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5" +
+ "D16", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("1776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A3" +
+ "0715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2E" +
+ "D2E", 16),
+ new BigInteger("050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17B" +
+ "A41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B" +
+ "41F", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("1511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659" +
+ "D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E" +
+ "1A7", 16),
+ new BigInteger("04A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916" +
+ "E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7E" +
+ "CFC", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("1EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4" +
+ "B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67" +
+ "451", 16),
+ new BigInteger("1F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5" +
+ "FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65" +
+ "D61", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F1" +
+ "74E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E37" +
+ "7FA", 16),
+ new BigInteger("0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF2" +
+ "82623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A" +
+ "67A", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("13BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0" +
+ "693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0" +
+ "367", 16),
+ new BigInteger("1E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90" +
+ "F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC91679" +
+ "7FF", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("1C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086" +
+ "BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE1" +
+ "7FB", 16),
+ new BigInteger("177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5" +
+ "BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD51" +
+ "9A4", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("00E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D807104" +
+ "2EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656" +
+ "AA8", 16),
+ new BigInteger("0CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9" +
+ "FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694" +
+ "E86", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("14BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C" +
+ "89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF60755" +
+ "78C", 16),
+ new BigInteger("133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0E" +
+ "D94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B" +
+ "979", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10" +
+ "CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47E" +
+ "E6D", 16),
+ new BigInteger("1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78" +
+ "A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4D" +
+ "CE3", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-163");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("35318FC447D48D7E6BC93B48617DDDEDF26AA658F", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("153FEBD179A69B6122DEBF5BC61EB947B24C93526", 16), new BigInteger("37AC9C670F8CF18045049BAE7DD35553545C19E49", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("0A379E69C44F9C16EA3215EA39EB1A9B5D58CC955", 16), new BigInteger("04BAFF5308DA2A7FE2C1742769265AD3ED1D24E74", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("134E00F78FC1CB9501675D91C401DE20DDF228CDC", 16), new BigInteger("373273AEC6C36CB7BAFBB1903A5F5EA6A1D50B624", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("29430B935AF8E77519B0CA4F6903B0B82E6A21A66", 16), new BigInteger("1EA1415306E9353FA5AA54BC7C2581DFBB888440D", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("0B2F177A99F9DF2D51CCAF55F015F326E4B65E7A0", 16), new BigInteger("0DF1FB4487E9B120C5E970EFE48F55E406306C3A1", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("256D4079C6C7169B8BC92529D701776A269D56308", 16), new BigInteger("341D3FFEC9F1EB6A6ACBE88E3C86A1C8FDEB8B8E1", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("28ECC6F1272CE80EA59DCF32F7AC2D861BA803393", 16), new BigInteger("0AD4AE2C06E60183C1567D2B82F19421FE3053CE2", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("227DF377B3FA50F90C1CB3CDCBBDBA552C1D35104", 16), new BigInteger("1F7BEAD92583FE920D353F368C1960D0E88B46A56", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("11811DAFEEA441845B6118A0DFEE8A0061231337D", 16), new BigInteger("36258301865EE48C5C6F91D63F62695002AB55B57", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("3B6BB95CA823BE2ED8E3972FF516EB8972D765571", 16), new BigInteger("13DC6F420628969DF900C3FCC48220B38BE24A541", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-233");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("07ADC13DD5BF34D1DDEEB50B2CE23B5F5E6D18067306D60C5F6FF11E5D3", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("015CC6FD78BB06E0878E71465515EA5A21A2C18E6FC77B4B158DBEB3944", 16), new BigInteger("0822A4A6C2EB2DF213A5E90BF40377956365EE8C4B4A5A4E2EB9270CB6A", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("05D9920B53471148E10502AB49AB7A3F11084820A074FD89883CF51BC1A", 16), new BigInteger("04D3938900C0A9AAA7080D1DFEB56CFB0FADABE4214536C7ED5117ED13A", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("0A797F3B8AEFCE7456202DF1E46CCC291EA5A49DA3D4BDDA9A4B62D5E0D", 16), new BigInteger("01F6F81DA55C22DA4152134C661588F4BD6F82FDBAF0C5877096B070DC2", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("015E85A8D46225DD7E314A1C4289731FC14DECE949349FE535D11043B85", 16), new BigInteger("03F189D37F50493EFD5111A129443A662AB3C6B289129AD8C0CAC85119C", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("03B62A4BF783919098B1E42F496E65F7621F01D1D466C46940F0F132A95", 16), new BigInteger("0F4BE031C6E5239E7DAA014CBBF1ED19425E49DAEB426EC9DF4C28A2E30", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("02F1FEDC57BE203E4C8C6B8C1CEB35E13C1FCD956AB41E3BD4C8A6EFB1F", 16), new BigInteger("05738EC8A8EDEA8E435EE7266AD3EDE1EEFC2CEBE2BE1D614008D5D2951", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("0CCE175124D3586BA7486F7146894C65C2A4A5A1904658E5C7F9DF5FA5D", 16), new BigInteger("08804B456D847ACE5CA86D97BF79FD6335E5B17F6C0D964B5D0036C867E", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("035C3D6DFEEA1CFB29B93BE3FDB91A7B130951770C2690C16833A159677", 16), new BigInteger("0600F7301D12AB376B56D4459774159ADB51F97E282FF384406AFD53A02", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("061602FC8068BFD5FB86027B97455D200EC603057446CCE4D76DB8EF42C", 16), new BigInteger("03396DD0D59C067BB999B422D9883736CF9311DFD6951F91033BD03CA8D", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("07E12CB60FDD614958E8E34B3C12DDFF35D85A9C5800E31EA2CC2EF63B1", 16), new BigInteger("0E8970FD99D836F3CC1C807A2C58760DE6EDAA23705A82B9CB1CE93FECC", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-283");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("14510D4BC44F2D26F4553942C98073C1BD35545CEABB5CC138853C5158D2729EA408836", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("201E18D48C6DB3D5D097C4DCE1E25587E1501FC3CF47BDB5B4289D79E273D6A9" +
+ "ACB8285", 16), new BigInteger("151AE05712B024CE617358260774C8CA8B0E7A7E72EF8229BF2ACE7609560CB3" +
+ "0322C4F", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("143E878DDFD4DF40D97B8CD638B3C4706501C2201CF7108F2FB91478C11D6947" +
+ "3246925", 16), new BigInteger("0CBF1B9717FEEA3AABB09D9654110144267098E0E1E8D0289A6211BE0EEDFDD8" +
+ "6A3DB79", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("29FD82497FB3E5CEF65579272138DE59E2B666B8689466572B3B69A172CEE83B" +
+ "E145659", 16), new BigInteger("05A89D9166B40795AF0FE5958201B9C0523E500013CA12B4840EA2BC53F25F9B" +
+ "3CE87C0", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("2F00689C1BFCD2A8C7A41E0DE55AE182E6463A152828EF89FE3525139B660329" +
+ "4E69353", 16), new BigInteger("1744514FE0A37447250C8A329EAAADA81572226CABA16F39270EE5DD03F27B1F" +
+ "665EB5D", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("0DA43A9ADFAA6AD767998A054C6A8F1CF77A562924628D73C62761847AD8286E" +
+ "0D91B47", 16), new BigInteger("1D118733AE2C88357827CAFC6F68ABC25C80C640532925E95CFE66D40F8792F3" +
+ "AC44C42", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("05A408133919F2CDCDBE5E4C14FBC706C1F71BADAFEF41F5DE4EC27272FC1CA9" +
+ "366FBB2", 16), new BigInteger("012966272872C097FEA7BCE64FAB1A81982A773E26F6E4EF7C99969846E67CA9" +
+ "CBE1692", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("08F3824E40C16FF1DDA8DC992776D26F4A5981AB5092956C4FDBB4F1AE0A711E" +
+ "EAA10E5", 16), new BigInteger("0A64B91EFADB213E11483FB61C73E3EF63D3B44EEFC56EA401B99DCC60CC28E9" +
+ "9F0F1FA", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("3597B406F5329D11A79E887847E5EC60861CCBB19EC61F252DB7BD549C699951" +
+ "C182796", 16), new BigInteger("0A6A100B997BC622D91701D9F5C6F6D3815517E577622DA69D3A0E8917C1CBE6" +
+ "3ACD345", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("1BB490926E5A1FDC7C5AA86D0835F9B994EDA315CA408002AF54A298728D422E" +
+ "BF59E4C", 16), new BigInteger("36C682CFC9E2C89A782BFD3A191609D1F0C1910D5FD6981442070393159D65FB" +
+ "CC0A8BA", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("19944AA68F9778C2E3D6E240947613E6DA60EFCE9B9B2C063FF5466D72745B5A" +
+ "0B25BA2", 16), new BigInteger("03F1567B3C5B02DF15C874F0EE22850824693D5ADC4663BAA19E384E550B1DD4" +
+ "1F31EE6", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-409");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("0494994CC325B08E7B4CE038BD9436F90B5E59A2C13C3140CD3AE07C04A01FC489F572CE0569A6DB7B8060393DE76330C624177", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("0D8783188E1A540E2022D389E1D35B32F56F8C2BB5636B8ABF7718806B27A713" +
+ "EBAE37F63ECD4B61445CEF5801B62594EF3E982", 16), new BigInteger("03A6B4A80E204DB0DE12E7415C13C9EC091C52935658316B4A0C591216A38791" +
+ "54BEB1712560E346E7EF26517707435B55C3141", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("0EE4F39ACC2E03CE96C3D9FCBAFA5C22C89053662F8D4117752A9B10F09ADFDA" +
+ "59DB061E247FE5321D6B170EE758ACE1BE4D157", 16), new BigInteger("00A2B83265B456A430A8BF27DCC8A9488B3F126C10F0D6D64BF7B8A218FAAF20" +
+ "E51A295A3AE78F205E5A4A6AE224C3639F1BB34", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("02D8B1B31E33E74D7EB46C30FDE5AD2CA04EC8FE08FBA0E73BA5E568953AC5EA" +
+ "307C072942238DFC07F4A4D7C7C6A9F86436D17", 16), new BigInteger("079F7D471E6CB73234AF7F7C381D2CE15DE35BAF8BB68393B73235B3A26EC2DF" +
+ "4842CE433FB492D6E074E604D4870024D42189A", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("07BC638B7E7CE6FEE5E9C64A0F966D722D01BB4BC3F3A35F30D4CDDA92DFC5F7" +
+ "F0B4BBFE8065D9AD452FD77A1914BE3A2440C18", 16), new BigInteger("06D904429850521B28A32CBF55C7C0FDF35DC4E0BDA2552C7BF68A171E970E67" +
+ "88ACC0B9521EACB4796E057C70DD9B95FED5BFB", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("05D178DECAFD2D02A3DA0D8BA1C4C1D95EE083C760DF782193A9F7B4A8BE6FC5" +
+ "C21FD60613BCA65C063A61226E050A680B3ABD4", 16), new BigInteger("013B7581E98F6A63FBBCB3E49BCDA60F816DB230B888506D105DC229600497C3" +
+ "B46588C784BE3AA9343BEF82F7C9C80AEB63C3B", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("049F54E7C10D2732B4638473053782C6919218BBEFCEC8B51640FC193E832291" +
+ "F05FA12371E9B448417B3290193F08EE9319195", 16), new BigInteger("0499E267DEC84E02F6F108B10E82172C414F15B1B7364BE8BFD66ADC0C5DE23F" +
+ "EE3DF0D811134C25AFE0E05A6672F98889F28F1", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("0B1527FFAA7DD7C7E46B628587A5BEC0539A2D04D3CF27C54841C2544E1BBDB4" +
+ "2FDBDAAF8671A4CA86DFD619B1E3732D7BB56F2", 16), new BigInteger("0442C68C044868DF4832C807F1EDDEBF7F5052A64B826FD03451440794063F52" +
+ "B022DF304F47403D4069234CA9EB4C964B37C02", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("0BB27755B991D6D31757BCBF68CB01225A38E1CFA20F775E861055DD108ED7EA" +
+ "455E4B96B2F6F7CD6C6EC2B3C70C3EDDEB9743B", 16), new BigInteger("0C5BE90980E7F444B5F7A12C9E9AC7A04CA81412822DD5AD1BE7C45D5032555E" +
+ "A070864245CF69266871FEB8CD1B7EDC30EF6D5", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("04EFEB7098772187907C87B33E0FBBA4584226C50C11E98CA7AAC6986F8D3BE0" +
+ "44E5B52D201A410B852536527724CA5F8CE6549", 16), new BigInteger("09574102FEB3EF87E6D66B94119F5A6062950FF4F902EA1E6BD9E2037F33FF99" +
+ "1E31F5956C23AFE48FCDC557FD6F088C7C9B2B3", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("07E0249C68536AE2AEC2EC30090340DA49E6DC9E9EEC8F85E5AABFB234B6DA7D" +
+ "2E9524028CF821F21C6019770474CC40B01FAF6", 16), new BigInteger("08125B5A03FB44AE81EA46D446130C2A415ECCA265910CA69D55F2453E16CD7B" +
+ "2DFA4E28C50FA8137F9C0C6CEE4CD37ABCCF6D8", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-571");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("028A04857F24C1C082DF0D909C0E72F453F2E2340CCB071F0E389BCA2575DA19" +
+ "124198C57174929AD26E348CF63F78D28021EF5A9BF2D5CBEAF6B7CCB6C4DA82" +
+ "4DD5C82CFB24E11", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("147D3EB0EDA9F2152DFD014363D6A9CE816D7A1467D326A625FC4AB0C786E1B7" +
+ "4DDF7CD4D0E99541391B266C704BB6B6E8DCCD27B460802E0867143727AA4155" +
+ "55454321EFE5CB6", 16),
+ new BigInteger("17319571CAF533D90D2E78A64060B9C53169AB7FC908947B3EDADC54C79CCF0A" +
+ "7920B4C64A4EAB6282AFE9A459677CDA37FD6DD50BEF18709590FE18B923BDF7" +
+ "4A66B189A850819", 16));
+
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("10F4B63E79B2E54E4F4F6A2DBC786D8F4A143ECA7B2AD97810F6472AC6AE2085" +
+ "3222854553BE1D44A7974599DB7061AE8560DF57F2675BE5F9DD94ABAF3D47F1" +
+ "582B318E459748B", 16),
+ new BigInteger("3BBEA07C6B269C2B7FE9AE4DDB118338D0C2F0022920A7F9DCFCB7489594C03B" +
+ "536A9900C4EA6A10410007222D3DAE1A96F291C4C9275D75D98EB290DC0EEF17" +
+ "6037B2C7A7A39A3", 16));
+
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("213EF9F3B0CFC4BF996B8AF3A7E1F6CACD2B87C8C63820000800AC787F17EC99" +
+ "C04BCEDF29A8413CFF83142BB88A50EF8D9A086AF4EB03E97C567500C21D8657" +
+ "14D832E03C6D054", 16),
+ new BigInteger("3D32322559B094E20D8935E250B6EC139AC4AAB77920812C119AF419FB62B332" +
+ "C8D226C6C9362AE3C1E4AABE19359B8428EA74EC8FBE83C8618C2BCCB6B43FBA" +
+ "A0F2CCB7D303945", 16));
+
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("375D8F49C656A0BBD21D3F54CDA287D853C4BB1849983CD891EF6CD6BB56A62B" +
+ "687807C16685C2C9BCA2663C33696ACCE344C45F3910B1DF806204FF731ECB28" +
+ "9C100EF4D1805EC", 16),
+ new BigInteger("1CDEC6F46DFEEE44BCE71D41C60550DC67CF98D6C91363625AC2553E4368D2DF" +
+ "B734A8E8C72E118A76ACDB0E58697940A0F3DF49E72894BD799450FC9E550CC0" +
+ "4B9FF9B0380021C", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("1C26F40D940A7EAA0EB1E62991028057D91FEDA0366B606F6C434C361F04E545" +
+ "A6A51A435E26416F6838FFA260C617E798E946B57215284182BE55F29A355E60" +
+ "24FE32A47289CF0", 16),
+ new BigInteger("3691DE4369D921FE94EDDA67CB71FBBEC9A436787478063EB1CC778B3DCDC1C4" +
+ "162662752D28DEEDF6F32A269C82D1DB80C87CE4D3B662E03AC347806E3F19D1" +
+ "8D6D4DE7358DF7E", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("133F5414F2A9BC41466D339B79376038A64D045E5B0F792A98E5A7AA87E0AD01" +
+ "6419E5F8D176007D5C9C10B5FD9E2E0AB8331B195797C0358BA05ECBF24ACE59" +
+ "C5F368A6C0997CC", 16),
+ new BigInteger("3D16743AE9F00F0B1A500F738719C5582550FEB64689DA241665C4CE4F328BA0" +
+ "E34A7EF527ED13BFA5889FD2D1D214C11EB17D6BC338E05A56F41CAFF1AF7B8D" +
+ "574DB62EF0D0F21", 16));
+
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("3048E76506C5C43D92B2E33F62B33E3111CEEB87F6C7DF7C7C01E3CDA28FA5E8" +
+ "BE04B5B23AA03C0C70FEF8F723CBCEBFF0B7A52A3F5C8B84B741B4F6157E69A5" +
+ "FB0524B48F31828", 16),
+ new BigInteger("2C99078CCFE5C82102B8D006E3703E020C46C87C75163A2CD839C885550BA5CB" +
+ "501AC282D29A1C26D26773B60FBE05AAB62BFA0BA32127563D42F7669C97784C" +
+ "8897C22CFB4B8FA", 16));
+
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("184BC808506E11A65D628B457FDA60952803C604CC7181B59BD25AEE1411A66D" +
+ "12A777F3A0DC99E1190C58D0037807A95E5080FA1B2E5CCAA37B50D401CFFC34" +
+ "17C005AEE963469", 16),
+ new BigInteger("27280D45F81B19334DBDB07B7E63FE8F39AC7E9AE14DE1D2A6884D2101850289" +
+ "D70EE400F26ACA5E7D73F534A14568478E59D00594981ABE6A1BA18554C13EB5" +
+ "E03921E4DC98333", 16));
+
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("319EE57912E7B0FAA1FBB145B0505849A89C6DB1EC06EA20A6A7EDE072A6268A" +
+ "F6FD9C809C7E422A5F33C6C3326EAD7402467DF3272A1B2726C1C20975950F0F" +
+ "50D8324578F13EC", 16),
+ new BigInteger("2CF3EA27EADD0612DD2F96F46E89AB894B01A10DF985C5FC099CFFE0EA083EB4" +
+ "4BE682B08BFE405DAD5F37D0A2C59015BA41027E24B99F8F75A70B6B7385BF39" +
+ "BBEA02513EB880C", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("2AA1888EAB05F7B00B6A784C4F7081D2C833D50794D9FEAF6E22B8BE728A2A90" +
+ "BFCABDC803162020AA629718295A1489EE7ED0ECB8AAA197B9BDFC49D18DDD78" +
+ "FC85A48F9715544", 16),
+ new BigInteger("0AA5371FE5CA671D6ED9665849C37F394FED85D51FEF72DA2B5F28EDFB2C6479" +
+ "CA63320C19596F5E1101988E2C619E302DD05112F47E8823040CE540CD3E90DC" +
+ "F41DBC461744EE9", 16));
+
+ }
+
+ private void doTestHMACDetECDSASample(Digest digest, ECPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new ECDSASigner(new HMacDSAKCalculator(digest)), digest, SAMPLE, privKey, r, s);
+ }
+
+ private void doTestHMACDetECDSATest(Digest digest, ECPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new ECDSASigner(new HMacDSAKCalculator(digest)), digest, TEST, privKey, r, s);
+ }
+
+ private void doTestHMACDetECDSA(DSA detSigner, Digest digest, byte[] data, CipherParameters privKey, BigInteger r, BigInteger s)
+ {
+ byte[] m = new byte[digest.getDigestSize()];
+
+ digest.update(data, 0, data.length);
+
+ digest.doFinal(m, 0);
+
+ detSigner.init(true, privKey);
+
+ BigInteger[] rs = detSigner.generateSignature(m);
+
+ if (!r.equals(rs[0]))
+ {
+ fail("r value wrong");
+ }
+ if (!s.equals(rs[1]))
+ {
+ fail("s value wrong");
+ }
+ }
+
+ public String getName()
+ {
+ return "DeterministicDSA";
+ }
+
+ public void performTest()
+ {
+ testHMacDeterministic();
+ testECHMacDeterministic();
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DeterministicDSATest());
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DigestRandomNumberTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DigestRandomNumberTest.java
new file mode 100644
index 000000000..b79eadd3e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DigestRandomNumberTest.java
@@ -0,0 +1,152 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.util.test.SimpleTest;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.Digest;
+
+public class DigestRandomNumberTest
+ extends SimpleTest
+{
+ private static final byte[] ZERO_SEED = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ private static final byte[] TEST_SEED = Hex.decode("81dcfafc885914057876");
+
+ private static final byte[] expected0SHA1 = Hex.decode("95bca677b3d4ff793213c00892d2356ec729ee02");
+ private static final byte[] noCycle0SHA1 = Hex.decode("d57ccd0eb12c3938d59226412bc1268037b6b846");
+ private static final byte[] expected0SHA256 = Hex.decode("587e2dfd597d086e47ddcd343eac983a5c913bef8c6a1a560a5c1bc3a74b0991");
+ private static final byte[] noCycle0SHA256 = Hex.decode("e5776c4483486ba7be081f4e1b9dafbab25c8fae290fd5474c1ceda2c16f9509");
+ private static final byte[] expected100SHA1 = Hex.decode("b9d924092546e0876cafd4937d7364ebf9efa4be");
+ private static final byte[] expected100SHA256 = Hex.decode("fbc4aa54b948b99de104c44563a552899d718bb75d1941cc62a2444b0506abaf");
+ private static final byte[] expectedTestSHA1 = Hex.decode("e9ecef9f5306daf1ac51a89a211a64cb24415649");
+ private static final byte[] expectedTestSHA256 = Hex.decode("bdab3ca831b472a2fa09bd1bade541ef16c96640a91fcec553679a136061de98");
+
+ private static final byte[] sha1Xors = Hex.decode("7edcc1216934f3891b03ffa65821611a3e2b1f79");
+ private static final byte[] sha256Xors = Hex.decode("5ec48189cc0aa71e79c707bc3c33ffd47bbba368a83d6cfebf3cd3969d7f3eed");
+
+ public String getName()
+ {
+ return "DigestRandomNumber";
+ }
+
+ private void doExpectedTest(Digest digest, int seed, byte[] expected)
+ {
+ doExpectedTest(digest, seed, expected, null);
+ }
+
+ private void doExpectedTest(Digest digest, int seed, byte[] expected, byte[] noCycle)
+ {
+ DigestRandomGenerator rGen = new DigestRandomGenerator(digest);
+ byte[] output = new byte[digest.getDigestSize()];
+
+ rGen.addSeedMaterial(seed);
+
+ for (int i = 0; i != 1024; i++)
+ {
+ rGen.nextBytes(output);
+ }
+
+ if (noCycle != null)
+ {
+ if (Arrays.areEqual(noCycle, output))
+ {
+ fail("seed not being cycled!");
+ }
+ }
+
+ if (!Arrays.areEqual(expected, output))
+ {
+ fail("expected output doesn't match");
+ }
+ }
+
+ private void doExpectedTest(Digest digest, byte[] seed, byte[] expected)
+ {
+ DigestRandomGenerator rGen = new DigestRandomGenerator(digest);
+ byte[] output = new byte[digest.getDigestSize()];
+
+ rGen.addSeedMaterial(seed);
+
+ for (int i = 0; i != 1024; i++)
+ {
+ rGen.nextBytes(output);
+ }
+
+ if (!Arrays.areEqual(expected, output))
+ {
+ fail("expected output doesn't match");
+ }
+ }
+
+ private void doCountTest(Digest digest, byte[] seed, byte[] expectedXors)
+ {
+ DigestRandomGenerator rGen = new DigestRandomGenerator(digest);
+ byte[] output = new byte[digest.getDigestSize()];
+ int[] averages = new int[digest.getDigestSize()];
+ byte[] ands = new byte[digest.getDigestSize()];
+ byte[] xors = new byte[digest.getDigestSize()];
+ byte[] ors = new byte[digest.getDigestSize()];
+
+ rGen.addSeedMaterial(seed);
+
+ for (int i = 0; i != 1000000; i++)
+ {
+ rGen.nextBytes(output);
+ for (int j = 0; j != output.length; j++)
+ {
+ averages[j] += output[j] & 0xff;
+ ands[j] &= output[j];
+ xors[j] ^= output[j];
+ ors[j] |= output[j];
+ }
+ }
+
+ for (int i = 0; i != output.length; i++)
+ {
+ if ((averages[i] / 1000000) != 127)
+ {
+ fail("average test failed for " + digest.getAlgorithmName());
+ }
+ if (ands[i] != 0)
+ {
+ fail("and test failed for " + digest.getAlgorithmName());
+ }
+ if ((ors[i] & 0xff) != 0xff)
+ {
+ fail("or test failed for " + digest.getAlgorithmName());
+ }
+ if (xors[i] != expectedXors[i])
+ {
+ fail("xor test failed for " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ doExpectedTest(new SHA1Digest(), 0, expected0SHA1, noCycle0SHA1);
+ doExpectedTest(new SHA256Digest(), 0, expected0SHA256, noCycle0SHA256);
+
+ doExpectedTest(new SHA1Digest(), 100, expected100SHA1);
+ doExpectedTest(new SHA256Digest(), 100, expected100SHA256);
+
+ doExpectedTest(new SHA1Digest(), ZERO_SEED, expected0SHA1);
+ doExpectedTest(new SHA256Digest(), ZERO_SEED, expected0SHA256);
+
+ doExpectedTest(new SHA1Digest(), TEST_SEED, expectedTestSHA1);
+ doExpectedTest(new SHA256Digest(), TEST_SEED, expectedTestSHA256);
+
+ doCountTest(new SHA1Digest(), TEST_SEED, sha1Xors);
+ doCountTest(new SHA256Digest(), TEST_SEED, sha256Xors);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DigestRandomNumberTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DigestTest.java
new file mode 100644
index 000000000..9b9849e26
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/DigestTest.java
@@ -0,0 +1,178 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.util.Memoable;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public abstract class DigestTest
+ extends SimpleTest
+{
+ private Digest digest;
+ private String[] input;
+ private String[] results;
+
+ DigestTest(
+ Digest digest,
+ String[] input,
+ String[] results)
+ {
+ this.digest = digest;
+ this.input = input;
+ this.results = results;
+ }
+
+ public String getName()
+ {
+ return digest.getAlgorithmName();
+ }
+
+ public void performTest()
+ {
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i < input.length - 1; i++)
+ {
+ byte[] m = toByteArray(input[i]);
+
+ vectorTest(digest, i, resBuf, m, Hex.decode(results[i]));
+ }
+
+ byte[] lastV = toByteArray(input[input.length - 1]);
+ byte[] lastDigest = Hex.decode(results[input.length - 1]);
+
+ vectorTest(digest, input.length - 1, resBuf, lastV, Hex.decode(results[input.length - 1]));
+
+ //
+ // clone test
+ //
+ digest.update(lastV, 0, lastV.length/2);
+
+ // clone the Digest
+ Digest d = cloneDigest(digest);
+
+ digest.update(lastV, lastV.length/2, lastV.length - lastV.length/2);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(lastDigest, resBuf))
+ {
+ fail("failing clone vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ d.update(lastV, lastV.length/2, lastV.length - lastV.length/2);
+ d.doFinal(resBuf, 0);
+
+ if (!areEqual(lastDigest, resBuf))
+ {
+ fail("failing second clone vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ //
+ // memo test
+ //
+ Memoable m = (Memoable)digest;
+
+ digest.update(lastV, 0, lastV.length/2);
+
+ // copy the Digest
+ Memoable copy1 = m.copy();
+ Memoable copy2 = copy1.copy();
+
+ digest.update(lastV, lastV.length/2, lastV.length - lastV.length/2);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(lastDigest, resBuf))
+ {
+ fail("failing memo vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ m.reset(copy1);
+
+ digest.update(lastV, lastV.length/2, lastV.length - lastV.length/2);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(lastDigest, resBuf))
+ {
+ fail("failing memo reset vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ Digest md = (Digest)copy2;
+
+ md.update(lastV, lastV.length/2, lastV.length - lastV.length/2);
+ md.doFinal(resBuf, 0);
+
+ if (!areEqual(lastDigest, resBuf))
+ {
+ fail("failing memo copy vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+ }
+
+ private byte[] toByteArray(String input)
+ {
+ byte[] bytes = new byte[input.length()];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ bytes[i] = (byte)input.charAt(i);
+ }
+
+ return bytes;
+ }
+
+ private void vectorTest(
+ Digest digest,
+ int count,
+ byte[] resBuf,
+ byte[] input,
+ byte[] expected)
+ {
+ digest.update(input, 0, input.length);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, expected))
+ {
+ fail("Vector " + count + " failed got " + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ protected abstract Digest cloneDigest(Digest digest);
+
+ //
+ // optional tests
+ //
+ protected void millionATest(
+ String expected)
+ {
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i < 1000000; i++)
+ {
+ digest.update((byte)'a');
+ }
+
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(expected)))
+ {
+ fail("Million a's failed", expected, new String(Hex.encode(resBuf)));
+ }
+ }
+
+ protected void sixtyFourKTest(
+ String expected)
+ {
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i < 65536; i++)
+ {
+ digest.update((byte)(i & 0xff));
+ }
+
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(expected)))
+ {
+ fail("64k test failed", expected, new String(Hex.encode(resBuf)));
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/EAXTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/EAXTest.java
new file mode 100644
index 000000000..d6284990d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/EAXTest.java
@@ -0,0 +1,351 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+import org.spongycastle.crypto.modes.EAXBlockCipher;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class EAXTest
+ extends SimpleTest
+{
+ private byte[] K1 = Hex.decode("233952DEE4D5ED5F9B9C6D6FF80FF478");
+ private byte[] N1 = Hex.decode("62EC67F9C3A4A407FCB2A8C49031A8B3");
+ private byte[] A1 = Hex.decode("6BFB914FD07EAE6B");
+ private byte[] P1 = Hex.decode("");
+ private byte[] C1 = Hex.decode("E037830E8389F27B025A2D6527E79D01");
+ private byte[] T1 = Hex.decode("E037830E8389F27B025A2D6527E79D01");
+
+ private byte[] K2 = Hex.decode("91945D3F4DCBEE0BF45EF52255F095A4");
+ private byte[] N2 = Hex.decode("BECAF043B0A23D843194BA972C66DEBD");
+ private byte[] A2 = Hex.decode("FA3BFD4806EB53FA");
+ private byte[] P2 = Hex.decode("F7FB");
+ private byte[] C2 = Hex.decode("19DD5C4C9331049D0BDAB0277408F67967E5");
+ private byte[] T2 = Hex.decode("5C4C9331049D0BDAB0277408F67967E5");
+
+ private byte[] K3 = Hex.decode("01F74AD64077F2E704C0F60ADA3DD523");
+ private byte[] N3 = Hex.decode("70C3DB4F0D26368400A10ED05D2BFF5E");
+ private byte[] A3 = Hex.decode("234A3463C1264AC6");
+ private byte[] P3 = Hex.decode("1A47CB4933");
+ private byte[] C3 = Hex.decode("D851D5BAE03A59F238A23E39199DC9266626C40F80");
+ private byte[] T3 = Hex.decode("3A59F238A23E39199DC9266626C40F80");
+
+ private byte[] K4 = Hex.decode("D07CF6CBB7F313BDDE66B727AFD3C5E8");
+ private byte[] N4 = Hex.decode("8408DFFF3C1A2B1292DC199E46B7D617");
+ private byte[] A4 = Hex.decode("33CCE2EABFF5A79D");
+ private byte[] P4 = Hex.decode("481C9E39B1");
+ private byte[] C4 = Hex.decode("632A9D131AD4C168A4225D8E1FF755939974A7BEDE");
+ private byte[] T4 = Hex.decode("D4C168A4225D8E1FF755939974A7BEDE");
+
+ private byte[] K5 = Hex.decode("35B6D0580005BBC12B0587124557D2C2");
+ private byte[] N5 = Hex.decode("FDB6B06676EEDC5C61D74276E1F8E816");
+ private byte[] A5 = Hex.decode("AEB96EAEBE2970E9");
+ private byte[] P5 = Hex.decode("40D0C07DA5E4");
+ private byte[] C5 = Hex.decode("071DFE16C675CB0677E536F73AFE6A14B74EE49844DD");
+ private byte[] T5 = Hex.decode("CB0677E536F73AFE6A14B74EE49844DD");
+
+ private byte[] K6 = Hex.decode("BD8E6E11475E60B268784C38C62FEB22");
+ private byte[] N6 = Hex.decode("6EAC5C93072D8E8513F750935E46DA1B");
+ private byte[] A6 = Hex.decode("D4482D1CA78DCE0F");
+ private byte[] P6 = Hex.decode("4DE3B35C3FC039245BD1FB7D");
+ private byte[] C6 = Hex.decode("835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F");
+ private byte[] T6 = Hex.decode("ABB8644FD6CCB86947C5E10590210A4F");
+
+ private byte[] K7 = Hex.decode("7C77D6E813BED5AC98BAA417477A2E7D");
+ private byte[] N7 = Hex.decode("1A8C98DCD73D38393B2BF1569DEEFC19");
+ private byte[] A7 = Hex.decode("65D2017990D62528");
+ private byte[] P7 = Hex.decode("8B0A79306C9CE7ED99DAE4F87F8DD61636");
+ private byte[] C7 = Hex.decode("02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2");
+ private byte[] T7 = Hex.decode("137327D10649B0AA6E1C181DB617D7F2");
+
+ private byte[] K8 = Hex.decode("5FFF20CAFAB119CA2FC73549E20F5B0D");
+ private byte[] N8 = Hex.decode("DDE59B97D722156D4D9AFF2BC7559826");
+ private byte[] A8 = Hex.decode("54B9F04E6A09189A");
+ private byte[] P8 = Hex.decode("1BDA122BCE8A8DBAF1877D962B8592DD2D56");
+ private byte[] C8 = Hex.decode("2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A");
+ private byte[] T8 = Hex.decode("3B60450599BD02C96382902AEF7F832A");
+
+ private byte[] K9 = Hex.decode("A4A4782BCFFD3EC5E7EF6D8C34A56123");
+ private byte[] N9 = Hex.decode("B781FCF2F75FA5A8DE97A9CA48E522EC");
+ private byte[] A9 = Hex.decode("899A175897561D7E");
+ private byte[] P9 = Hex.decode("6CF36720872B8513F6EAB1A8A44438D5EF11");
+ private byte[] C9 = Hex.decode("0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700");
+ private byte[] T9 = Hex.decode("E7F6D2231618102FDB7FE55FF1991700");
+
+ private byte[] K10 = Hex.decode("8395FCF1E95BEBD697BD010BC766AAC3");
+ private byte[] N10 = Hex.decode("22E7ADD93CFC6393C57EC0B3C17D6B44");
+ private byte[] A10 = Hex.decode("126735FCC320D25A");
+ private byte[] P10 = Hex.decode("CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7");
+ private byte[] C10 = Hex.decode("CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E");
+ private byte[] T10 = Hex.decode("CFC46AFC253B4652B1AF3795B124AB6E");
+
+ private byte[] K11 = Hex.decode("8395FCF1E95BEBD697BD010BC766AAC3");
+ private byte[] N11 = Hex.decode("22E7ADD93CFC6393C57EC0B3C17D6B44");
+ private byte[] A11 = Hex.decode("126735FCC320D25A");
+ private byte[] P11 = Hex.decode("CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7");
+ private byte[] C11 = Hex.decode("CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC");
+ private byte[] T11 = Hex.decode("CFC46AFC");
+
+ private static final int NONCE_LEN = 8;
+ private static final int MAC_LEN = 8;
+ private static final int AUTHEN_LEN = 20;
+
+ public String getName()
+ {
+ return "EAX";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ checkVectors(1, K1, 128, N1, A1, P1, T1, C1);
+ checkVectors(2, K2, 128, N2, A2, P2, T2, C2);
+ checkVectors(3, K3, 128, N3, A3, P3, T3, C3);
+ checkVectors(4, K4, 128, N4, A4, P4, T4, C4);
+ checkVectors(5, K5, 128, N5, A5, P5, T5, C5);
+ checkVectors(6, K6, 128, N6, A6, P6, T6, C6);
+ checkVectors(7, K7, 128, N7, A7, P7, T7, C7);
+ checkVectors(8, K8, 128, N8, A8, P8, T8, C8);
+ checkVectors(9, K9, 128, N9, A9, P9, T9, C9);
+ checkVectors(10, K10, 128, N10, A10, P10, T10, C10);
+ checkVectors(11, K11, 32, N11, A11, P11, T11, C11);
+
+ EAXBlockCipher eax = new EAXBlockCipher(new AESFastEngine());
+ ivParamTest(1, eax, K1, N1);
+
+ //
+ // exception tests
+ //
+
+ try
+ {
+ eax.init(false, new AEADParameters(new KeyParameter(K1), 32, N2, A2));
+
+ byte[] enc = new byte[C2.length];
+ int len = eax.processBytes(C2, 0, C2.length, enc, 0);
+
+ len += eax.doFinal(enc, len);
+
+ fail("invalid cipher text not picked up");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ eax.init(false, new KeyParameter(K1));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ randomTests();
+ AEADTestUtil.testReset(this, new EAXBlockCipher(new AESEngine()), new EAXBlockCipher(new AESEngine()), new AEADParameters(new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testTampering(this, eax, new AEADParameters(new KeyParameter(K1), 32, N2));
+ }
+
+ private void checkVectors(
+ int count,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ byte[] fa = new byte[a.length / 2];
+ byte[] la = new byte[a.length - (a.length / 2)];
+ System.arraycopy(a, 0, fa, 0, fa.length);
+ System.arraycopy(a, fa.length, la, 0, la.length);
+
+ checkVectors(count, "all initial associated data", k, macSize, n, a, null, p, t, c);
+ checkVectors(count, "subsequent associated data", k, macSize, n, null, a, p, t, c);
+ checkVectors(count, "split associated data", k, macSize, n, fa, la, p, t, c);
+ }
+
+ private void checkVectors(
+ int count,
+ String additionalDataType,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] sa,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ EAXBlockCipher encEax = new EAXBlockCipher(new AESFastEngine());
+ EAXBlockCipher decEax = new EAXBlockCipher(new AESFastEngine());
+
+ AEADParameters parameters = new AEADParameters(new KeyParameter(k), macSize, n, a);
+ encEax.init(true, parameters);
+ decEax.init(false, parameters);
+
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+
+ // key reuse test
+ parameters = new AEADParameters(null, macSize, n, a);
+ encEax.init(true, parameters);
+ decEax.init(false, parameters);
+
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+ }
+
+ private void runCheckVectors(
+ int count,
+ EAXBlockCipher encEax,
+ EAXBlockCipher decEax,
+ String additionalDataType,
+ byte[] sa,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ byte[] enc = new byte[c.length];
+
+ if (sa != null)
+ {
+ encEax.processAADBytes(sa, 0, sa.length);
+ }
+
+ int len = encEax.processBytes(p, 0, p.length, enc, 0);
+
+ len += encEax.doFinal(enc, len);
+
+ if (!areEqual(c, enc))
+ {
+ fail("encrypted stream fails to match in test " + count + " with " + additionalDataType);
+ }
+
+ byte[] tmp = new byte[enc.length];
+
+ if (sa != null)
+ {
+ decEax.processAADBytes(sa, 0, sa.length);
+ }
+
+ len = decEax.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += decEax.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count + " with " + additionalDataType);
+ }
+
+ if (!areEqual(t, decEax.getMac()))
+ {
+ fail("MAC fails to match in test " + count + " with " + additionalDataType);
+ }
+ }
+
+ private void ivParamTest(
+ int count,
+ AEADBlockCipher eax,
+ byte[] k,
+ byte[] n)
+ throws InvalidCipherTextException
+ {
+ byte[] p = Strings.toByteArray("hello world!!");
+
+ eax.init(true, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] enc = new byte[p.length + 8];
+
+ int len = eax.processBytes(p, 0, p.length, enc, 0);
+
+ len += eax.doFinal(enc, len);
+
+ eax.init(false, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] tmp = new byte[enc.length];
+
+ len = eax.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += eax.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count);
+ }
+ }
+
+ private void randomTests()
+ throws InvalidCipherTextException
+ {
+ SecureRandom srng = new SecureRandom();
+ for (int i = 0; i < 10; ++i)
+ {
+ randomTest(srng);
+ }
+ }
+
+ private void randomTest(
+ SecureRandom srng)
+ throws InvalidCipherTextException
+ {
+ int DAT_LEN = srng.nextInt() >>> 22; // Note: JDK1.0 compatibility
+ byte[] nonce = new byte[NONCE_LEN];
+ byte[] authen = new byte[AUTHEN_LEN];
+ byte[] datIn = new byte[DAT_LEN];
+ byte[] key = new byte[16];
+ srng.nextBytes(nonce);
+ srng.nextBytes(authen);
+ srng.nextBytes(datIn);
+ srng.nextBytes(key);
+
+ AESFastEngine engine = new AESFastEngine();
+ KeyParameter sessKey = new KeyParameter(key);
+ EAXBlockCipher eaxCipher = new EAXBlockCipher(engine);
+
+ AEADParameters params = new AEADParameters(sessKey, MAC_LEN * 8, nonce, authen);
+ eaxCipher.init(true, params);
+
+ byte[] intrDat = new byte[eaxCipher.getOutputSize(datIn.length)];
+ int outOff = eaxCipher.processBytes(datIn, 0, DAT_LEN, intrDat, 0);
+ outOff += eaxCipher.doFinal(intrDat, outOff);
+
+ eaxCipher.init(false, params);
+ byte[] datOut = new byte[eaxCipher.getOutputSize(outOff)];
+ int resultLen = eaxCipher.processBytes(intrDat, 0, outOff, datOut, 0);
+ eaxCipher.doFinal(datOut, resultLen);
+
+ if (!areEqual(datIn, datOut))
+ {
+ fail("EAX roundtrip failed to match");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new EAXTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECDHKEKGeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECDHKEKGeneratorTest.java
new file mode 100644
index 000000000..757599929
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECDHKEKGeneratorTest.java
@@ -0,0 +1,71 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.DerivationParameters;
+import org.spongycastle.crypto.agreement.kdf.DHKDFParameters;
+import org.spongycastle.crypto.agreement.kdf.ECDHKEKGenerator;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ECDHKEK Generator tests.
+ */
+public class ECDHKEKGeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("db4a8daba1f98791d54e940175dd1a5f3a0826a1066aa9b668d4dc1e1e0790158dcad1533c03b44214d1b61fefa8b579");
+ private DERObjectIdentifier alg1 = NISTObjectIdentifiers.id_aes256_wrap;
+ private byte[] result1 = Hex.decode("8ecc6d85caf25eaba823a7d620d4ab0d33e4c645f2");
+
+ private byte[] seed2 = Hex.decode("75d7487b5d3d2bfb3c69ce0365fe64e3bfab5d0d63731628a9f47eb8fddfa28c65decaf228a0b38f0c51c6a3356d7c56");
+ private DERObjectIdentifier alg2 = NISTObjectIdentifiers.id_aes128_wrap;
+ private byte[] result2 = Hex.decode("042be1faca3a4a8fc859241bfb87ba35");
+
+ private byte[] seed3 = Hex.decode("fdeb6d809f997e8ac174d638734dc36d37aaf7e876e39967cd82b1cada3de772449788461ee7f856bad9305627f8e48b");
+ private DERObjectIdentifier alg3 = PKCSObjectIdentifiers.id_alg_CMS3DESwrap;
+ private byte[] result3 = Hex.decode("bcd701fc92109b1b9d6f3b6497ad5ca9627fa8a597010305");
+
+ public ECDHKEKGeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new ECDHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg1, 256, seed1), result1);
+ checkMask(2, new ECDHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg2, 128, seed2), result2);
+ checkMask(3, new ECDHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg3, 192, seed3), result3);
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ DerivationParameters params,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(params);
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("ECDHKEKGenerator failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "ECDHKEKGenerator";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECDHKEKGeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECGOST3410Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECGOST3410Test.java
new file mode 100644
index 000000000..5d08540ff
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECGOST3410Test.java
@@ -0,0 +1,317 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.digests.GOST3411Digest;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.ECGOST3410Signer;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.FixedSecureRandom;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ECGOST3410 tests are taken from GOST R 34.10-2001.
+ */
+public class ECGOST3410Test
+ extends SimpleTest
+ {
+ byte[] hashmessage = Hex.decode("3042453136414534424341374533364339313734453431443642453241453435");
+
+ /**
+ * ECGOST3410 over the field Fp
+ * Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
+ * consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
+ * effective key bits) used in the authenticator.
+ *
+ * This implementation is adapted from the public domain nacl
+ *
+Note: The classes in this package are also a useful source of example code.
+
+
diff --git a/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/crypto/test/package.html b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/crypto/test/package.html
new file mode 100644
index 000000000..7bb5e6b1a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/crypto/test/package.html
@@ -0,0 +1,5 @@
+
+
+Note: The classes in this package are also a useful source of example code.
+
+
diff --git a/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/mozilla/test/package.html b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/mozilla/test/package.html
new file mode 100644
index 000000000..54047adcf
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/mozilla/test/package.html
@@ -0,0 +1,5 @@
+
+
+Note: The classes in this package are also a useful source of example code.
+
+
diff --git a/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/openssl/test/package.html b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/openssl/test/package.html
new file mode 100644
index 000000000..368d7096b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/openssl/test/package.html
@@ -0,0 +1,5 @@
+
+
+ ASN1EncodableVector asnQInv = new ASN1EncodableVector();
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ asnQInv.add(new DEROctetString(encqInv[i]));
+ }
+
+ v.add(new DERSequence(asnQInv));
+
+ return new DERSequence(v);
+ }
+
+ public static McElieceCCA2PrivateKey getInstance(Object o)
+ {
+ if (o instanceof McElieceCCA2PrivateKey)
+ {
+ return (McElieceCCA2PrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new McElieceCCA2PrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java
new file mode 100644
index 000000000..186d8ca4d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java
@@ -0,0 +1,96 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+public class McElieceCCA2PublicKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int t;
+
+ private byte[] matrixG;
+
+ public McElieceCCA2PublicKey(ASN1ObjectIdentifier oid, int n, int t, GF2Matrix g)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = g.getEncoded();
+ }
+
+ private McElieceCCA2PublicKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigT = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ t = bigT.intValue();
+
+ matrixG = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public GF2Matrix getG()
+ {
+ return new GF2Matrix(matrixG);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode
+ ASN1EncodableVector asnQInv = new ASN1EncodableVector();
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ asnQInv.add(new DEROctetString(encqInv[i]));
+ }
+
+ v.add(new DERSequence(asnQInv));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java
new file mode 100644
index 000000000..1415587da
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java
@@ -0,0 +1,97 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+public class McEliecePublicKey
+ extends ASN1Object
+{
+
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int t;
+
+ private byte[] matrixG;
+
+ public McEliecePublicKey(ASN1ObjectIdentifier oid, int n, int t, GF2Matrix g)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = g.getEncoded();
+ }
+
+ private McEliecePublicKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigT = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ t = bigT.intValue();
+
+ matrixG = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public GF2Matrix getG()
+ {
+ return new GF2Matrix(matrixG);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode
+ * ParSet ::= SEQUENCE {
+ * T INTEGER
+ * h SEQUENCE OF INTEGER
+ * w SEQUENCE OF INTEGER
+ * K SEQUENCE OF INTEGER
+ * }
+ *
+ */
+public class ParSet
+ extends ASN1Object
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private int t;
+ private int[] h;
+ private int[] w;
+ private int[] k;
+
+ private static int checkBigIntegerInIntRangeAndPositive(BigInteger b)
+ {
+ if ((b.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) ||
+ (b.compareTo(ZERO) <= 0))
+ {
+ throw new IllegalArgumentException("BigInteger not in Range: " + b.toString());
+ }
+ return b.intValue();
+ }
+
+ private ParSet(ASN1Sequence seq)
+ {
+ if (seq.size() != 4)
+ {
+ throw new IllegalArgumentException("sie of seqOfParams = " + seq.size());
+ }
+ BigInteger asn1int = ((ASN1Integer)seq.getObjectAt(0)).getValue();
+
+ t = checkBigIntegerInIntRangeAndPositive(asn1int);
+
+ ASN1Sequence seqOfPSh = (ASN1Sequence)seq.getObjectAt(1);
+ ASN1Sequence seqOfPSw = (ASN1Sequence)seq.getObjectAt(2);
+ ASN1Sequence seqOfPSK = (ASN1Sequence)seq.getObjectAt(3);
+
+ if ((seqOfPSh.size() != t) ||
+ (seqOfPSw.size() != t) ||
+ (seqOfPSK.size() != t))
+ {
+ throw new IllegalArgumentException("invalid size of sequences");
+ }
+
+ h = new int[seqOfPSh.size()];
+ w = new int[seqOfPSw.size()];
+ k = new int[seqOfPSK.size()];
+
+ for (int i = 0; i < t; i++)
+ {
+ h[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSh.getObjectAt(i))).getValue());
+ w[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSw.getObjectAt(i))).getValue());
+ k[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSK.getObjectAt(i))).getValue());
+ }
+ }
+
+ public ParSet(int t, int[] h, int[] w, int[] k)
+ {
+ this.t = t;
+ this.h = h;
+ this.w = w;
+ this.k = k;
+ }
+
+ public static ParSet getInstance(Object o)
+ {
+ if (o instanceof ParSet)
+ {
+ return (ParSet)o;
+ }
+ else if (o != null)
+ {
+ return new ParSet(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public int[] getH()
+ {
+ return Arrays.clone(h);
+ }
+
+ public int[] getW()
+ {
+ return Arrays.clone(w);
+ }
+
+ public int[] getK()
+ {
+ return Arrays.clone(k);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seqOfPSh = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfPSw = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfPSK = new ASN1EncodableVector();
+
+ for (int i = 0; i < h.length; i++)
+ {
+ seqOfPSh.add(new ASN1Integer(h[i]));
+ seqOfPSw.add(new ASN1Integer(w[i]));
+ seqOfPSK.add(new ASN1Integer(k[i]));
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(t));
+ v.add(new DERSequence(seqOfPSh));
+ v.add(new DERSequence(seqOfPSw));
+ v.add(new DERSequence(seqOfPSK));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java
new file mode 100644
index 000000000..4914e940f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java
@@ -0,0 +1,350 @@
+package org.spongycastle.pqc.asn1;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.crypto.rainbow.Layer;
+import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil;
+
+/**
+ * Return the key data to encode in the PrivateKeyInfo structure.
+ *
+ * The ASN.1 definition of the key structure is
+ *
+ *
+ * RainbowPrivateKey ::= SEQUENCE {
+ * CHOICE
+ * {
+ * oid OBJECT IDENTIFIER -- OID identifying the algorithm
+ * version INTEGER -- 0
+ * }
+ * A1inv SEQUENCE OF OCTET STRING -- inversed matrix of L1
+ * b1 OCTET STRING -- translation vector of L1
+ * A2inv SEQUENCE OF OCTET STRING -- inversed matrix of L2
+ * b2 OCTET STRING -- translation vector of L2
+ * vi OCTET STRING -- num of elmts in each Set S
+ * layers SEQUENCE OF Layer -- layers of F
+ * }
+ *
+ * Layer ::= SEQUENCE OF Poly
+ *
+ * Poly ::= SEQUENCE {
+ * alpha SEQUENCE OF OCTET STRING
+ * beta SEQUENCE OF OCTET STRING
+ * gamma OCTET STRING
+ * eta INTEGER
+ * }
+ *
+ */
+public class RainbowPrivateKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier oid;
+
+ private byte[][] invA1;
+ private byte[] b1;
+ private byte[][] invA2;
+ private byte[] b2;
+ private byte[] vi;
+ private Layer[] layers;
+
+ private RainbowPrivateKey(ASN1Sequence seq)
+ {
+ //
+ * RainbowPublicKey ::= SEQUENCE {
+ * CHOICE
+ * {
+ * oid OBJECT IDENTIFIER -- OID identifying the algorithm
+ * version INTEGER -- 0
+ * }
+ * docLength Integer -- length of the code
+ * coeffquadratic SEQUENCE OF OCTET STRING -- quadratic (mixed) coefficients
+ * coeffsingular SEQUENCE OF OCTET STRING -- singular coefficients
+ * coeffscalar SEQUENCE OF OCTET STRING -- scalar coefficients
+ * }
+ *
+ */
+public class RainbowPublicKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier oid;
+ private ASN1Integer docLength;
+ private byte[][] coeffQuadratic;
+ private byte[][] coeffSingular;
+ private byte[] coeffScalar;
+
+ private RainbowPublicKey(ASN1Sequence seq)
+ {
+ // keySize
as input. It provides a simple use of the GMSS for
+ * testing demands.
+ *
+ * A given keysize
of less than 10 creates an amount 2^10
+ * signatures. A keySize between 10 and 20 creates 2^20 signatures. Given an
+ * integer greater than 20 the key pair generator creates 2^40 signatures.
+ *
+ * @param keySize Assigns the parameters used for the GMSS signatures. There are
+ * 3 choices:
+ * 1. keysize <= 10: creates 2^10 signatures using the
+ * parameterset
+ * P = (2, (5, 5), (3, 3), (3, 3))
+ * 2. keysize > 10 and <= 20: creates 2^20 signatures using the
+ * parameterset
+ * P = (2, (10, 10), (5, 4), (2, 2))
+ * 3. keysize > 20: creates 2^40 signatures using the
+ * parameterset
+ * P = (2, (10, 10, 10, 10), (9, 9, 9, 3), (2, 2, 2, 2))
+ * @param secureRandom not used by GMSS, the SHA1PRNG of the SUN Provider is always
+ * used
+ */
+ public void initialize(int keySize, SecureRandom secureRandom)
+ {
+
+ KeyGenerationParameters kgp;
+ if (keySize <= 10)
+ { // create 2^10 keys
+ int[] defh = {10};
+ int[] defw = {3};
+ int[] defk = {2};
+ // XXX sec random neede?
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+ else if (keySize <= 20)
+ { // create 2^20 keys
+ int[] defh = {10, 10};
+ int[] defw = {5, 4};
+ int[] defk = {2, 2};
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+ else
+ { // create 2^40 keys, keygen lasts around 80 seconds
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {9, 9, 9, 3};
+ int[] defk = {2, 2, 2, 2};
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+
+ // call the initializer with the chosen parameters
+ this.initialize(kgp);
+
+ }
+
+
+ /**
+ * Initalizes the key pair generator using a parameter set as input
+ */
+ public void initialize(KeyGenerationParameters param)
+ {
+
+ this.gmssParams = (GMSSKeyGenerationParameters)param;
+
+ // generate GMSSParameterset
+ this.gmssPS = new GMSSParameters(gmssParams.getParameters().getNumOfLayers(), gmssParams.getParameters().getHeightOfTrees(),
+ gmssParams.getParameters().getWinternitzParameter(), gmssParams.getParameters().getK());
+
+ this.numLayer = gmssPS.getNumOfLayers();
+ this.heightOfTrees = gmssPS.getHeightOfTrees();
+ this.otsIndex = gmssPS.getWinternitzParameter();
+ this.K = gmssPS.getK();
+
+ // seeds
+ this.currentSeeds = new byte[numLayer][mdLength];
+ this.nextNextSeeds = new byte[numLayer - 1][mdLength];
+
+ // construct SecureRandom for initial seed generation
+ SecureRandom secRan = new SecureRandom();
+
+ // generation of initial seeds
+ for (int i = 0; i < numLayer; i++)
+ {
+ secRan.nextBytes(currentSeeds[i]);
+ gmssRandom.nextSeed(currentSeeds[i]);
+ }
+
+ this.initialized = true;
+ }
+
+ /**
+ * This method is called by generateKeyPair() in case that no other
+ * initialization method has been called by the user
+ */
+ private void initializeDefault()
+ {
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {3, 3, 3, 3};
+ int[] defk = {2, 2, 2, 2};
+
+ KeyGenerationParameters kgp = new GMSSKeyGenerationParameters(new SecureRandom(), new GMSSParameters(defh.length, defh, defw, defk));
+ this.initialize(kgp);
+
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java
new file mode 100644
index 000000000..d2dcbba7a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java
@@ -0,0 +1,22 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+public class GMSSKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private GMSSParameters params;
+
+ public GMSSKeyParameters(
+ boolean isPrivate,
+ GMSSParameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+ public GMSSParameters getParameters()
+ {
+ return params;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java
new file mode 100644
index 000000000..ade340ad2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java
@@ -0,0 +1,376 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements the distributed computation of the public key of the
+ * Winternitz one-time signature scheme (OTSS). The class is used by the GMSS
+ * classes for calculation of upcoming leafs.
+ */
+public class GMSSLeaf
+{
+
+ /**
+ * The hash function used by the OTS and the PRNG
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Byte array for distributed computation of the upcoming leaf
+ */
+ private byte[] leaf;
+
+ /**
+ * Byte array for storing the concatenated hashes of private key parts
+ */
+ private byte[] concHashs;
+
+ /**
+ * indices for distributed computation
+ */
+ private int i, j;
+
+ /**
+ * storing 2^w
+ */
+ private int two_power_w;
+
+ /**
+ * Winternitz parameter w
+ */
+ private int w;
+
+ /**
+ * the amount of distributed computation steps when updateLeaf is called
+ */
+ private int steps;
+
+ /**
+ * the internal seed
+ */
+ private byte[] seed;
+
+ /**
+ * the OTS privateKey parts
+ */
+ byte[] privateKeyOTS;
+
+ /**
+ * This constructor regenerates a prior GMSSLeaf object
+ *
+ * @param digest an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding
+ * provider
+ * @param otsIndex status bytes
+ * @param numLeafs status ints
+ */
+ public GMSSLeaf(Digest digest, byte[][] otsIndex, int[] numLeafs)
+ {
+ this.i = numLeafs[0];
+ this.j = numLeafs[1];
+ this.steps = numLeafs[2];
+ this.w = numLeafs[3];
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+
+ // initialize arrays
+ this.privateKeyOTS = otsIndex[0];
+ this.seed = otsIndex[1];
+ this.concHashs = otsIndex[2];
+ this.leaf = otsIndex[3];
+ }
+
+ /**
+ * The constructor precomputes some needed variables for distributed leaf
+ * calculation
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function and PRNG and the digest of the corresponding
+ * provider
+ * @param w the winterniz parameter of that tree the leaf is computed
+ * for
+ * @param numLeafs the number of leafs of the tree from where the distributed
+ * computation is called
+ */
+ GMSSLeaf(Digest digest, int w, int numLeafs)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+ this.steps = (int)Math
+ .ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
+ / (double)(numLeafs));
+
+ // initialize arrays
+ this.seed = new byte[mdsize];
+ this.leaf = new byte[mdsize];
+ this.privateKeyOTS = new byte[mdsize];
+ this.concHashs = new byte[mdsize * keysize];
+ }
+
+ public GMSSLeaf(Digest digest, int w, int numLeafs, byte[] seed0)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+ this.steps = (int)Math
+ .ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
+ / (double)(numLeafs));
+
+ // initialize arrays
+ this.seed = new byte[mdsize];
+ this.leaf = new byte[mdsize];
+ this.privateKeyOTS = new byte[mdsize];
+ this.concHashs = new byte[mdsize * keysize];
+
+ initLeafCalc(seed0);
+ }
+
+ private GMSSLeaf(GMSSLeaf original)
+ {
+ this.messDigestOTS = original.messDigestOTS;
+ this.mdsize = original.mdsize;
+ this.keysize = original.keysize;
+ this.gmssRandom = original.gmssRandom;
+ this.leaf = Arrays.clone(original.leaf);
+ this.concHashs = Arrays.clone(original.concHashs);
+ this.i = original.i;
+ this.j = original.j;
+ this.two_power_w = original.two_power_w;
+ this.w = original.w;
+ this.steps = original.steps;
+ this.seed = Arrays.clone(original.seed);
+ this.privateKeyOTS = Arrays.clone(original.privateKeyOTS);
+ }
+
+ /**
+ * initialize the distributed leaf calculation reset i,j and compute OTSseed
+ * with seed0
+ *
+ * @param seed0 the starting seed
+ */
+ // TODO: this really looks like it should be either always called from a constructor or nextLeaf.
+ void initLeafCalc(byte[] seed0)
+ {
+ this.i = 0;
+ this.j = 0;
+ byte[] dummy = new byte[mdsize];
+ System.arraycopy(seed0, 0, dummy, 0, seed.length);
+ this.seed = gmssRandom.nextSeed(dummy);
+ }
+
+ GMSSLeaf nextLeaf()
+ {
+ GMSSLeaf nextLeaf = new GMSSLeaf(this);
+
+ nextLeaf.updateLeafCalc();
+
+ return nextLeaf;
+ }
+
+ /**
+ * Processes steps
steps of distributed leaf calculation
+ *
+ * @return true if leaf is completed, else false
+ */
+ private void updateLeafCalc()
+ {
+ byte[] buf = new byte[messDigestOTS.getDigestSize()];
+
+ // steps times do
+ // TODO: this really needs to be looked at, the 10000 has been added as
+ // prior to this the leaf value always ended up as zeros.
+ for (int s = 0; s < steps + 10000; s++)
+ {
+ if (i == keysize && j == two_power_w - 1)
+ { // [3] at last hash the
+ // concatenation
+ messDigestOTS.update(concHashs, 0, concHashs.length);
+ leaf = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(leaf, 0);
+ return;
+ }
+ else if (i == 0 || j == two_power_w - 1)
+ { // [1] at the
+ // beginning and
+ // when [2] is
+ // finished: get the
+ // next private key
+ // part
+ i++;
+ j = 0;
+ // get next privKey part
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ }
+ else
+ { // [2] hash the privKey part
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = buf;
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ j++;
+ if (j == two_power_w - 1)
+ { // after w hashes add to the
+ // concatenated array
+ System.arraycopy(privateKeyOTS, 0, concHashs, mdsize
+ * (i - 1), mdsize);
+ }
+ }
+ }
+
+ throw new IllegalStateException("unable to updateLeaf in steps: " + steps + " " + i + " " + j);
+ }
+
+ /**
+ * Returns the leaf value.
+ *
+ * @return the leaf value
+ */
+ public byte[] getLeaf()
+ {
+ return Arrays.clone(leaf);
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer intValue
.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of intValue
+ */
+ private int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[4][];
+ statByte[0] = new byte[mdsize];
+ statByte[1] = new byte[mdsize];
+ statByte[2] = new byte[mdsize * keysize];
+ statByte[3] = new byte[mdsize];
+ statByte[0] = privateKeyOTS;
+ statByte[1] = seed;
+ statByte[2] = concHashs;
+ statByte[3] = leaf;
+
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int[] statInt = new int[4];
+ statInt[0] = i;
+ statInt[1] = j;
+ statInt[2] = steps;
+ statInt[3] = w;
+ return statInt;
+ }
+
+ /**
+ * Returns a String representation of the main part of this element
+ *
+ * @return a String representation of the main part of this element
+ */
+ public String toString()
+ {
+ String out = "";
+
+ for (int i = 0; i < 4; i++)
+ {
+ out = out + this.getStatInt()[i] + " ";
+ }
+ out = out + " " + this.mdsize + " " + this.keysize + " "
+ + this.two_power_w + " ";
+
+ byte[][] temp = this.getStatByte();
+ for (int i = 0; i < 4; i++)
+ {
+ if (temp[i] != null)
+ {
+ out = out + new String(Hex.encode(temp[i])) + " ";
+ }
+ else
+ {
+ out = out + "null ";
+ }
+ }
+ return out;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java
new file mode 100644
index 000000000..ffc5043e5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java
@@ -0,0 +1,156 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * This class provides a specification for the GMSS parameters that are used by
+ * the GMSSKeyPairGenerator and GMSSSignature classes.
+ *
+ * @see org.spongycastle.pqc.crypto.gmss.GMSSKeyPairGenerator
+ */
+public class GMSSParameters
+{
+ /**
+ * The number of authentication tree layers.
+ */
+ private int numOfLayers;
+
+ /**
+ * The height of the authentication trees of each layer.
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * The Winternitz Parameter 'w' of each layer.
+ */
+ private int[] winternitzParameter;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ /**
+ * The constructor for the parameters of the GMSSKeyPairGenerator.
+ *
+ *
+ * @param layers the number of authentication tree layers
+ * @param heightOfTrees the height of the authentication trees
+ * @param winternitzParameter the Winternitz Parameter 'w' of each layer
+ * @param K parameter for authpath computation
+ */
+ public GMSSParameters(int layers, int[] heightOfTrees, int[] winternitzParameter, int[] K)
+ throws IllegalArgumentException
+ {
+ init(layers, heightOfTrees, winternitzParameter, K);
+ }
+
+ private void init(int layers, int[] heightOfTrees,
+ int[] winternitzParameter, int[] K)
+ throws IllegalArgumentException
+ {
+ boolean valid = true;
+ String errMsg = "";
+ this.numOfLayers = layers;
+ if ((numOfLayers != winternitzParameter.length)
+ || (numOfLayers != heightOfTrees.length)
+ || (numOfLayers != K.length))
+ {
+ valid = false;
+ errMsg = "Unexpected parameterset format";
+ }
+ for (int i = 0; i < numOfLayers; i++)
+ {
+ if ((K[i] < 2) || ((heightOfTrees[i] - K[i]) % 2 != 0))
+ {
+ valid = false;
+ errMsg = "Wrong parameter K (K >= 2 and H-K even required)!";
+ }
+
+ if ((heightOfTrees[i] < 4) || (winternitzParameter[i] < 2))
+ {
+ valid = false;
+ errMsg = "Wrong parameter H or w (H > 3 and w > 1 required)!";
+ }
+ }
+
+ if (valid)
+ {
+ this.heightOfTrees = Arrays.clone(heightOfTrees);
+ this.winternitzParameter = Arrays.clone(winternitzParameter);
+ this.K = Arrays.clone(K);
+ }
+ else
+ {
+ throw new IllegalArgumentException(errMsg);
+ }
+ }
+
+ public GMSSParameters(int keySize)
+ throws IllegalArgumentException
+ {
+ if (keySize <= 10)
+ { // create 2^10 keys
+ int[] defh = {10};
+ int[] defw = {3};
+ int[] defk = {2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ else if (keySize <= 20)
+ { // create 2^20 keys
+ int[] defh = {10, 10};
+ int[] defw = {5, 4};
+ int[] defk = {2, 2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ else
+ { // create 2^40 keys, keygen lasts around 80 seconds
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {9, 9, 9, 3};
+ int[] defk = {2, 2, 2, 2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ }
+
+ /**
+ * Returns the number of levels of the authentication trees.
+ *
+ * @return The number of levels of the authentication trees.
+ */
+ public int getNumOfLayers()
+ {
+ return numOfLayers;
+ }
+
+ /**
+ * Returns the array of height (for each layer) of the authentication trees
+ *
+ * @return The array of height (for each layer) of the authentication trees
+ */
+ public int[] getHeightOfTrees()
+ {
+ return Arrays.clone(heightOfTrees);
+ }
+
+ /**
+ * Returns the array of WinternitzParameter (for each layer) of the
+ * authentication trees
+ *
+ * @return The array of WinternitzParameter (for each layer) of the
+ * authentication trees
+ */
+ public int[] getWinternitzParameter()
+ {
+ return Arrays.clone(winternitzParameter);
+ }
+
+ /**
+ * Returns the parameter K needed for authentication path computation
+ *
+ * @return The parameter K needed for authentication path computation
+ */
+ public int[] getK()
+ {
+ return Arrays.clone(K);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
new file mode 100644
index 000000000..29e84b1fa
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
@@ -0,0 +1,1041 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Vector;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+import org.spongycastle.util.Arrays;
+
+
+/**
+ * This class provides a specification for a GMSS private key.
+ */
+public class GMSSPrivateKeyParameters
+ extends GMSSKeyParameters
+{
+ private int[] index;
+
+ private byte[][] currentSeeds;
+ private byte[][] nextNextSeeds;
+
+ private byte[][][] currentAuthPaths;
+ private byte[][][] nextAuthPaths;
+
+ private Treehash[][] currentTreehash;
+ private Treehash[][] nextTreehash;
+
+ private Vector[] currentStack;
+ private Vector[] nextStack;
+
+ private Vector[][] currentRetain;
+ private Vector[][] nextRetain;
+
+ private byte[][][] keep;
+
+ private GMSSLeaf[] nextNextLeaf;
+ private GMSSLeaf[] upperLeaf;
+ private GMSSLeaf[] upperTreehashLeaf;
+
+ private int[] minTreehash;
+
+ private GMSSParameters gmssPS;
+
+ private byte[][] nextRoot;
+ private GMSSRootCalc[] nextNextRoot;
+
+ private byte[][] currentRootSig;
+ private GMSSRootSig[] nextRootSig;
+
+ private GMSSDigestProvider digestProvider;
+
+ private boolean used = false;
+
+ /**
+ * An array of the heights of the authentication trees of each layer
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * An array of the Winternitz parameter 'w' of each layer
+ */
+ private int[] otsIndex;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ /**
+ * the number of Layers
+ */
+ private int numLayer;
+
+ /**
+ * The hash function used to construct the authentication trees
+ */
+ private Digest messDigestTrees;
+
+ /**
+ * The message digest length
+ */
+ private int mdLength;
+
+ /**
+ * The PRNG used for private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+
+ /**
+ * The number of leafs of one tree of each layer
+ */
+ private int[] numLeafs;
+
+
+ /**
+ * Generates a new GMSS private key
+ *
+ * @param currentSeed seed for the generation of private OTS keys for the
+ * current subtrees
+ * @param nextNextSeed seed for the generation of private OTS keys for the next
+ * subtrees
+ * @param currentAuthPath array of current authentication paths
+ * @param nextAuthPath array of next authentication paths
+ * @param currentTreehash array of current treehash instances
+ * @param nextTreehash array of next treehash instances
+ * @param currentStack array of current shared stacks
+ * @param nextStack array of next shared stacks
+ * @param currentRetain array of current retain stacks
+ * @param nextRetain array of next retain stacks
+ * @param nextRoot the roots of the next subtree
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * @param gmssParameterset the GMSS Parameterset
+ * @see org.spongycastle.pqc.crypto.gmss.GMSSKeyPairGenerator
+ */
+
+ public GMSSPrivateKeyParameters(byte[][] currentSeed, byte[][] nextNextSeed,
+ byte[][][] currentAuthPath, byte[][][] nextAuthPath,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain, byte[][] nextRoot,
+ byte[][] currentRootSig, GMSSParameters gmssParameterset,
+ GMSSDigestProvider digestProvider)
+ {
+ this(null, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath,
+ null, currentTreehash, nextTreehash, currentStack, nextStack,
+ currentRetain, nextRetain, null, null, null, null, nextRoot,
+ null, currentRootSig, null, gmssParameterset, digestProvider);
+ }
+
+ /**
+ * /**
+ *
+ * @param index tree indices
+ * @param keep keep array for the authPath algorithm
+ * @param currentTreehash treehash for authPath algorithm of current tree
+ * @param nextTreehash treehash for authPath algorithm of next tree (TREE+)
+ * @param currentStack shared stack for authPath algorithm of current tree
+ * @param nextStack shared stack for authPath algorithm of next tree (TREE+)
+ * @param currentRetain retain stack for authPath algorithm of current tree
+ * @param nextRetain retain stack for authPath algorithm of next tree (TREE+)
+ * @param nextNextLeaf array of upcoming leafs of the tree after next (LEAF++) of
+ * each layer
+ * @param upperLeaf needed for precomputation of upper nodes
+ * @param upperTreehashLeaf needed for precomputation of upper treehash nodes
+ * @param minTreehash index of next treehash instance to receive an update
+ * @param nextRoot the roots of the next trees (ROOT+)
+ * @param nextNextRoot the roots of the tree after next (ROOT++)
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * (SIG)
+ * @param nextRootSig array of signatures of the roots of the next subtree
+ * (SIG+)
+ * @param gmssParameterset the GMSS Parameterset
+ */
+ public GMSSPrivateKeyParameters(int[] index, byte[][] currentSeeds,
+ byte[][] nextNextSeeds, byte[][][] currentAuthPaths,
+ byte[][][] nextAuthPaths, byte[][][] keep,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain,
+ GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf,
+ GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot,
+ GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig,
+ GMSSRootSig[] nextRootSig, GMSSParameters gmssParameterset,
+ GMSSDigestProvider digestProvider)
+ {
+
+ super(true, gmssParameterset);
+
+ // construct message digest
+
+ this.messDigestTrees = digestProvider.get();
+ this.mdLength = messDigestTrees.getDigestSize();
+
+
+ // Parameter
+ this.gmssPS = gmssParameterset;
+ this.otsIndex = gmssParameterset.getWinternitzParameter();
+ this.K = gmssParameterset.getK();
+ this.heightOfTrees = gmssParameterset.getHeightOfTrees();
+ // initialize numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+ // initialize index if null
+ if (index == null)
+ {
+ this.index = new int[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.index[i] = 0;
+ }
+ }
+ else
+ {
+ this.index = index;
+ }
+
+ this.currentSeeds = currentSeeds;
+ this.nextNextSeeds = nextNextSeeds;
+
+ this.currentAuthPaths = currentAuthPaths;
+ this.nextAuthPaths = nextAuthPaths;
+
+ // initialize keep if null
+ if (keep == null)
+ {
+ this.keep = new byte[numLayer][][];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.keep[i] = new byte[(int)Math.floor(heightOfTrees[i] / 2)][mdLength];
+ }
+ }
+ else
+ {
+ this.keep = keep;
+ }
+
+ // initialize stack if null
+ if (currentStack == null)
+ {
+ this.currentStack = new Vector[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.currentStack[i] = new Vector();
+ }
+ }
+ else
+ {
+ this.currentStack = currentStack;
+ }
+
+ // initialize nextStack if null
+ if (nextStack == null)
+ {
+ this.nextStack = new Vector[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.nextStack[i] = new Vector();
+ }
+ }
+ else
+ {
+ this.nextStack = nextStack;
+ }
+
+ this.currentTreehash = currentTreehash;
+ this.nextTreehash = nextTreehash;
+
+ this.currentRetain = currentRetain;
+ this.nextRetain = nextRetain;
+
+ this.nextRoot = nextRoot;
+
+ this.digestProvider = digestProvider;
+
+ if (nextNextRoot == null)
+ {
+ this.nextNextRoot = new GMSSRootCalc[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.nextNextRoot[i] = new GMSSRootCalc(
+ this.heightOfTrees[i + 1], this.K[i + 1], this.digestProvider);
+ }
+ }
+ else
+ {
+ this.nextNextRoot = nextNextRoot;
+ }
+ this.currentRootSig = currentRootSig;
+
+ // calculate numLeafs
+ numLeafs = new int[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ numLeafs[i] = 1 << heightOfTrees[i];
+ }
+ // construct PRNG
+ this.gmssRandom = new GMSSRandom(messDigestTrees);
+
+ if (numLayer > 1)
+ {
+ // construct the nextNextLeaf (LEAFs++) array for upcoming leafs in
+ // tree after next (TREE++)
+ if (nextNextLeaf == null)
+ {
+ this.nextNextLeaf = new GMSSLeaf[numLayer - 2];
+ for (int i = 0; i < numLayer - 2; i++)
+ {
+ this.nextNextLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i + 1], numLeafs[i + 2], this.nextNextSeeds[i]);
+ }
+ }
+ else
+ {
+ this.nextNextLeaf = nextNextLeaf;
+ }
+ }
+ else
+ {
+ this.nextNextLeaf = new GMSSLeaf[0];
+ }
+
+ // construct the upperLeaf array for upcoming leafs in tree over the
+ // actual
+ if (upperLeaf == null)
+ {
+ this.upperLeaf = new GMSSLeaf[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.upperLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i],
+ numLeafs[i + 1], this.currentSeeds[i]);
+ }
+ }
+ else
+ {
+ this.upperLeaf = upperLeaf;
+ }
+
+ // construct the leafs for upcoming leafs in treehashs in tree over the
+ // actual
+ if (upperTreehashLeaf == null)
+ {
+ this.upperTreehashLeaf = new GMSSLeaf[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.upperTreehashLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i], numLeafs[i + 1]);
+ }
+ }
+ else
+ {
+ this.upperTreehashLeaf = upperTreehashLeaf;
+ }
+
+ if (minTreehash == null)
+ {
+ this.minTreehash = new int[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.minTreehash[i] = -1;
+ }
+ }
+ else
+ {
+ this.minTreehash = minTreehash;
+ }
+
+ // construct the nextRootSig (RootSig++)
+ byte[] dummy = new byte[mdLength];
+ byte[] OTSseed = new byte[mdLength];
+ if (nextRootSig == null)
+ {
+ this.nextRootSig = new GMSSRootSig[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ System.arraycopy(currentSeeds[i], 0, dummy, 0, mdLength);
+ gmssRandom.nextSeed(dummy);
+ OTSseed = gmssRandom.nextSeed(dummy);
+ this.nextRootSig[i] = new GMSSRootSig(digestProvider.get(), otsIndex[i],
+ heightOfTrees[i + 1]);
+ this.nextRootSig[i].initSign(OTSseed, nextRoot[i]);
+ }
+ }
+ else
+ {
+ this.nextRootSig = nextRootSig;
+ }
+ }
+
+ // we assume this only gets called from nextKey so used is never copied.
+ private GMSSPrivateKeyParameters(GMSSPrivateKeyParameters original)
+ {
+ super(true, original.getParameters());
+
+ this.index = Arrays.clone(original.index);
+ this.currentSeeds = Arrays.clone(original.currentSeeds);
+ this.nextNextSeeds = Arrays.clone(original.nextNextSeeds);
+ this.currentAuthPaths = Arrays.clone(original.currentAuthPaths);
+ this.nextAuthPaths = Arrays.clone(original.nextAuthPaths);
+ this.currentTreehash = original.currentTreehash;
+ this.nextTreehash = original.nextTreehash;
+ this.currentStack = original.currentStack;
+ this.nextStack = original.nextStack;
+ this.currentRetain = original.currentRetain;
+ this.nextRetain = original.nextRetain;
+ this.keep = Arrays.clone(original.keep);
+ this.nextNextLeaf = original.nextNextLeaf;
+ this.upperLeaf = original.upperLeaf;
+ this.upperTreehashLeaf = original.upperTreehashLeaf;
+ this.minTreehash = original.minTreehash;
+ this.gmssPS = original.gmssPS;
+ this.nextRoot = Arrays.clone(original.nextRoot);
+ this.nextNextRoot = original.nextNextRoot;
+ this.currentRootSig = original.currentRootSig;
+ this.nextRootSig = original.nextRootSig;
+ this.digestProvider = original.digestProvider;
+ this.heightOfTrees = original.heightOfTrees;
+ this.otsIndex = original.otsIndex;
+ this.K = original.K;
+ this.numLayer = original.numLayer;
+ this.messDigestTrees = original.messDigestTrees;
+ this.mdLength = original.mdLength;
+ this.gmssRandom = original.gmssRandom;
+ this.numLeafs = original.numLeafs;
+ }
+
+ public boolean isUsed()
+ {
+ return this.used;
+ }
+
+ public void markUsed()
+ {
+ this.used = true;
+ }
+
+ public GMSSPrivateKeyParameters nextKey()
+ {
+ GMSSPrivateKeyParameters nKey = new GMSSPrivateKeyParameters(this);
+
+ nKey.nextKey(gmssPS.getNumOfLayers() - 1);
+
+ return nKey;
+ }
+
+ /**
+ * This method updates the GMSS private key for the next signature
+ *
+ * @param layer the layer where the next key is processed
+ */
+ private void nextKey(int layer)
+ {
+ // only for lowest layer ( other layers indices are raised in nextTree()
+ // method )
+ if (layer == numLayer - 1)
+ {
+ index[layer]++;
+ } // else System.out.println(" --- nextKey on layer " + layer + "
+ // index is now : " + index[layer]);
+
+ // if tree of this layer is depleted
+ if (index[layer] == numLeafs[layer])
+ {
+ if (numLayer != 1)
+ {
+ nextTree(layer);
+ index[layer] = 0;
+ }
+ }
+ else
+ {
+ updateKey(layer);
+ }
+ }
+
+ /**
+ * Switch to next subtree if the current one is depleted
+ *
+ * @param layer the layer where the next tree is processed
+ */
+ private void nextTree(int layer)
+ {
+ // System.out.println("NextTree method called on layer " + layer);
+ // dont create next tree for the top layer
+ if (layer > 0)
+ {
+ // raise index for upper layer
+ index[layer - 1]++;
+
+ // test if it is already the last tree
+ boolean lastTree = true;
+ int z = layer;
+ do
+ {
+ z--;
+ if (index[z] < numLeafs[z])
+ {
+ lastTree = false;
+ }
+ }
+ while (lastTree && (z > 0));
+
+ // only construct next subtree if last one is not already in use
+ if (!lastTree)
+ {
+ gmssRandom.nextSeed(currentSeeds[layer]);
+
+ // last step of distributed signature calculation
+ nextRootSig[layer - 1].updateSign();
+
+ // last step of distributed leaf calculation for nextNextLeaf
+ if (layer > 1)
+ {
+ nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf();
+ }
+
+ // last step of distributed leaf calculation for upper leaf
+ upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf();
+
+ // last step of distributed leaf calculation for all treehashs
+
+ if (minTreehash[layer - 1] >= 0)
+ {
+ upperTreehashLeaf[layer - 1] = upperTreehashLeaf[layer - 1].nextLeaf();
+ byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf();
+ // if update is required use the precomputed leaf to update
+ // treehash
+ try
+ {
+ currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .update(this.gmssRandom, leaf);
+ // System.out.println("UUUpdated TH " +
+ // minTreehash[layer - 1]);
+ if (currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .wasFinished())
+ {
+ // System.out.println("FFFinished TH " +
+ // minTreehash[layer - 1]);
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ }
+
+ // last step of nextNextAuthRoot calculation
+ this.updateNextNextAuthRoot(layer);
+
+ // ******************************************************** /
+
+ // NOW: advance to next tree on layer 'layer'
+
+ // NextRootSig --> currentRootSigs
+ this.currentRootSig[layer - 1] = nextRootSig[layer - 1]
+ .getSig();
+
+ // -----------------------
+
+ // nextTreehash --> currentTreehash
+ // nextNextTreehash --> nextTreehash
+ for (int i = 0; i < heightOfTrees[layer] - K[layer]; i++)
+ {
+ this.currentTreehash[layer][i] = this.nextTreehash[layer - 1][i];
+ this.nextTreehash[layer - 1][i] = this.nextNextRoot[layer - 1]
+ .getTreehash()[i];
+ }
+
+ // NextAuthPath --> currentAuthPath
+ // nextNextAuthPath --> nextAuthPath
+ for (int i = 0; i < heightOfTrees[layer]; i++)
+ {
+ System.arraycopy(nextAuthPaths[layer - 1][i], 0,
+ currentAuthPaths[layer][i], 0, mdLength);
+ System.arraycopy(nextNextRoot[layer - 1].getAuthPath()[i],
+ 0, nextAuthPaths[layer - 1][i], 0, mdLength);
+ }
+
+ // nextRetain --> currentRetain
+ // nextNextRetain --> nextRetain
+ for (int i = 0; i < K[layer] - 1; i++)
+ {
+ this.currentRetain[layer][i] = this.nextRetain[layer - 1][i];
+ this.nextRetain[layer - 1][i] = this.nextNextRoot[layer - 1]
+ .getRetain()[i];
+ }
+
+ // nextStack --> currentStack
+ this.currentStack[layer] = this.nextStack[layer - 1];
+ // nextNextStack --> nextStack
+ this.nextStack[layer - 1] = this.nextNextRoot[layer - 1]
+ .getStack();
+
+ // nextNextRoot --> nextRoot
+ this.nextRoot[layer - 1] = this.nextNextRoot[layer - 1]
+ .getRoot();
+ // -----------------------
+
+ // -----------------
+ byte[] OTSseed = new byte[mdLength];
+ byte[] dummy = new byte[mdLength];
+ // gmssRandom.setSeed(currentSeeds[layer]);
+ System
+ .arraycopy(currentSeeds[layer - 1], 0, dummy, 0,
+ mdLength);
+ OTSseed = gmssRandom.nextSeed(dummy); // only need OTSSeed
+ OTSseed = gmssRandom.nextSeed(dummy);
+ OTSseed = gmssRandom.nextSeed(dummy);
+ // nextWinSig[layer-1]=new
+ // GMSSWinSig(OTSseed,algNames,otsIndex[layer-1],heightOfTrees[layer],nextRoot[layer-1]);
+ nextRootSig[layer - 1].initSign(OTSseed, nextRoot[layer - 1]);
+
+ // nextKey for upper layer
+ nextKey(layer - 1);
+ }
+ }
+ }
+
+ /**
+ * This method computes the authpath (AUTH) for the current tree,
+ * Additionally the root signature for the next tree (SIG+), the authpath
+ * (AUTH++) and root (ROOT++) for the tree after next in layer
+ * layer
, and the LEAF++^1 for the next next tree in the
+ * layer above are updated This method is used by nextKey()
+ *
+ * @param layer
+ */
+ private void updateKey(int layer)
+ {
+ // ----------current tree processing of actual layer---------
+ // compute upcoming authpath for current Tree (AUTH)
+ computeAuthPaths(layer);
+
+ // -----------distributed calculations part------------
+ // not for highest tree layer
+ if (layer > 0)
+ {
+
+ // compute (partial) next leaf on TREE++ (not on layer 1 and 0)
+ if (layer > 1)
+ {
+ nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf();
+ }
+
+ // compute (partial) next leaf on tree above (not on layer 0)
+ upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf();
+
+ // compute (partial) next leaf for all treehashs on tree above (not
+ // on layer 0)
+
+ int t = (int)Math
+ .floor((double)(this.getNumLeafs(layer) * 2)
+ / (double)(this.heightOfTrees[layer - 1] - this.K[layer - 1]));
+
+ if (index[layer] % t == 1)
+ {
+ // System.out.println(" layer: " + layer + " index: " +
+ // index[layer] + " t : " + t);
+
+ // take precomputed node for treehash update
+ // ------------------------------------------------
+ if (index[layer] > 1 && minTreehash[layer - 1] >= 0)
+ {
+ byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf();
+ // if update is required use the precomputed leaf to update
+ // treehash
+ try
+ {
+ currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .update(this.gmssRandom, leaf);
+ // System.out.println("Updated TH " + minTreehash[layer
+ // - 1]);
+ if (currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .wasFinished())
+ {
+ // System.out.println("Finished TH " +
+ // minTreehash[layer - 1]);
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ // ------------------------------------------------
+ }
+
+ // initialize next leaf precomputation
+ // ------------------------------------------------
+
+ // get lowest index of treehashs
+ this.minTreehash[layer - 1] = getMinTreehashIndex(layer - 1);
+
+ if (this.minTreehash[layer - 1] >= 0)
+ {
+ // initialize leaf
+ byte[] seed = this.currentTreehash[layer - 1][this.minTreehash[layer - 1]]
+ .getSeedActive();
+ this.upperTreehashLeaf[layer - 1] = new GMSSLeaf(
+ this.digestProvider.get(), this.otsIndex[layer - 1], t, seed);
+ this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf();
+ // System.out.println("restarted treehashleaf (" + (layer -
+ // 1) + "," + this.minTreehash[layer - 1] + ")");
+ }
+ // ------------------------------------------------
+
+ }
+ else
+ {
+ // update the upper leaf for the treehash one step
+ if (this.minTreehash[layer - 1] >= 0)
+ {
+ this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf();
+ // if (minTreehash[layer - 1] > 3)
+ // System.out.print("#");
+ }
+ }
+
+ // compute (partial) the signature of ROOT+ (RootSig+) (not on top
+ // layer)
+ nextRootSig[layer - 1].updateSign();
+
+ // compute (partial) AUTHPATH++ & ROOT++ (not on top layer)
+ if (index[layer] == 1)
+ {
+ // init root and authpath calculation for tree after next
+ // (AUTH++, ROOT++)
+ this.nextNextRoot[layer - 1].initialize(new Vector());
+ }
+
+ // update root and authpath calculation for tree after next (AUTH++,
+ // ROOT++)
+ this.updateNextNextAuthRoot(layer);
+ }
+ // ----------- end distributed calculations part-----------------
+ }
+
+ /**
+ * This method returns the index of the next Treehash instance that should
+ * receive an update
+ *
+ * @param layer the layer of the GMSS tree
+ * @return index of the treehash instance that should get the update
+ */
+ private int getMinTreehashIndex(int layer)
+ {
+ int minTreehash = -1;
+ for (int h = 0; h < heightOfTrees[layer] - K[layer]; h++)
+ {
+ if (currentTreehash[layer][h].wasInitialized()
+ && !currentTreehash[layer][h].wasFinished())
+ {
+ if (minTreehash == -1)
+ {
+ minTreehash = h;
+ }
+ else if (currentTreehash[layer][h].getLowestNodeHeight() < currentTreehash[layer][minTreehash]
+ .getLowestNodeHeight())
+ {
+ minTreehash = h;
+ }
+ }
+ }
+ return minTreehash;
+ }
+
+ /**
+ * Computes the upcoming currentAuthpath of layer layer
using
+ * the revisited authentication path computation of Dahmen/Schneider 2008
+ *
+ * @param layer the actual layer
+ */
+ private void computeAuthPaths(int layer)
+ {
+
+ int Phi = index[layer];
+ int H = heightOfTrees[layer];
+ int K = this.K[layer];
+
+ // update all nextSeeds for seed scheduling
+ for (int i = 0; i < H - K; i++)
+ {
+ currentTreehash[layer][i].updateNextSeed(gmssRandom);
+ }
+
+ // STEP 1 of Algorithm
+ int Tau = heightOfPhi(Phi);
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(currentSeeds[layer]);
+
+ // STEP 2 of Algorithm
+ // if phi's parent on height tau + 1 if left node, store auth_tau
+ // in keep_tau.
+ // TODO check it, formerly was
+ // int L = Phi / (int) Math.floor(Math.pow(2, Tau + 1));
+ // L %= 2;
+ int L = (Phi >>> (Tau + 1)) & 1;
+
+ byte[] tempKeep = new byte[mdLength];
+ // store the keep node not in keep[layer][tau/2] because it might be in
+ // use
+ // wait until the space is freed in step 4a
+ if (Tau < H - 1 && L == 0)
+ {
+ System.arraycopy(currentAuthPaths[layer][Tau], 0, tempKeep, 0,
+ mdLength);
+ }
+
+ byte[] help = new byte[mdLength];
+ // STEP 3 of Algorithm
+ // if phi is left child, compute and store leaf for next currentAuthPath
+ // path,
+ // (obtained by veriying current signature)
+ if (Tau == 0)
+ {
+ // LEAFCALC !!!
+ if (layer == numLayer - 1)
+ { // lowest layer computes the
+ // necessary leaf completely at this
+ // time
+ WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed,
+ digestProvider.get(), otsIndex[layer]);
+ help = ots.getPublicKey();
+ }
+ else
+ { // other layers use the precomputed leafs in
+ // nextNextLeaf
+ byte[] dummy = new byte[mdLength];
+ System.arraycopy(currentSeeds[layer], 0, dummy, 0, mdLength);
+ gmssRandom.nextSeed(dummy);
+ help = upperLeaf[layer].getLeaf();
+ this.upperLeaf[layer].initLeafCalc(dummy);
+
+ // WinternitzOTSVerify otsver = new
+ // WinternitzOTSVerify(algNames, otsIndex[layer]);
+ // byte[] help2 = otsver.Verify(currentRoot[layer],
+ // currentRootSig[layer]);
+ // System.out.println(" --- " + layer + " " +
+ // ByteUtils.toHexString(help) + " " +
+ // ByteUtils.toHexString(help2));
+ }
+ System.arraycopy(help, 0, currentAuthPaths[layer][0], 0, mdLength);
+ }
+ else
+ {
+ // STEP 4a of Algorithm
+ // get new left currentAuthPath node on height tau
+ byte[] toBeHashed = new byte[mdLength << 1];
+ System.arraycopy(currentAuthPaths[layer][Tau - 1], 0, toBeHashed,
+ 0, mdLength);
+ // free the shared keep[layer][tau/2]
+ System.arraycopy(keep[layer][(int)Math.floor((Tau - 1) / 2)], 0,
+ toBeHashed, mdLength, mdLength);
+ messDigestTrees.update(toBeHashed, 0, toBeHashed.length);
+ currentAuthPaths[layer][Tau] = new byte[messDigestTrees.getDigestSize()];
+ messDigestTrees.doFinal(currentAuthPaths[layer][Tau], 0);
+
+ // STEP 4b and 4c of Algorithm
+ // copy right nodes to currentAuthPath on height 0..Tau-1
+ for (int i = 0; i < Tau; i++)
+ {
+
+ // STEP 4b of Algorithm
+ // 1st: copy from treehashs
+ if (i < H - K)
+ {
+ if (currentTreehash[layer][i].wasFinished())
+ {
+ System.arraycopy(currentTreehash[layer][i]
+ .getFirstNode(), 0, currentAuthPaths[layer][i],
+ 0, mdLength);
+ currentTreehash[layer][i].destroy();
+ }
+ else
+ {
+ System.err
+ .println("Treehash ("
+ + layer
+ + ","
+ + i
+ + ") not finished when needed in AuthPathComputation");
+ }
+ }
+
+ // 2nd: copy precomputed values from Retain
+ if (i < H - 1 && i >= H - K)
+ {
+ if (currentRetain[layer][i - (H - K)].size() > 0)
+ {
+ // pop element from retain
+ System.arraycopy(currentRetain[layer][i - (H - K)]
+ .lastElement(), 0, currentAuthPaths[layer][i],
+ 0, mdLength);
+ currentRetain[layer][i - (H - K)]
+ .removeElementAt(currentRetain[layer][i
+ - (H - K)].size() - 1);
+ }
+ }
+
+ // STEP 4c of Algorithm
+ // initialize new stack at heights 0..Tau-1
+ if (i < H - K)
+ {
+ // create stacks anew
+ int startPoint = Phi + 3 * (1 << i);
+ if (startPoint < numLeafs[layer])
+ {
+ // if (layer < 2) {
+ // System.out.println("initialized TH " + i + " on layer
+ // " + layer);
+ // }
+ currentTreehash[layer][i].initialize();
+ }
+ }
+ }
+ }
+
+ // now keep space is free to use
+ if (Tau < H - 1 && L == 0)
+ {
+ System.arraycopy(tempKeep, 0,
+ keep[layer][(int)Math.floor(Tau / 2)], 0, mdLength);
+ }
+
+ // only update empty stack at height h if all other stacks have
+ // tailnodes with height >h
+ // finds active stack with lowest node height, choses lower index in
+ // case of tie
+
+ // on the lowest layer leafs must be computed at once, no precomputation
+ // is possible. So all treehash updates are done at once here
+ if (layer == numLayer - 1)
+ {
+ for (int tmp = 1; tmp <= (H - K) / 2; tmp++)
+ {
+ // index of the treehash instance that receives the next update
+ int minTreehash = getMinTreehashIndex(layer);
+
+ // if active treehash is found update with a leaf
+ if (minTreehash >= 0)
+ {
+ try
+ {
+ byte[] seed = new byte[mdLength];
+ System.arraycopy(
+ this.currentTreehash[layer][minTreehash]
+ .getSeedActive(), 0, seed, 0, mdLength);
+ byte[] seed2 = gmssRandom.nextSeed(seed);
+ WinternitzOTSignature ots = new WinternitzOTSignature(
+ seed2, this.digestProvider.get(), this.otsIndex[layer]);
+ byte[] leaf = ots.getPublicKey();
+ currentTreehash[layer][minTreehash].update(
+ this.gmssRandom, leaf);
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ }
+ }
+ }
+ else
+ { // on higher layers the updates are done later
+ this.minTreehash[layer] = getMinTreehashIndex(layer);
+ }
+ }
+
+ /**
+ * Returns the largest h such that 2^h | Phi
+ *
+ * @param Phi the leaf index
+ * @return The largest h
with 2^h | Phi
if
+ * Phi!=0
else return -1
+ */
+ private int heightOfPhi(int Phi)
+ {
+ if (Phi == 0)
+ {
+ return -1;
+ }
+ int Tau = 0;
+ int modul = 1;
+ while (Phi % modul == 0)
+ {
+ modul *= 2;
+ Tau += 1;
+ }
+ return Tau - 1;
+ }
+
+ /**
+ * Updates the authentication path and root calculation for the tree after
+ * next (AUTH++, ROOT++) in layer layer
+ *
+ * @param layer
+ */
+ private void updateNextNextAuthRoot(int layer)
+ {
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(nextNextSeeds[layer - 1]);
+
+ // get the necessary leaf
+ if (layer == numLayer - 1)
+ { // lowest layer computes the necessary
+ // leaf completely at this time
+ WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed,
+ digestProvider.get(), otsIndex[layer]);
+ this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], ots
+ .getPublicKey());
+ }
+ else
+ { // other layers use the precomputed leafs in nextNextLeaf
+ this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], nextNextLeaf[layer - 1].getLeaf());
+ this.nextNextLeaf[layer - 1].initLeafCalc(nextNextSeeds[layer - 1]);
+ }
+ }
+
+ public int[] getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * @return The current index of layer i
+ */
+ public int getIndex(int i)
+ {
+ return index[i];
+ }
+
+ public byte[][] getCurrentSeeds()
+ {
+ return Arrays.clone(currentSeeds);
+ }
+
+ public byte[][][] getCurrentAuthPaths()
+ {
+ return Arrays.clone(currentAuthPaths);
+ }
+
+ /**
+ * @return The one-time signature of the root of the current subtree
+ */
+ public byte[] getSubtreeRootSig(int i)
+ {
+ return currentRootSig[i];
+ }
+
+
+ public GMSSDigestProvider getName()
+ {
+ return digestProvider;
+ }
+
+ /**
+ * @return The number of leafs of each tree of layer i
+ */
+ public int getNumLeafs(int i)
+ {
+ return numLeafs[i];
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java
new file mode 100644
index 000000000..381ed00b0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java
@@ -0,0 +1,33 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+
+public class GMSSPublicKeyParameters
+ extends GMSSKeyParameters
+{
+ /**
+ * The GMSS public key
+ */
+ private byte[] gmssPublicKey;
+
+ /**
+ * The constructor.
+ *
+ * @param key a raw GMSS public key
+ * @param gmssParameterSet an instance of GMSSParameterset
+ */
+ public GMSSPublicKeyParameters(byte[] key, GMSSParameters gmssParameterSet)
+ {
+ super(false, gmssParameterSet);
+ this.gmssPublicKey = key;
+ }
+
+ /**
+ * Returns the GMSS public key
+ *
+ * @return The GMSS public key
+ */
+ public byte[] getPublicKey()
+ {
+ return gmssPublicKey;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java
new file mode 100644
index 000000000..88e87e9c6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java
@@ -0,0 +1,596 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class computes a whole Merkle tree and saves the needed values for
+ * AuthPath computation. It is used for precomputation of the root of a
+ * following tree. After initialization, 2^H updates are required to complete
+ * the root. Every update requires one leaf value as parameter. While computing
+ * the root all initial values for the authentication path algorithm (treehash,
+ * auth, retain) are stored for later use.
+ */
+public class GMSSRootCalc
+{
+
+ /**
+ * max height of the tree
+ */
+ private int heightOfTree;
+
+ /**
+ * length of the messageDigest
+ */
+ private int mdLength;
+
+ /**
+ * the treehash instances of the tree
+ */
+ private Treehash[] treehash;
+
+ /**
+ * stores the retain nodes for authPath computation
+ */
+ private Vector[] retain;
+
+ /**
+ * finally stores the root of the tree when finished
+ */
+ private byte[] root;
+
+ /**
+ * stores the authentication path y_1(i), i = 0..H-1
+ */
+ private byte[][] AuthPath;
+
+ /**
+ * the value K for the authentication path computation
+ */
+ private int K;
+
+ /**
+ * Vector element that stores the nodes on the stack
+ */
+ private Vector tailStack;
+
+ /**
+ * stores the height of all nodes laying on the tailStack
+ */
+ private Vector heightOfNodes;
+ /**
+ * The hash function used for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * An array of strings containing the name of the hash function used to
+ * construct the authentication trees and used by the OTS.
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * stores the index of the current node on each height of the tree
+ */
+ private int[] index;
+
+ /**
+ * true if instance was already initialized, false otherwise
+ */
+ private boolean isInitialized;
+
+ /**
+ * true it instance was finished
+ */
+ private boolean isFinished;
+
+ /**
+ * Integer that stores the index of the next seed that has to be omitted to
+ * the treehashs
+ */
+ private int indexForNextSeed;
+
+ /**
+ * temporary integer that stores the height of the next treehash instance
+ * that gets initialized with a seed
+ */
+ private int heightOfNextSeed;
+
+ /**
+ * This constructor regenerates a prior treehash object
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function and PRNG and the digest of the corresponding
+ * provider
+ * @param statByte status bytes
+ * @param statInt status ints
+ */
+ public GMSSRootCalc(Digest digest, byte[][] statByte, int[] statInt,
+ Treehash[] treeH, Vector[] ret)
+ {
+ this.messDigestTree = digestProvider.get();
+ this.digestProvider = digestProvider;
+ // decode statInt
+ this.heightOfTree = statInt[0];
+ this.mdLength = statInt[1];
+ this.K = statInt[2];
+ this.indexForNextSeed = statInt[3];
+ this.heightOfNextSeed = statInt[4];
+ if (statInt[5] == 1)
+ {
+ this.isFinished = true;
+ }
+ else
+ {
+ this.isFinished = false;
+ }
+ if (statInt[6] == 1)
+ {
+ this.isInitialized = true;
+ }
+ else
+ {
+ this.isInitialized = false;
+ }
+
+ int tailLength = statInt[7];
+
+ this.index = new int[heightOfTree];
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.index[i] = statInt[8 + i];
+ }
+
+ this.heightOfNodes = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.heightOfNodes.addElement(Integers.valueOf(statInt[8 + heightOfTree
+ + i]));
+ }
+
+ // decode statByte
+ this.root = statByte[0];
+
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.AuthPath[i] = statByte[1 + i];
+ }
+
+ this.tailStack = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.tailStack.addElement(statByte[1 + heightOfTree + i]);
+ }
+
+ // decode treeH
+ this.treehash = GMSSUtils.clone(treeH);
+
+ // decode ret
+ this.retain = GMSSUtils.clone(ret);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param heightOfTree maximal height of the tree
+ * @param digestProvider an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding
+ * provider
+ */
+ public GMSSRootCalc(int heightOfTree, int K, GMSSDigestProvider digestProvider)
+ {
+ this.heightOfTree = heightOfTree;
+ this.digestProvider = digestProvider;
+ this.messDigestTree = digestProvider.get();
+ this.mdLength = messDigestTree.getDigestSize();
+ this.K = K;
+ this.index = new int[heightOfTree];
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ this.root = new byte[mdLength];
+ // this.treehash = new Treehash[this.heightOfTree - this.K];
+ this.retain = new Vector[this.K - 1];
+ for (int i = 0; i < K - 1; i++)
+ {
+ this.retain[i] = new Vector();
+ }
+
+ }
+
+ /**
+ * Initializes the calculation of a new root
+ *
+ * @param sharedStack the stack shared by all treehash instances of this tree
+ */
+ public void initialize(Vector sharedStack)
+ {
+ this.treehash = new Treehash[this.heightOfTree - this.K];
+ for (int i = 0; i < this.heightOfTree - this.K; i++)
+ {
+ this.treehash[i] = new Treehash(sharedStack, i, this.digestProvider.get());
+ }
+
+ this.index = new int[heightOfTree];
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ this.root = new byte[mdLength];
+
+ this.tailStack = new Vector();
+ this.heightOfNodes = new Vector();
+ this.isInitialized = true;
+ this.isFinished = false;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.index[i] = -1;
+ }
+
+ this.retain = new Vector[this.K - 1];
+ for (int i = 0; i < K - 1; i++)
+ {
+ this.retain[i] = new Vector();
+ }
+
+ this.indexForNextSeed = 3;
+ this.heightOfNextSeed = 0;
+ }
+
+ /**
+ * updates the root with one leaf and stores needed values in retain,
+ * treehash or authpath. Additionally counts the seeds used. This method is
+ * used when performing the updates for TREE++.
+ *
+ * @param seed the initial seed for treehash: seedNext
+ * @param leaf the height of the treehash
+ */
+ public void update(byte[] seed, byte[] leaf)
+ {
+ if (this.heightOfNextSeed < (this.heightOfTree - this.K)
+ && this.indexForNextSeed - 2 == index[0])
+ {
+ this.initializeTreehashSeed(seed, this.heightOfNextSeed);
+ this.heightOfNextSeed++;
+ this.indexForNextSeed *= 2;
+ }
+ // now call the simple update
+ this.update(leaf);
+ }
+
+ /**
+ * Updates the root with one leaf and stores the needed values in retain,
+ * treehash or authpath
+ */
+ public void update(byte[] leaf)
+ {
+
+ if (isFinished)
+ {
+ System.out.print("Too much updates for Tree!!");
+ return;
+ }
+ if (!isInitialized)
+ {
+ System.err.println("GMSSRootCalc not initialized!");
+ return;
+ }
+
+ // a new leaf was omitted, so raise index on lowest layer
+ index[0]++;
+
+ // store the nodes on the lowest layer in treehash or authpath
+ if (index[0] == 1)
+ {
+ System.arraycopy(leaf, 0, AuthPath[0], 0, mdLength);
+ }
+ else if (index[0] == 3)
+ {
+ // store in treehash only if K < H
+ if (heightOfTree > K)
+ {
+ treehash[0].setFirstNode(leaf);
+ }
+ }
+
+ if ((index[0] - 3) % 2 == 0 && index[0] >= 3)
+ {
+ // store in retain if K = H
+ if (heightOfTree == K)
+ // TODO: check it
+ {
+ retain[0].insertElementAt(leaf, 0);
+ }
+ }
+
+ // if first update to this tree is made
+ if (index[0] == 0)
+ {
+ tailStack.addElement(leaf);
+ heightOfNodes.addElement(Integers.valueOf(0));
+ }
+ else
+ {
+
+ byte[] help = new byte[mdLength];
+ byte[] toBeHashed = new byte[mdLength << 1];
+
+ // store the new leaf in help
+ System.arraycopy(leaf, 0, help, 0, mdLength);
+ int helpHeight = 0;
+ // while top to nodes have same height
+ while (tailStack.size() > 0
+ && helpHeight == ((Integer)heightOfNodes.lastElement())
+ .intValue())
+ {
+
+ // help <-- hash(stack top element || help)
+ System.arraycopy(tailStack.lastElement(), 0, toBeHashed, 0,
+ mdLength);
+ tailStack.removeElementAt(tailStack.size() - 1);
+ heightOfNodes.removeElementAt(heightOfNodes.size() - 1);
+ System.arraycopy(help, 0, toBeHashed, mdLength, mdLength);
+
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ help = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(help, 0);
+
+ // the new help node is one step higher
+ helpHeight++;
+ if (helpHeight < heightOfTree)
+ {
+ index[helpHeight]++;
+
+ // add index 1 element to initial authpath
+ if (index[helpHeight] == 1)
+ {
+ System.arraycopy(help, 0, AuthPath[helpHeight], 0,
+ mdLength);
+ }
+
+ if (helpHeight >= heightOfTree - K)
+ {
+ if (helpHeight == 0)
+ {
+ System.out.println("MïżœïżœïżœP");
+ }
+ // add help element to retain stack if it is a right
+ // node
+ // and not stored in treehash
+ if ((index[helpHeight] - 3) % 2 == 0
+ && index[helpHeight] >= 3)
+ // TODO: check it
+ {
+ retain[helpHeight - (heightOfTree - K)]
+ .insertElementAt(help, 0);
+ }
+ }
+ else
+ {
+ // if element is third in his line add it to treehash
+ if (index[helpHeight] == 3)
+ {
+ treehash[helpHeight].setFirstNode(help);
+ }
+ }
+ }
+ }
+ // push help element to the stack
+ tailStack.addElement(help);
+ heightOfNodes.addElement(Integers.valueOf(helpHeight));
+
+ // is the root calculation finished?
+ if (helpHeight == heightOfTree)
+ {
+ isFinished = true;
+ isInitialized = false;
+ root = (byte[])tailStack.lastElement();
+ }
+ }
+
+ }
+
+ /**
+ * initializes the seeds for the treehashs of the tree precomputed by this
+ * class
+ *
+ * @param seed the initial seed for treehash: seedNext
+ * @param index the height of the treehash
+ */
+ public void initializeTreehashSeed(byte[] seed, int index)
+ {
+ treehash[index].initializeSeed(seed);
+ }
+
+ /**
+ * Method to check whether the instance has been initialized or not
+ *
+ * @return true if treehash was already initialized
+ */
+ public boolean wasInitialized()
+ {
+ return isInitialized;
+ }
+
+ /**
+ * Method to check whether the instance has been finished or not
+ *
+ * @return true if tree has reached its maximum height
+ */
+ public boolean wasFinished()
+ {
+ return isFinished;
+ }
+
+ /**
+ * returns the authentication path of the first leaf of the tree
+ *
+ * @return the authentication path of the first leaf of the tree
+ */
+ public byte[][] getAuthPath()
+ {
+ return GMSSUtils.clone(AuthPath);
+ }
+
+ /**
+ * returns the initial treehash instances, storing value y_3(i)
+ *
+ * @return the initial treehash instances, storing value y_3(i)
+ */
+ public Treehash[] getTreehash()
+ {
+ return GMSSUtils.clone(treehash);
+ }
+
+ /**
+ * returns the retain stacks storing all right nodes near to the root
+ *
+ * @return the retain stacks storing all right nodes near to the root
+ */
+ public Vector[] getRetain()
+ {
+ return GMSSUtils.clone(retain);
+ }
+
+ /**
+ * returns the finished root value
+ *
+ * @return the finished root value
+ */
+ public byte[] getRoot()
+ {
+ return Arrays.clone(root);
+ }
+
+ /**
+ * returns the shared stack
+ *
+ * @return the shared stack
+ */
+ public Vector getStack()
+ {
+ Vector copy = new Vector();
+ for (Enumeration en = tailStack.elements(); en.hasMoreElements();)
+ {
+ copy.addElement(en.nextElement());
+ }
+ return copy;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+ byte[][] statByte = new byte[1 + heightOfTree + tailLength][64]; //FIXME: messDigestTree.getByteLength()
+ statByte[0] = root;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ statByte[1 + i] = AuthPath[i];
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statByte[1 + heightOfTree + i] = (byte[])tailStack.elementAt(i);
+ }
+
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+ int[] statInt = new int[8 + heightOfTree + tailLength];
+ statInt[0] = heightOfTree;
+ statInt[1] = mdLength;
+ statInt[2] = K;
+ statInt[3] = indexForNextSeed;
+ statInt[4] = heightOfNextSeed;
+ if (isFinished)
+ {
+ statInt[5] = 1;
+ }
+ else
+ {
+ statInt[5] = 0;
+ }
+ if (isInitialized)
+ {
+ statInt[6] = 1;
+ }
+ else
+ {
+ statInt[6] = 0;
+ }
+ statInt[7] = tailLength;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ statInt[8 + i] = index[i];
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statInt[8 + heightOfTree + i] = ((Integer)heightOfNodes
+ .elementAt(i)).intValue();
+ }
+
+ return statInt;
+ }
+
+ /**
+ * @return a human readable version of the structure
+ */
+ public String toString()
+ {
+ String out = "";
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+
+ for (int i = 0; i < 8 + heightOfTree + tailLength; i++)
+ {
+ out = out + getStatInt()[i] + " ";
+ }
+ for (int i = 0; i < 1 + heightOfTree + tailLength; i++)
+ {
+ out = out + new String(Hex.encode(getStatByte()[i])) + " ";
+ }
+ out = out + " " + digestProvider.get().getDigestSize();
+ return out;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java
new file mode 100644
index 000000000..f08529cfd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java
@@ -0,0 +1,666 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements the distributed signature generation of the Winternitz
+ * one-time signature scheme (OTSS), described in C.Dods, N.P. Smart, and M.
+ * Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages 96–115,
+ * 2005. The class is used by the GMSS classes.
+ */
+public class GMSSRootSig
+{
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * The private key
+ */
+ private byte[] privateKeyOTS;
+
+ /**
+ * The message bytes
+ */
+ private byte[] hash;
+
+ /**
+ * The signature bytes
+ */
+ private byte[] sign;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Sizes of the message
+ */
+ private int messagesize;
+
+ /**
+ * Some precalculated values
+ */
+ private int k;
+
+ /**
+ * Some variables for storing the actual status of distributed signing
+ */
+ private int r, test, counter, ii;
+
+ /**
+ * variables for storing big numbers for the actual status of distributed
+ * signing
+ */
+ private long test8, big8;
+
+ /**
+ * The necessary steps of each updateSign() call
+ */
+ private int steps;
+
+ /**
+ * The checksum part
+ */
+ private int checksum;
+
+ /**
+ * The height of the tree
+ */
+ private int height;
+
+ /**
+ * The current intern OTSseed
+ */
+ private byte[] seed;
+
+ /**
+ * This constructor regenerates a prior GMSSRootSig object used by the
+ * GMSSPrivateKeyASN.1 class
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function, the digest of the PRGN and the names of the
+ * corresponding providers
+ * @param statByte status byte array
+ * @param statInt status int array
+ */
+ public GMSSRootSig(Digest digest, byte[][] statByte, int[] statInt)
+ {
+ messDigestOTS = digest;
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ this.counter = statInt[0];
+ this.test = statInt[1];
+ this.ii = statInt[2];
+ this.r = statInt[3];
+ this.steps = statInt[4];
+ this.keysize = statInt[5];
+ this.height = statInt[6];
+ this.w = statInt[7];
+ this.checksum = statInt[8];
+
+ this.mdsize = messDigestOTS.getDigestSize();
+
+ this.k = (1 << w) - 1;
+
+ int mdsizeBit = mdsize << 3;
+ this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+
+ this.privateKeyOTS = statByte[0];
+ this.seed = statByte[1];
+ this.hash = statByte[2];
+
+ this.sign = statByte[3];
+
+ this.test8 = ((statByte[4][0] & 0xff))
+ | ((long)(statByte[4][1] & 0xff) << 8)
+ | ((long)(statByte[4][2] & 0xff) << 16)
+ | ((long)(statByte[4][3] & 0xff)) << 24
+ | ((long)(statByte[4][4] & 0xff)) << 32
+ | ((long)(statByte[4][5] & 0xff)) << 40
+ | ((long)(statByte[4][6] & 0xff)) << 48
+ | ((long)(statByte[4][7] & 0xff)) << 56;
+
+ this.big8 = ((statByte[4][8] & 0xff))
+ | ((long)(statByte[4][9] & 0xff) << 8)
+ | ((long)(statByte[4][10] & 0xff) << 16)
+ | ((long)(statByte[4][11] & 0xff)) << 24
+ | ((long)(statByte[4][12] & 0xff)) << 32
+ | ((long)(statByte[4][13] & 0xff)) << 40
+ | ((long)(statByte[4][14] & 0xff)) << 48
+ | ((long)(statByte[4][15] & 0xff)) << 56;
+ }
+
+ /**
+ * The constructor generates the PRNG and initializes some variables
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function, the digest of the PRGN and the names of the
+ * corresponding providers
+ * @param w the winternitz parameter
+ * @param height the heigth of the tree
+ */
+ public GMSSRootSig(Digest digest, int w, int height)
+ {
+ messDigestOTS = digest;
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ this.mdsize = messDigestOTS.getDigestSize();
+ this.w = w;
+ this.height = height;
+
+ this.k = (1 << w) - 1;
+
+ int mdsizeBit = mdsize << 3;
+ this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ }
+
+ /**
+ * This method initializes the distributed sigature calculation. Variables
+ * are reseted and necessary steps are calculated
+ *
+ * @param seed0 the initial OTSseed
+ * @param message the massage which will be signed
+ */
+ public void initSign(byte[] seed0, byte[] message)
+ {
+
+ // create hash of message m
+ this.hash = new byte[mdsize];
+ messDigestOTS.update(message, 0, message.length);
+ this.hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(this.hash, 0);
+
+ // variables for calculation of steps
+ byte[] messPart = new byte[mdsize];
+ System.arraycopy(hash, 0, messPart, 0, mdsize);
+ int checkPart = 0;
+ int sumH = 0;
+ int checksumsize = getLog((messagesize << w) + 1);
+
+ // ------- calculation of necessary steps ------
+ if (8 % w == 0)
+ {
+ int dt = 8 / w;
+ // message part
+ for (int a = 0; a < mdsize; a++)
+ {
+ // count necessary hashs in 'sumH'
+ for (int b = 0; b < dt; b++)
+ {
+ sumH += messPart[a] & k;
+ messPart[a] = (byte)(messPart[a] >>> w);
+ }
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int b = 0; b < checksumsize; b += w)
+ {
+ sumH += checkPart & k;
+ checkPart >>>= w;
+ }
+ } // end if ( 8 % w == 0 )
+ else if (w < 8)
+ {
+ long big8;
+ int ii = 0;
+ int dt = mdsize / w;
+
+ // first d*w bytes of hash (main message part)
+ for (int i = 0; i < dt; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (messPart[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ // count necessary hashs in 'sumH'
+ for (int j = 0; j < 8; j++)
+ {
+ sumH += (int)(big8 & k);
+ big8 >>>= w;
+ }
+ }
+ // rest of message part
+ dt = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < dt; j++)
+ {
+ big8 ^= (messPart[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ dt <<= 3;
+ // count necessary hashs in 'sumH'
+ for (int j = 0; j < dt; j += w)
+ {
+ sumH += (int)(big8 & k);
+ big8 >>>= w;
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ sumH += checkPart & k;
+ checkPart >>>= w;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ long big8;
+ int r = 0;
+ int s, f, rest, ii;
+
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w (main
+ // message part)
+ while (r <= ((mdsize << 3) - w))
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (messPart[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+ big8 >>>= rest;
+ // count necessary hashs in 'sumH'
+ sumH += (big8 & k);
+
+ }
+ // rest of message part
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (messPart[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ // count necessary hashs in 'sumH'
+ sumH += (big8 & k);
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ sumH += (checkPart & k);
+ checkPart >>>= w;
+ }
+ }// end if(w<57)
+
+ // calculate keysize
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+
+ // calculate steps: 'keysize' times PRNG, 'sumH' times hashing,
+ // (1<intValue
.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of intValue
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * This method returns the status byte array
+ *
+ * @return statBytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[5][mdsize];
+ statByte[0] = privateKeyOTS;
+ statByte[1] = seed;
+ statByte[2] = hash;
+ statByte[3] = sign;
+ statByte[4] = this.getStatLong();
+
+ return statByte;
+ }
+
+ /**
+ * This method returns the status int array
+ *
+ * @return statInt
+ */
+ public int[] getStatInt()
+ {
+ int[] statInt = new int[9];
+ statInt[0] = counter;
+ statInt[1] = test;
+ statInt[2] = ii;
+ statInt[3] = r;
+ statInt[4] = steps;
+ statInt[5] = keysize;
+ statInt[6] = height;
+ statInt[7] = w;
+ statInt[8] = checksum;
+ return statInt;
+ }
+
+ /**
+ * Converts the long parameters into byte arrays to store it in
+ * statByte-Array
+ */
+ public byte[] getStatLong()
+ {
+ byte[] bytes = new byte[16];
+
+ bytes[0] = (byte)((test8) & 0xff);
+ bytes[1] = (byte)((test8 >> 8) & 0xff);
+ bytes[2] = (byte)((test8 >> 16) & 0xff);
+ bytes[3] = (byte)((test8 >> 24) & 0xff);
+ bytes[4] = (byte)((test8) >> 32 & 0xff);
+ bytes[5] = (byte)((test8 >> 40) & 0xff);
+ bytes[6] = (byte)((test8 >> 48) & 0xff);
+ bytes[7] = (byte)((test8 >> 56) & 0xff);
+
+ bytes[8] = (byte)((big8) & 0xff);
+ bytes[9] = (byte)((big8 >> 8) & 0xff);
+ bytes[10] = (byte)((big8 >> 16) & 0xff);
+ bytes[11] = (byte)((big8 >> 24) & 0xff);
+ bytes[12] = (byte)((big8) >> 32 & 0xff);
+ bytes[13] = (byte)((big8 >> 40) & 0xff);
+ bytes[14] = (byte)((big8 >> 48) & 0xff);
+ bytes[15] = (byte)((big8 >> 56) & 0xff);
+
+ return bytes;
+ }
+
+ /**
+ * returns a string representation of the instance
+ *
+ * @return a string representation of the instance
+ */
+ public String toString()
+ {
+ String out = "" + this.big8 + " ";
+ int[] statInt = new int[9];
+ statInt = this.getStatInt();
+ byte[][] statByte = new byte[5][mdsize];
+ statByte = this.getStatByte();
+ for (int i = 0; i < 9; i++)
+ {
+ out = out + statInt[i] + " ";
+ }
+ for (int i = 0; i < 5; i++)
+ {
+ out = out + new String(Hex.encode(statByte[i])) + " ";
+ }
+
+ return out;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java
new file mode 100644
index 000000000..0bb04fa0c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java
@@ -0,0 +1,404 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageSigner;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSUtil;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSVerify;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+import org.spongycastle.util.Arrays;
+
+/**
+ * This class implements the GMSS signature scheme.
+ */
+public class GMSSSigner
+ implements MessageSigner
+{
+
+ /**
+ * Instance of GMSSParameterSpec
+ */
+ //private GMSSParameterSpec gmssParameterSpec;
+
+ /**
+ * Instance of GMSSUtilities
+ */
+ private GMSSUtil gmssUtil = new GMSSUtil();
+
+
+ /**
+ * The raw GMSS public key
+ */
+ private byte[] pubKeyBytes;
+
+ /**
+ * Hash function for the construction of the authentication trees
+ */
+ private Digest messDigestTrees;
+
+ /**
+ * The length of the hash function output
+ */
+ private int mdLength;
+
+ /**
+ * The number of tree layers
+ */
+ private int numLayer;
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * An instance of the Winternitz one-time signature
+ */
+ private WinternitzOTSignature ots;
+
+ /**
+ * Array of strings containing the name of the hash function used by the OTS
+ * and the corresponding provider name
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * The current main tree and subtree indices
+ */
+ private int[] index;
+
+ /**
+ * Array of the authentication paths for the current trees of all layers
+ */
+ private byte[][][] currentAuthPaths;
+
+ /**
+ * The one-time signature of the roots of the current subtrees
+ */
+ private byte[][] subtreeRootSig;
+
+
+ /**
+ * The GMSSParameterset
+ */
+ private GMSSParameters gmssPS;
+
+ /**
+ * The PRNG
+ */
+ private GMSSRandom gmssRandom;
+
+ GMSSKeyParameters key;
+
+ // XXX needed? Source of randomness
+ private SecureRandom random;
+
+
+ /**
+ * The standard constructor tries to generate the MerkleTree Algorithm
+ * identifier with the corresponding OID.
+ *
+ * @param digest the digest to use
+ */
+ // TODO
+ public GMSSSigner(GMSSDigestProvider digest)
+ {
+ digestProvider = digest;
+ messDigestTrees = digest.get();
+ messDigestOTS = messDigestTrees;
+ mdLength = messDigestTrees.getDigestSize();
+ gmssRandom = new GMSSRandom(messDigestTrees);
+ }
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ // XXX random needed?
+ this.random = rParam.getRandom();
+ this.key = (GMSSPrivateKeyParameters)rParam.getParameters();
+ initSign();
+
+ }
+ else
+ {
+
+ this.random = new SecureRandom();
+ this.key = (GMSSPrivateKeyParameters)param;
+ initSign();
+ }
+ }
+ else
+ {
+ this.key = (GMSSPublicKeyParameters)param;
+ initVerify();
+
+ }
+
+ }
+
+
+ /**
+ * Initializes the signature algorithm for signing a message.
+ */
+ private void initSign()
+ {
+ messDigestTrees.reset();
+ // set private key and take from it ots key, auth, tree and key
+ // counter, rootSign
+ GMSSPrivateKeyParameters gmssPrivateKey = (GMSSPrivateKeyParameters)key;
+
+ if (gmssPrivateKey.isUsed())
+ {
+ throw new IllegalStateException("Private key already used");
+ }
+
+ // check if last signature has been generated
+ if (gmssPrivateKey.getIndex(0) >= gmssPrivateKey.getNumLeafs(0))
+ {
+ throw new IllegalStateException("No more signatures can be generated");
+ }
+
+ // get Parameterset
+ this.gmssPS = gmssPrivateKey.getParameters();
+ // get numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+ // get OTS Instance of lowest layer
+ byte[] seed = gmssPrivateKey.getCurrentSeeds()[numLayer - 1];
+ byte[] OTSSeed = new byte[mdLength];
+ byte[] dummy = new byte[mdLength];
+ System.arraycopy(seed, 0, dummy, 0, mdLength);
+ OTSSeed = gmssRandom.nextSeed(dummy); // secureRandom.nextBytes(currentSeeds[currentSeeds.length-1]);secureRandom.nextBytes(OTSseed);
+ this.ots = new WinternitzOTSignature(OTSSeed, digestProvider.get(), gmssPS.getWinternitzParameter()[numLayer - 1]);
+
+ byte[][][] helpCurrentAuthPaths = gmssPrivateKey.getCurrentAuthPaths();
+ currentAuthPaths = new byte[numLayer][][];
+
+ // copy the main tree authentication path
+ for (int j = 0; j < numLayer; j++)
+ {
+ currentAuthPaths[j] = new byte[helpCurrentAuthPaths[j].length][mdLength];
+ for (int i = 0; i < helpCurrentAuthPaths[j].length; i++)
+ {
+ System.arraycopy(helpCurrentAuthPaths[j][i], 0, currentAuthPaths[j][i], 0, mdLength);
+ }
+ }
+
+ // copy index
+ index = new int[numLayer];
+ System.arraycopy(gmssPrivateKey.getIndex(), 0, index, 0, numLayer);
+
+ // copy subtreeRootSig
+ byte[] helpSubtreeRootSig;
+ subtreeRootSig = new byte[numLayer - 1][];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ helpSubtreeRootSig = gmssPrivateKey.getSubtreeRootSig(i);
+ subtreeRootSig[i] = new byte[helpSubtreeRootSig.length];
+ System.arraycopy(helpSubtreeRootSig, 0, subtreeRootSig[i], 0, helpSubtreeRootSig.length);
+ }
+
+ gmssPrivateKey.markUsed();
+ }
+
+ /**
+ * Signs a message.
+ *
+ *
+ * @return the signature.
+ */
+ public byte[] generateSignature(byte[] message)
+ {
+
+ byte[] otsSig = new byte[mdLength];
+ byte[] authPathBytes;
+ byte[] indexBytes;
+
+ otsSig = ots.getSignature(message);
+
+ // get concatenated lowest layer tree authentication path
+ authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[numLayer - 1]);
+
+ // put lowest layer index into a byte array
+ indexBytes = gmssUtil.intToBytesLittleEndian(index[numLayer - 1]);
+
+ // create first part of GMSS signature
+ byte[] gmssSigFirstPart = new byte[indexBytes.length + otsSig.length + authPathBytes.length];
+ System.arraycopy(indexBytes, 0, gmssSigFirstPart, 0, indexBytes.length);
+ System.arraycopy(otsSig, 0, gmssSigFirstPart, indexBytes.length, otsSig.length);
+ System.arraycopy(authPathBytes, 0, gmssSigFirstPart, (indexBytes.length + otsSig.length), authPathBytes.length);
+ // --- end first part
+
+ // --- next parts of the signature
+ // create initial array with length 0 for iteration
+ byte[] gmssSigNextPart = new byte[0];
+
+ for (int i = numLayer - 1 - 1; i >= 0; i--)
+ {
+
+ // get concatenated next tree authentication path
+ authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[i]);
+
+ // put next tree index into a byte array
+ indexBytes = gmssUtil.intToBytesLittleEndian(index[i]);
+
+ // create next part of GMSS signature
+
+ // create help array and copy actual gmssSig into it
+ byte[] helpGmssSig = new byte[gmssSigNextPart.length];
+ System.arraycopy(gmssSigNextPart, 0, helpGmssSig, 0, gmssSigNextPart.length);
+ // adjust length of gmssSigNextPart for adding next part
+ gmssSigNextPart = new byte[helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length + authPathBytes.length];
+
+ // copy old data (help array) and new data in gmssSigNextPart
+ System.arraycopy(helpGmssSig, 0, gmssSigNextPart, 0, helpGmssSig.length);
+ System.arraycopy(indexBytes, 0, gmssSigNextPart, helpGmssSig.length, indexBytes.length);
+ System.arraycopy(subtreeRootSig[i], 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length), subtreeRootSig[i].length);
+ System.arraycopy(authPathBytes, 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length), authPathBytes.length);
+
+ }
+ // --- end next parts
+
+ // concatenate the two parts of the GMSS signature
+ byte[] gmssSig = new byte[gmssSigFirstPart.length + gmssSigNextPart.length];
+ System.arraycopy(gmssSigFirstPart, 0, gmssSig, 0, gmssSigFirstPart.length);
+ System.arraycopy(gmssSigNextPart, 0, gmssSig, gmssSigFirstPart.length, gmssSigNextPart.length);
+
+ // return the GMSS signature
+ return gmssSig;
+ }
+
+ /**
+ * Initializes the signature algorithm for verifying a signature.
+ */
+ private void initVerify()
+ {
+ messDigestTrees.reset();
+
+ GMSSPublicKeyParameters gmssPublicKey = (GMSSPublicKeyParameters)key;
+ pubKeyBytes = gmssPublicKey.getPublicKey();
+ gmssPS = gmssPublicKey.getParameters();
+ // get numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param message the message
+ * @param signature the signature associated with the message
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verifySignature(byte[] message, byte[] signature)
+ {
+
+ boolean success = false;
+ // int halfSigLength = signature.length >>> 1;
+ messDigestOTS.reset();
+ WinternitzOTSVerify otsVerify;
+ int otsSigLength;
+
+ byte[] help = message;
+
+ byte[] otsSig;
+ byte[] otsPublicKey;
+ byte[][] authPath;
+ byte[] dest;
+ int nextEntry = 0;
+ int index;
+ // Verify signature
+
+ // --- begin with message = 'message that was signed'
+ // and then in each step message = subtree root
+ for (int j = numLayer - 1; j >= 0; j--)
+ {
+ otsVerify = new WinternitzOTSVerify(digestProvider.get(), gmssPS.getWinternitzParameter()[j]);
+ otsSigLength = otsVerify.getSignatureLength();
+
+ message = help;
+ // get the subtree index
+ index = gmssUtil.bytesToIntLittleEndian(signature, nextEntry);
+
+ // 4 is the number of bytes in integer
+ nextEntry += 4;
+
+ // get one-time signature
+ otsSig = new byte[otsSigLength];
+ System.arraycopy(signature, nextEntry, otsSig, 0, otsSigLength);
+ nextEntry += otsSigLength;
+
+ // compute public OTS key from the one-time signature
+ otsPublicKey = otsVerify.Verify(message, otsSig);
+
+ // test if OTSsignature is correct
+ if (otsPublicKey == null)
+ {
+ System.err.println("OTS Public Key is null in GMSSSignature.verify");
+ return false;
+ }
+
+ // get authentication path from the signature
+ authPath = new byte[gmssPS.getHeightOfTrees()[j]][mdLength];
+ for (int i = 0; i < authPath.length; i++)
+ {
+ System.arraycopy(signature, nextEntry, authPath[i], 0, mdLength);
+ nextEntry = nextEntry + mdLength;
+ }
+
+ // compute the root of the subtree from the authentication path
+ help = new byte[mdLength];
+
+ help = otsPublicKey;
+
+ int count = 1 << authPath.length;
+ count = count + index;
+
+ for (int i = 0; i < authPath.length; i++)
+ {
+ dest = new byte[mdLength << 1];
+
+ if ((count % 2) == 0)
+ {
+ System.arraycopy(help, 0, dest, 0, mdLength);
+ System.arraycopy(authPath[i], 0, dest, mdLength, mdLength);
+ count = count / 2;
+ }
+ else
+ {
+ System.arraycopy(authPath[i], 0, dest, 0, mdLength);
+ System.arraycopy(help, 0, dest, mdLength, help.length);
+ count = (count - 1) / 2;
+ }
+ messDigestTrees.update(dest, 0, dest.length);
+ help = new byte[messDigestTrees.getDigestSize()];
+ messDigestTrees.doFinal(help, 0);
+ }
+ }
+
+ // now help contains the root of the maintree
+
+ // test if help is equal to the GMSS public key
+ if (Arrays.areEqual(pubKeyBytes, help))
+ {
+ success = true;
+ }
+
+ return success;
+ }
+
+
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java
new file mode 100644
index 000000000..1f79bdde1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java
@@ -0,0 +1,145 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.util.Arrays;
+
+class GMSSUtils
+{
+ static GMSSLeaf[] clone(GMSSLeaf[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSLeaf[] copy = new GMSSLeaf[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static GMSSRootCalc[] clone(GMSSRootCalc[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSRootCalc[] copy = new GMSSRootCalc[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static GMSSRootSig[] clone(GMSSRootSig[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSRootSig[] copy = new GMSSRootSig[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static byte[][] clone(byte[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[][] copy = new byte[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = Arrays.clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static byte[][][] clone(byte[][][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[][][] copy = new byte[data.length][][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static Treehash[] clone(Treehash[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Treehash[] copy = new Treehash[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static Treehash[][] clone(Treehash[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Treehash[][] copy = new Treehash[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static Vector[] clone(Vector[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Vector[] copy = new Vector[data.length];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = new Vector();
+ for (Enumeration en = data[i].elements(); en.hasMoreElements();)
+ {
+ copy[i].addElement(en.nextElement());
+ }
+ }
+
+ return copy;
+ }
+
+ static Vector[][] clone(Vector[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Vector[][] copy = new Vector[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java
new file mode 100644
index 000000000..82784e349
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java
@@ -0,0 +1,525 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Vector;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements a treehash instance for the Merkle tree traversal
+ * algorithm. The first node of the stack is stored in this instance itself,
+ * additional tail nodes are stored on a tailstack.
+ */
+public class Treehash
+{
+
+ /**
+ * max height of current treehash instance.
+ */
+ private int maxHeight;
+
+ /**
+ * Vector element that stores the nodes on the stack
+ */
+ private Vector tailStack;
+
+ /**
+ * Vector element that stores the height of the nodes on the stack
+ */
+ private Vector heightOfNodes;
+
+ /**
+ * the first node is stored in the treehash instance itself, not on stack
+ */
+ private byte[] firstNode;
+
+ /**
+ * seedActive needed for the actual node
+ */
+ private byte[] seedActive;
+
+ /**
+ * the seed needed for the next re-initialization of the treehash instance
+ */
+ private byte[] seedNext;
+
+ /**
+ * number of nodes stored on the stack and belonging to this treehash
+ * instance
+ */
+ private int tailLength;
+
+ /**
+ * the height in the tree of the first node stored in treehash
+ */
+ private int firstNodeHeight;
+
+ /**
+ * true if treehash instance was already initialized, false otherwise
+ */
+ private boolean isInitialized;
+
+ /**
+ * true if the first node's height equals the maxHeight of the treehash
+ */
+ private boolean isFinished;
+
+ /**
+ * true if the nextSeed has been initialized with index 3*2^h needed for the
+ * seed scheduling
+ */
+ private boolean seedInitialized;
+
+ /**
+ * denotes the Message Digest used by the tree to create nodes
+ */
+ private Digest messDigestTree;
+
+ /**
+ * This constructor regenerates a prior treehash object
+ *
+ * @param name an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding provider
+ * @param statByte status bytes
+ * @param statInt status ints
+ */
+ public Treehash(Digest name, byte[][] statByte, int[] statInt)
+ {
+ this.messDigestTree = name;
+
+ // decode statInt
+ this.maxHeight = statInt[0];
+ this.tailLength = statInt[1];
+ this.firstNodeHeight = statInt[2];
+
+ if (statInt[3] == 1)
+ {
+ this.isFinished = true;
+ }
+ else
+ {
+ this.isFinished = false;
+ }
+ if (statInt[4] == 1)
+ {
+ this.isInitialized = true;
+ }
+ else
+ {
+ this.isInitialized = false;
+ }
+ if (statInt[5] == 1)
+ {
+ this.seedInitialized = true;
+ }
+ else
+ {
+ this.seedInitialized = false;
+ }
+
+ this.heightOfNodes = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.heightOfNodes.addElement(Integers.valueOf(statInt[6 + i]));
+ }
+
+ // decode statByte
+ this.firstNode = statByte[0];
+ this.seedActive = statByte[1];
+ this.seedNext = statByte[2];
+
+ this.tailStack = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.tailStack.addElement(statByte[3 + i]);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param tailStack a vector element where the stack nodes are stored
+ * @param maxHeight maximal height of the treehash instance
+ * @param digest an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding provider
+ */
+ public Treehash(Vector tailStack, int maxHeight, Digest digest)
+ {
+ this.tailStack = tailStack;
+ this.maxHeight = maxHeight;
+ this.firstNode = null;
+ this.isInitialized = false;
+ this.isFinished = false;
+ this.seedInitialized = false;
+ this.messDigestTree = digest;
+
+ this.seedNext = new byte[messDigestTree.getDigestSize()];
+ this.seedActive = new byte[messDigestTree.getDigestSize()];
+ }
+
+ /**
+ * Method to initialize the seeds needed for the precomputation of right
+ * nodes. Should be initialized with index 3*2^i for treehash_i
+ *
+ * @param seedIn
+ */
+ public void initializeSeed(byte[] seedIn)
+ {
+ System.arraycopy(seedIn, 0, this.seedNext, 0, this.messDigestTree
+ .getDigestSize());
+ this.seedInitialized = true;
+ }
+
+ /**
+ * initializes the treehash instance. The seeds must already have been
+ * initialized to work correctly.
+ */
+ public void initialize()
+ {
+ if (!this.seedInitialized)
+ {
+ System.err.println("Seed " + this.maxHeight + " not initialized");
+ return;
+ }
+
+ this.heightOfNodes = new Vector();
+ this.tailLength = 0;
+ this.firstNode = null;
+ this.firstNodeHeight = -1;
+ this.isInitialized = true;
+ System.arraycopy(this.seedNext, 0, this.seedActive, 0, messDigestTree
+ .getDigestSize());
+ }
+
+ /**
+ * Calculates one update of the treehash instance, i.e. creates a new leaf
+ * and hashes if possible
+ *
+ * @param gmssRandom an instance of the PRNG
+ * @param leaf The byte value of the leaf needed for the update
+ */
+ public void update(GMSSRandom gmssRandom, byte[] leaf)
+ {
+
+ if (this.isFinished)
+ {
+ System.err
+ .println("No more update possible for treehash instance!");
+ return;
+ }
+ if (!this.isInitialized)
+ {
+ System.err
+ .println("Treehash instance not initialized before update");
+ return;
+ }
+
+ byte[] help = new byte[this.messDigestTree.getDigestSize()];
+ int helpHeight = -1;
+
+ gmssRandom.nextSeed(this.seedActive);
+
+ // if treehash gets first update
+ if (this.firstNode == null)
+ {
+ this.firstNode = leaf;
+ this.firstNodeHeight = 0;
+ }
+ else
+ {
+ // store the new node in help array, do not push it on the stack
+ help = leaf;
+ helpHeight = 0;
+
+ // hash the nodes on the stack if possible
+ while (this.tailLength > 0
+ && helpHeight == ((Integer)heightOfNodes.lastElement())
+ .intValue())
+ {
+ // put top element of the stack and help node in array
+ // 'tobehashed'
+ // and hash them together, put result again in help array
+ byte[] toBeHashed = new byte[this.messDigestTree
+ .getDigestSize() << 1];
+
+ // pop element from stack
+ System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed,
+ 0, this.messDigestTree.getDigestSize());
+ this.tailStack.removeElementAt(this.tailStack.size() - 1);
+ this.heightOfNodes
+ .removeElementAt(this.heightOfNodes.size() - 1);
+
+ System.arraycopy(help, 0, toBeHashed, this.messDigestTree
+ .getDigestSize(), this.messDigestTree
+ .getDigestSize());
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ help = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(help, 0);
+
+ // increase help height, stack was reduced by one element
+ helpHeight++;
+ this.tailLength--;
+ }
+
+ // push the new node on the stack
+ this.tailStack.addElement(help);
+ this.heightOfNodes.addElement(Integers.valueOf(helpHeight));
+ this.tailLength++;
+
+ // finally check whether the top node on stack and the first node
+ // in treehash have same height. If so hash them together
+ // and store them in treehash
+ if (((Integer)heightOfNodes.lastElement()).intValue() == this.firstNodeHeight)
+ {
+ byte[] toBeHashed = new byte[this.messDigestTree
+ .getDigestSize() << 1];
+ System.arraycopy(this.firstNode, 0, toBeHashed, 0,
+ this.messDigestTree.getDigestSize());
+
+ // pop element from tailStack and copy it into help2 array
+ System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed,
+ this.messDigestTree.getDigestSize(),
+ this.messDigestTree.getDigestSize());
+ this.tailStack.removeElementAt(this.tailStack.size() - 1);
+ this.heightOfNodes
+ .removeElementAt(this.heightOfNodes.size() - 1);
+
+ // store new element in firstNode, stack is then empty
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ this.firstNode = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(this.firstNode, 0);
+ this.firstNodeHeight++;
+
+ // empty the stack
+ this.tailLength = 0;
+ }
+ }
+
+ // check if treehash instance is completed
+ if (this.firstNodeHeight == this.maxHeight)
+ {
+ this.isFinished = true;
+ }
+ }
+
+ /**
+ * Destroys a treehash instance after the top node was taken for
+ * authentication path.
+ */
+ public void destroy()
+ {
+ this.isInitialized = false;
+ this.isFinished = false;
+ this.firstNode = null;
+ this.tailLength = 0;
+ this.firstNodeHeight = -1;
+ }
+
+ /**
+ * Returns the height of the lowest node stored either in treehash or on the
+ * stack. It must not be set to infinity (as mentioned in the paper) because
+ * this cases are considered in the computeAuthPaths method of
+ * JDKGMSSPrivateKey
+ *
+ * @return Height of the lowest node
+ */
+ public int getLowestNodeHeight()
+ {
+ if (this.firstNode == null)
+ {
+ return this.maxHeight;
+ }
+ else if (this.tailLength == 0)
+ {
+ return this.firstNodeHeight;
+ }
+ else
+ {
+ return Math.min(this.firstNodeHeight, ((Integer)heightOfNodes
+ .lastElement()).intValue());
+ }
+ }
+
+ /**
+ * Returns the top node height
+ *
+ * @return Height of the first node, the top node
+ */
+ public int getFirstNodeHeight()
+ {
+ if (firstNode == null)
+ {
+ return maxHeight;
+ }
+ return firstNodeHeight;
+ }
+
+ /**
+ * Method to check whether the instance has been initialized or not
+ *
+ * @return true if treehash was already initialized
+ */
+ public boolean wasInitialized()
+ {
+ return this.isInitialized;
+ }
+
+ /**
+ * Method to check whether the instance has been finished or not
+ *
+ * @return true if treehash has reached its maximum height
+ */
+ public boolean wasFinished()
+ {
+ return this.isFinished;
+ }
+
+ /**
+ * returns the first node stored in treehash instance itself
+ *
+ * @return the first node stored in treehash instance itself
+ */
+ public byte[] getFirstNode()
+ {
+ return this.firstNode;
+ }
+
+ /**
+ * returns the active seed
+ *
+ * @return the active seed
+ */
+ public byte[] getSeedActive()
+ {
+ return this.seedActive;
+ }
+
+ /**
+ * This method sets the first node stored in the treehash instance itself
+ *
+ * @param hash
+ */
+ public void setFirstNode(byte[] hash)
+ {
+ if (!this.isInitialized)
+ {
+ this.initialize();
+ }
+ this.firstNode = hash;
+ this.firstNodeHeight = this.maxHeight;
+ this.isFinished = true;
+ }
+
+ /**
+ * updates the nextSeed of this treehash instance one step needed for the
+ * schedulng of the seeds
+ *
+ * @param gmssRandom the prng used for the seeds
+ */
+ public void updateNextSeed(GMSSRandom gmssRandom)
+ {
+ gmssRandom.nextSeed(seedNext);
+ }
+
+ /**
+ * Returns the tailstack
+ *
+ * @return the tailstack
+ */
+ public Vector getTailStack()
+ {
+ return this.tailStack;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[3 + tailLength][this.messDigestTree
+ .getDigestSize()];
+ statByte[0] = firstNode;
+ statByte[1] = seedActive;
+ statByte[2] = seedNext;
+ for (int i = 0; i < tailLength; i++)
+ {
+ statByte[3 + i] = (byte[])tailStack.elementAt(i);
+ }
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = maxHeight;
+ statInt[1] = tailLength;
+ statInt[2] = firstNodeHeight;
+ if (this.isFinished)
+ {
+ statInt[3] = 1;
+ }
+ else
+ {
+ statInt[3] = 0;
+ }
+ if (this.isInitialized)
+ {
+ statInt[4] = 1;
+ }
+ else
+ {
+ statInt[4] = 0;
+ }
+ if (this.seedInitialized)
+ {
+ statInt[5] = 1;
+ }
+ else
+ {
+ statInt[5] = 0;
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statInt[6 + i] = ((Integer)heightOfNodes.elementAt(i)).intValue();
+ }
+ return statInt;
+ }
+
+ /**
+ * returns a String representation of the treehash instance
+ */
+ public String toString()
+ {
+ String out = "Treehash : ";
+ for (int i = 0; i < 6 + tailLength; i++)
+ {
+ out = out + this.getStatInt()[i] + " ";
+ }
+ for (int i = 0; i < 3 + tailLength; i++)
+ {
+ if (this.getStatByte()[i] != null)
+ {
+ out = out + new String(Hex.encode((this.getStatByte()[i]))) + " ";
+ }
+ else
+ {
+ out = out + "null ";
+ }
+ }
+ out = out + " " + this.messDigestTree.getDigestSize();
+ return out;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java
new file mode 100644
index 000000000..367aae0e2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java
@@ -0,0 +1,78 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * This class provides a PRNG for GMSS
+ */
+public class GMSSRandom
+{
+ /**
+ * Hash function for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * Constructor
+ *
+ * @param messDigestTree2
+ */
+ public GMSSRandom(Digest messDigestTree2)
+ {
+
+ this.messDigestTree = messDigestTree2;
+ }
+
+ /**
+ * computes the next seed value, returns a random byte array and sets
+ * outseed to the next value
+ *
+ * @param outseed byte array in which ((1 + SEEDin +RAND) mod 2^n) will be
+ * stored
+ * @return byte array of H(SEEDin)
+ */
+ public byte[] nextSeed(byte[] outseed)
+ {
+ // RAND <-- H(SEEDin)
+ byte[] rand = new byte[outseed.length];
+ messDigestTree.update(outseed, 0, outseed.length);
+ rand = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(rand, 0);
+
+ // SEEDout <-- (1 + SEEDin +RAND) mod 2^n
+ addByteArrays(outseed, rand);
+ addOne(outseed);
+
+ // System.arraycopy(outseed, 0, outseed, 0, outseed.length);
+
+ return rand;
+ }
+
+ private void addByteArrays(byte[] a, byte[] b)
+ {
+
+ byte overflow = 0;
+ int temp;
+
+ for (int i = 0; i < a.length; i++)
+ {
+ temp = (0xFF & a[i]) + (0xFF & b[i]) + overflow;
+ a[i] = (byte)temp;
+ overflow = (byte)(temp >> 8);
+ }
+ }
+
+ private void addOne(byte[] a)
+ {
+
+ byte overflow = 1;
+ int temp;
+
+ for (int i = 0; i < a.length; i++)
+ {
+ temp = (0xFF & a[i]) + overflow;
+ a[i] = (byte)temp;
+ overflow = (byte)(temp >> 8);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java
new file mode 100644
index 000000000..57678d854
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java
@@ -0,0 +1,151 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+/**
+ * This class provides several methods that are required by the GMSS classes.
+ */
+public class GMSSUtil
+{
+ /**
+ * Converts a 32 bit integer into a byte array beginning at
+ * offset
(little-endian representation)
+ *
+ * @param value the integer to convert
+ */
+ public byte[] intToBytesLittleEndian(int value)
+ {
+ byte[] bytes = new byte[4];
+
+ bytes[0] = (byte)((value) & 0xff);
+ bytes[1] = (byte)((value >> 8) & 0xff);
+ bytes[2] = (byte)((value >> 16) & 0xff);
+ bytes[3] = (byte)((value >> 24) & 0xff);
+ return bytes;
+ }
+
+ /**
+ * Converts a byte array beginning at offset
into a 32 bit
+ * integer (little-endian representation)
+ *
+ * @param bytes the byte array
+ * @return The resulting integer
+ */
+ public int bytesToIntLittleEndian(byte[] bytes)
+ {
+
+ return ((bytes[0] & 0xff)) | ((bytes[1] & 0xff) << 8)
+ | ((bytes[2] & 0xff) << 16) | ((bytes[3] & 0xff)) << 24;
+ }
+
+ /**
+ * Converts a byte array beginning at offset
into a 32 bit
+ * integer (little-endian representation)
+ *
+ * @param bytes the byte array
+ * @param offset the integer offset into the byte array
+ * @return The resulting integer
+ */
+ public int bytesToIntLittleEndian(byte[] bytes, int offset)
+ {
+ return ((bytes[offset++] & 0xff)) | ((bytes[offset++] & 0xff) << 8)
+ | ((bytes[offset++] & 0xff) << 16)
+ | ((bytes[offset] & 0xff)) << 24;
+ }
+
+ /**
+ * This method concatenates a 2-dimensional byte array into a 1-dimensional
+ * byte array
+ *
+ * @param arraycp a 2-dimensional byte array.
+ * @return 1-dimensional byte array with concatenated input array
+ */
+ public byte[] concatenateArray(byte[][] arraycp)
+ {
+ byte[] dest = new byte[arraycp.length * arraycp[0].length];
+ int indx = 0;
+ for (int i = 0; i < arraycp.length; i++)
+ {
+ System.arraycopy(arraycp[i], 0, dest, indx, arraycp[i].length);
+ indx = indx + arraycp[i].length;
+ }
+ return dest;
+ }
+
+ /**
+ * This method prints the values of a 2-dimensional byte array
+ *
+ * @param text a String
+ * @param array a 2-dimensional byte array
+ */
+ public void printArray(String text, byte[][] array)
+ {
+ System.out.println(text);
+ int counter = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ for (int j = 0; j < array[0].length; j++)
+ {
+ System.out.println(counter + "; " + array[i][j]);
+ counter++;
+ }
+ }
+ }
+
+ /**
+ * This method prints the values of a 1-dimensional byte array
+ *
+ * @param text a String
+ * @param array a 1-dimensional byte array.
+ */
+ public void printArray(String text, byte[] array)
+ {
+ System.out.println(text);
+ int counter = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ System.out.println(counter + "; " + array[i]);
+ counter++;
+ }
+ }
+
+ /**
+ * This method tests if an integer is a power of 2.
+ *
+ * @param testValue an integer
+ * @return TRUE
if testValue
is a power of 2,
+ * FALSE
otherwise
+ */
+ public boolean testPowerOfTwo(int testValue)
+ {
+ int a = 1;
+ while (a < testValue)
+ {
+ a <<= 1;
+ }
+ if (testValue == a)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer intValue
.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of intValue
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java
new file mode 100644
index 000000000..f971a8bd6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java
@@ -0,0 +1,345 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * This class implements signature verification of the Winternitz one-time
+ * signature scheme (OTSS), described in C.Dods, N.P. Smart, and M. Stam, "Hash
+ * Based Digital Signature Schemes", LNCS 3796, pages 96–115, 2005. The
+ * class is used by the GMSS classes.
+ */
+public class WinternitzOTSVerify
+{
+
+ private Digest messDigestOTS;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The constructor
+ *
+ *
+ * @param digest the name of the hash function used by the OTS and the provider
+ * name of the hash function
+ * @param w the Winternitz parameter
+ */
+ public WinternitzOTSVerify(Digest digest, int w)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+ }
+
+ /**
+ * @return The length of the one-time signature
+ */
+ public int getSignatureLength()
+ {
+ int mdsize = messDigestOTS.getDigestSize();
+ int size = ((mdsize << 3) + (w - 1)) / w;
+ int logs = getLog((size << w) + 1);
+ size += (logs + w - 1) / w;
+
+ return mdsize * size;
+ }
+
+ /**
+ * This method computes the public OTS key from the one-time signature of a
+ * message. This is *NOT* a complete OTS signature verification, but it
+ * suffices for usage with CMSS.
+ *
+ * @param message the message
+ * @param signature the one-time signature
+ * @return The public OTS key
+ */
+ public byte[] Verify(byte[] message, byte[] signature)
+ {
+
+ int mdsize = messDigestOTS.getDigestSize();
+ byte[] hash = new byte[mdsize]; // hash of message m
+
+ // create hash of message m
+ messDigestOTS.update(message, 0, message.length);
+ hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hash, 0);
+
+ int size = ((mdsize << 3) + (w - 1)) / w;
+ int logs = getLog((size << w) + 1);
+ int keysize = size + (logs + w - 1) / w;
+
+ int testKeySize = mdsize * keysize;
+
+ if (testKeySize != signature.length)
+ {
+ return null;
+ }
+
+ byte[] testKey = new byte[testKeySize];
+
+ int c = 0;
+ int counter = 0;
+ int test;
+
+ if (8 % w == 0)
+ {
+ int d = 8 / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+
+ // verify signature
+ for (int i = 0; i < hash.length; i++)
+ {
+ for (int j = 0; j < d; j++)
+ {
+ test = hash[i] & k;
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ hash[i] = (byte)(hash[i] >>> w);
+ counter++;
+ }
+ }
+
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }
+ else if (w < 8)
+ {
+ int d = mdsize / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8;
+ int ii = 0;
+ // create signature
+ // first d*w bytes of hash
+ for (int i = 0; i < d; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ for (int j = 0; j < 8; j++)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+ }
+ // rest of hash
+ d = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < d; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ d <<= 3;
+ for (int j = 0; j < d; j += w)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ // check bytes
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ int d = (mdsize << 3) - w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8, test8;
+ int r = 0;
+ int s, f, rest, ii;
+ // create signature
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w
+ while (r <= d)
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ counter++;
+
+ }
+ // rest of hash
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ counter++;
+ }
+ // check bytes
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test8 = (c & k);
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<57)
+
+ byte[] TKey = new byte[mdsize];
+ messDigestOTS.update(testKey, 0, testKey.length);
+ TKey = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(TKey, 0);
+
+ return TKey;
+
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer intValue
.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base
+ * 256 of intValue
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java
new file mode 100644
index 000000000..92fa23dd0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java
@@ -0,0 +1,405 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * This class implements key pair generation and signature generation of the
+ * Winternitz one-time signature scheme (OTSS), described in C.Dods, N.P. Smart,
+ * and M. Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages
+ * 96–115, 2005. The class is used by the GMSS classes.
+ */
+
+public class WinternitzOTSignature
+{
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * An array of strings, containing the name of the used hash function, the
+ * name of the PRGN and the names of the corresponding providers
+ */
+ // private String[] name = new String[2];
+ /**
+ * The private key
+ */
+ private byte[][] privateKeyOTS;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Sizes of the message and the checksum, both
+ */
+ private int messagesize, checksumsize;
+
+ /**
+ * The constructor generates an OTS key pair, using seed0
and
+ * the PRNG
+ *
+ *
+ * @param seed0 the seed for the PRGN
+ * @param digest an array of strings, containing the name of the used hash
+ * function, the name of the PRGN and the names of the
+ * corresponding providers
+ * @param w the Winternitz parameter
+ */
+ public WinternitzOTSignature(byte[] seed0, Digest digest, int w)
+ {
+ // this.name = name;
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private and public key and also the help
+ // array
+
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+
+ checksumsize = getLog((messagesize << w) + 1);
+
+ keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+
+ /*
+ * mdsize = messDigestOTS.getDigestLength(); messagesize =
+ * ((mdsize<<3)+(w-1))/w;
+ *
+ * checksumsize = getlog((messagesize<intValue
.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of intValue
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java
new file mode 100644
index 000000000..9772d018d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java
@@ -0,0 +1,236 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.math.BigInteger;
+
+import org.spongycastle.pqc.math.linearalgebra.BigIntUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.IntegerFunctions;
+
+
+/**
+ * Provides methods for CCA2-Secure Conversions of McEliece PKCS
+ */
+final class Conversions
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ /**
+ * Default constructor (private).
+ */
+ private Conversions()
+ {
+ }
+
+ /**
+ * Encode a number between 0 and (n|t) (binomial coefficient) into a binary
+ * vector of length n with weight t. The number is given as a byte array.
+ * Only the first s bits are used, where s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param m the message as a byte array
+ * @return the encoded message as {@link GF2Vector}
+ */
+ public static GF2Vector encode(final int n, final int t, final byte[] m)
+ {
+ if (n < t)
+ {
+ throw new IllegalArgumentException("n < t");
+ }
+
+ // compute the binomial c = (n|t)
+ BigInteger c = IntegerFunctions.binomial(n, t);
+ // get the number encoded in m
+ BigInteger i = new BigInteger(1, m);
+ // compare
+ if (i.compareTo(c) >= 0)
+ {
+ throw new IllegalArgumentException("Encoded number too large.");
+ }
+
+ GF2Vector result = new GF2Vector(n);
+
+ int nn = n;
+ int tt = t;
+ for (int j = 0; j < n; j++)
+ {
+ c = c.multiply(BigInteger.valueOf(nn - tt)).divide(
+ BigInteger.valueOf(nn));
+ nn--;
+ if (c.compareTo(i) <= 0)
+ {
+ result.setBit(j);
+ i = i.subtract(c);
+ tt--;
+ if (nn == tt)
+ {
+ c = ONE;
+ }
+ else
+ {
+ c = (c.multiply(BigInteger.valueOf(tt + 1)))
+ .divide(BigInteger.valueOf(nn - tt));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Decode a binary vector of length n and weight t into a number between 0
+ * and (n|t) (binomial coefficient). The result is given as a byte array of
+ * length floor[(s+7)/8], where s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param vec the binary vector
+ * @return the decoded vector as a byte array
+ */
+ public static byte[] decode(int n, int t, GF2Vector vec)
+ {
+ if ((vec.getLength() != n) || (vec.getHammingWeight() != t))
+ {
+ throw new IllegalArgumentException(
+ "vector has wrong length or hamming weight");
+ }
+ int[] vecArray = vec.getVecArray();
+
+ BigInteger bc = IntegerFunctions.binomial(n, t);
+ BigInteger d = ZERO;
+ int nn = n;
+ int tt = t;
+ for (int i = 0; i < n; i++)
+ {
+ bc = bc.multiply(BigInteger.valueOf(nn - tt)).divide(
+ BigInteger.valueOf(nn));
+ nn--;
+
+ int q = i >> 5;
+ int e = vecArray[q] & (1 << (i & 0x1f));
+ if (e != 0)
+ {
+ d = d.add(bc);
+ tt--;
+ if (nn == tt)
+ {
+ bc = ONE;
+ }
+ else
+ {
+ bc = bc.multiply(BigInteger.valueOf(tt + 1)).divide(
+ BigInteger.valueOf(nn - tt));
+ }
+
+ }
+ }
+
+ return BigIntUtils.toMinimalByteArray(d);
+ }
+
+ /**
+ * Compute a message representative of a message given as a vector of length
+ * n bit and of hamming weight t. The result is a
+ * byte array of length (s+7)/8, where
+ * s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param m the message vector as a byte array
+ * @return a message representative for m
+ */
+ public static byte[] signConversion(int n, int t, byte[] m)
+ {
+ if (n < t)
+ {
+ throw new IllegalArgumentException("n < t");
+ }
+
+ BigInteger bc = IntegerFunctions.binomial(n, t);
+ // finds s = floor[log(binomial(n,t))]
+ int s = bc.bitLength() - 1;
+ // s = sq*8 + sr;
+ int sq = s >> 3;
+ int sr = s & 7;
+ if (sr == 0)
+ {
+ sq--;
+ sr = 8;
+ }
+
+ // n = nq*8+nr;
+ int nq = n >> 3;
+ int nr = n & 7;
+ if (nr == 0)
+ {
+ nq--;
+ nr = 8;
+ }
+ // take s bit from m
+ byte[] data = new byte[nq + 1];
+ if (m.length < data.length)
+ {
+ System.arraycopy(m, 0, data, 0, m.length);
+ for (int i = m.length; i < data.length; i++)
+ {
+ data[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(m, 0, data, 0, nq);
+ int h = (1 << nr) - 1;
+ data[nq] = (byte)(h & m[nq]);
+ }
+
+ BigInteger d = ZERO;
+ int nn = n;
+ int tt = t;
+ for (int i = 0; i < n; i++)
+ {
+ bc = (bc.multiply(new BigInteger(Integer.toString(nn - tt))))
+ .divide(new BigInteger(Integer.toString(nn)));
+ nn--;
+
+ int q = i >>> 3;
+ int r = i & 7;
+ r = 1 << r;
+ byte e = (byte)(r & data[q]);
+ if (e != 0)
+ {
+ d = d.add(bc);
+ tt--;
+ if (nn == tt)
+ {
+ bc = ONE;
+ }
+ else
+ {
+ bc = (bc
+ .multiply(new BigInteger(Integer.toString(tt + 1))))
+ .divide(new BigInteger(Integer.toString(nn - tt)));
+ }
+ }
+ }
+
+ byte[] result = new byte[sq + 1];
+ byte[] help = d.toByteArray();
+ if (help.length < result.length)
+ {
+ System.arraycopy(help, 0, result, 0, help.length);
+ for (int i = help.length; i < result.length; i++)
+ {
+ result[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(help, 0, result, 0, sq);
+ result[sq] = (byte)(((1 << sr) - 1) & help[sq]);
+ }
+
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java
new file mode 100644
index 000000000..8095d291e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class McElieceCCA2KeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private McElieceCCA2Parameters params;
+
+ public McElieceCCA2KeyGenerationParameters(
+ SecureRandom random,
+ McElieceCCA2Parameters params)
+ {
+ // XXX key size?
+ super(random, 128);
+ this.params = params;
+ }
+
+ public McElieceCCA2Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
new file mode 100644
index 000000000..c950bda7a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
@@ -0,0 +1,119 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialRingGF2m;
+
+
+/**
+ * This class implements key pair generation of the McEliece Public Key
+ * Cryptosystem (McEliecePKC).
+ */
+public class McElieceCCA2KeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2";
+
+ private McElieceCCA2KeyGenerationParameters mcElieceCCA2Params;
+
+ // the extension degree of the finite field GF(2^m)
+ private int m;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability
+ private int t;
+
+ // the field polynomial
+ private int fieldPoly;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ // flag indicating whether the key pair generator has been initialized
+ private boolean initialized = false;
+
+ /**
+ * Default initialization of the key pair generator.
+ */
+ private void initializeDefault()
+ {
+ McElieceCCA2KeyGenerationParameters mcCCA2Params = new McElieceCCA2KeyGenerationParameters(new SecureRandom(), new McElieceCCA2Parameters());
+ init(mcCCA2Params);
+ }
+
+ // TODO
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.mcElieceCCA2Params = (McElieceCCA2KeyGenerationParameters)param;
+
+ // set source of randomness
+ this.random = new SecureRandom();
+
+ this.m = this.mcElieceCCA2Params.getParameters().getM();
+ this.n = this.mcElieceCCA2Params.getParameters().getN();
+ this.t = this.mcElieceCCA2Params.getParameters().getT();
+ this.fieldPoly = this.mcElieceCCA2Params.getParameters().getFieldPoly();
+ this.initialized = true;
+ }
+
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // finite field GF(2^m)
+ GF2mField field = new GF2mField(m, fieldPoly);
+
+ // irreducible Goppa polynomial
+ PolynomialGF2mSmallM gp = new PolynomialGF2mSmallM(field, t,
+ PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL, random);
+ PolynomialRingGF2m ring = new PolynomialRingGF2m(field, gp);
+
+ // matrix for computing square roots in (GF(2^m))^t
+ PolynomialGF2mSmallM[] qInv = ring.getSquareRootMatrix();
+
+ // generate canonical check matrix
+ GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
+
+ // compute short systematic form of check matrix
+ MaMaPe mmp = GoppaCode.computeSystematicForm(h, random);
+ GF2Matrix shortH = mmp.getSecondMatrix();
+ Permutation p = mmp.getPermutation();
+
+ // compute short systematic form of generator matrix
+ GF2Matrix shortG = (GF2Matrix)shortH.computeTranspose();
+
+ // obtain number of rows of G (= dimension of the code)
+ int k = shortG.getNumRows();
+
+ // generate keys
+ McElieceCCA2PublicKeyParameters pubKey = new McElieceCCA2PublicKeyParameters(OID, n, t, shortG, mcElieceCCA2Params.getParameters());
+ McElieceCCA2PrivateKeyParameters privKey = new McElieceCCA2PrivateKeyParameters(OID, n, k,
+ field, gp, p, h, qInv, mcElieceCCA2Params.getParameters());
+
+ // return key pair
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java
new file mode 100644
index 000000000..4dc3b8d24
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+
+public class McElieceCCA2KeyParameters
+ extends AsymmetricKeyParameter
+{
+ private McElieceCCA2Parameters params;
+
+ public McElieceCCA2KeyParameters(
+ boolean isPrivate,
+ McElieceCCA2Parameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+
+ public McElieceCCA2Parameters getParameters()
+ {
+ return params;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java
new file mode 100644
index 000000000..11eae2e09
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java
@@ -0,0 +1,51 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+
+/**
+ * This class provides a specification for the parameters of the CCA2-secure
+ * variants of the McEliece PKCS that are used with
+ * {@link McElieceFujisakiCipher}, {@link McElieceKobaraImaiCipher}, and
+ * {@link McEliecePointchevalCipher}.
+ *
+ * @see McElieceFujisakiCipher
+ * @see McElieceKobaraImaiCipher
+ * @see McEliecePointchevalCipher
+ */
+public class McElieceCCA2Parameters
+ extends McElieceParameters
+{
+
+
+ public Digest digest;
+
+
+ /**
+ * Construct the default parameters.
+ * The default message digest is SHA256.
+ */
+ public McElieceCCA2Parameters()
+ {
+ this.digest = new SHA256Digest();
+ }
+
+ public McElieceCCA2Parameters(int m, int t)
+ {
+ super(m, t);
+ this.digest = new SHA256Digest();
+ }
+
+ public McElieceCCA2Parameters(Digest digest)
+ {
+ this.digest = digest;
+ }
+
+ public Digest getDigest()
+ {
+ return this.digest;
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java
new file mode 100644
index 000000000..66c163a25
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java
@@ -0,0 +1,86 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.Vector;
+
+/**
+ * Core operations for the CCA-secure variants of McEliece.
+ */
+public final class McElieceCCA2Primitives
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private McElieceCCA2Primitives()
+ {
+ }
+
+ /**
+ * The McEliece encryption primitive.
+ *
+ * @param pubKey the public key
+ * @param m the message vector
+ * @param z the error vector
+ * @return m*G + z
+ */
+
+
+ public static GF2Vector encryptionPrimitive(McElieceCCA2PublicKeyParameters pubKey,
+ GF2Vector m, GF2Vector z)
+ {
+
+ GF2Matrix matrixG = pubKey.getMatrixG();
+ Vector mG = matrixG.leftMultiplyLeftCompactForm(m);
+ return (GF2Vector)mG.add(z);
+ }
+
+ /**
+ * The McEliece decryption primitive.
+ *
+ * @param privKey the private key
+ * @param c the ciphertext vector c = m*G + z
+ * @return the message vector m and the error vector z
+ */
+ public static GF2Vector[] decryptionPrimitive(
+ McElieceCCA2PrivateKeyParameters privKey, GF2Vector c)
+ {
+
+ // obtain values from private key
+ int k = privKey.getK();
+ Permutation p = privKey.getP();
+ GF2mField field = privKey.getField();
+ PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
+ GF2Matrix h = privKey.getH();
+ PolynomialGF2mSmallM[] q = privKey.getQInv();
+
+ // compute inverse permutation P^-1
+ Permutation pInv = p.computeInverse();
+
+ // multiply c with permutation P^-1
+ GF2Vector cPInv = (GF2Vector)c.multiply(pInv);
+
+ // compute syndrome of cP^-1
+ GF2Vector syndVec = (GF2Vector)h.rightMultiply(cPInv);
+
+ // decode syndrome
+ GF2Vector errors = GoppaCode.syndromeDecode(syndVec, field, gp, q);
+ GF2Vector mG = (GF2Vector)cPInv.add(errors);
+
+ // multiply codeword and error vector with P
+ mG = (GF2Vector)mG.multiply(p);
+ errors = (GF2Vector)errors.multiply(p);
+
+ // extract plaintext vector (last k columns of mG)
+ GF2Vector m = mG.extractRightVector(k);
+
+ // return vectors
+ return new GF2Vector[]{m, errors};
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java
new file mode 100644
index 000000000..a19a267b6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java
@@ -0,0 +1,172 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+/**
+ *
+ *
+ *
+ */
+public class McElieceCCA2PrivateKeyParameters
+ extends McElieceCCA2KeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the dimension of the code
+ private int k;
+
+ // the finte field GF(2^m)
+ private GF2mField field;
+
+ // the irreducible Goppa polynomial
+ private PolynomialGF2mSmallM goppaPoly;
+
+ // the permutation
+ private Permutation p;
+
+ // the canonical check matrix
+ private GF2Matrix h;
+
+ // the matrix used to compute square roots in (GF(2^m))^t
+ private PolynomialGF2mSmallM[] qInv;
+
+ /**
+ * Constructor.
+ *
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param field the finite field GF(2m)
+ * @param gp the irreducible Goppa polynomial
+ * @param p the permutation
+ * @param h the canonical check matrix
+ * @param qInv the matrix used to compute square roots in
+ * (GF(2^m))^t
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PrivateKeyParameters(String oid, int n, int k, GF2mField field,
+ PolynomialGF2mSmallM gp, Permutation p, GF2Matrix h,
+ PolynomialGF2mSmallM[] qInv, McElieceCCA2Parameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.field = field;
+ this.goppaPoly = gp;
+ this.p = p;
+ this.h = h;
+ this.qInv = qInv;
+ }
+
+ /**
+ * Constructor used by the {@link McElieceKeyFactory}.
+ *
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param encFieldPoly the encoded field polynomial defining the finite field
+ * GF(2m)
+ * @param encGoppaPoly the encoded irreducible Goppa polynomial
+ * @param encP the encoded permutation
+ * @param encH the encoded canonical check matrix
+ * @param encQInv the encoded matrix used to compute square roots in
+ * (GF(2^m))^t
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PrivateKeyParameters(String oid, int n, int k, byte[] encFieldPoly,
+ byte[] encGoppaPoly, byte[] encP, byte[] encH, byte[][] encQInv, McElieceCCA2Parameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ field = new GF2mField(encFieldPoly);
+ goppaPoly = new PolynomialGF2mSmallM(field, encGoppaPoly);
+ p = new Permutation(encP);
+ h = new GF2Matrix(encH);
+ qInv = new PolynomialGF2mSmallM[encQInv.length];
+ for (int i = 0; i < encQInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encQInv[i]);
+ }
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return k;
+ }
+
+ /**
+ * @return the degree of the Goppa polynomial (error correcting capability)
+ */
+ public int getT()
+ {
+ return goppaPoly.getDegree();
+ }
+
+ /**
+ * @return the finite field
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return the irreducible Goppa polynomial
+ */
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return goppaPoly;
+ }
+
+ /**
+ * @return the permutation P
+ */
+ public Permutation getP()
+ {
+ return p;
+ }
+
+ /**
+ * @return the canonical check matrix H
+ */
+ public GF2Matrix getH()
+ {
+ return h;
+ }
+
+ /**
+ * @return the matrix used to compute square roots in (GF(2^m))^t
+ */
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ return qInv;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java
new file mode 100644
index 000000000..9b5ba0a6b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java
@@ -0,0 +1,97 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+/**
+ *
+ *
+ *
+ */
+public class McElieceCCA2PublicKeyParameters
+ extends McElieceCCA2KeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability of the code
+ private int t;
+
+ // the generator matrix
+ private GF2Matrix matrixG;
+
+ /**
+ * Constructor.
+ *
+ * @param n length of the code
+ * @param t error correction capability
+ * @param matrix generator matrix
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PublicKeyParameters(String oid, int n, int t, GF2Matrix matrix, McElieceCCA2Parameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = new GF2Matrix(matrix);
+ }
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param n length of the code
+ * @param t error correction capability of the code
+ * @param encMatrix encoded generator matrix
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PublicKeyParameters(String oid, int n, int t, byte[] encMatrix, McElieceCCA2Parameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = new GF2Matrix(encMatrix);
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getMatrixG()
+ {
+ return matrixG;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return matrixG.getNumRows();
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
new file mode 100644
index 000000000..810a69c8e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
@@ -0,0 +1,218 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+
+/**
+ * This class implements the Fujisaki/Okamoto conversion of the McEliecePKCS.
+ * Fujisaki and Okamoto propose hybrid encryption that merges a symmetric
+ * encryption scheme which is secure in the find-guess model with an asymmetric
+ * one-way encryption scheme which is sufficiently probabilistic to obtain a
+ * public key cryptosystem which is CCA2-secure. For details, see D. Engelbert,
+ * R. Overbeck, A. Schmidt, "A summary of the development of the McEliece
+ * Cryptosystem", technical report.
+ */
+public class McElieceFujisakiCipher
+ implements MessageEncryptor
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.1";
+
+ private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+ McElieceCCA2KeyParameters key;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ throws IllegalArgumentException
+ {
+
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ }
+
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ t = privKey.getT();
+ }
+
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ // generate random vector r of length k bits
+ GF2Vector r = new GF2Vector(k, sr);
+
+ // convert r to byte array
+ byte[] rBytes = r.getEncoded();
+
+ // compute (r||input)
+ byte[] rm = ByteUtils.concatenate(rBytes, input);
+
+ // compute H(r||input)
+ messDigest.update(rm, 0, rm.length);
+ byte[] hrm = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hrm, 0);
+
+ // convert H(r||input) to error vector z
+ GF2Vector z = Conversions.encode(n, t, hrm);
+
+ // compute c1 = E(r, z)
+ byte[] c1 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key, r, z)
+ .getEncoded();
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rBytes);
+
+ // generate random c2
+ byte[] c2 = new byte[input.length];
+ sr0.nextBytes(c2);
+
+ // XOR with input
+ for (int i = 0; i < input.length; i++)
+ {
+ c2[i] ^= input[i];
+ }
+
+ // return (c1||c2)
+ return ByteUtils.concatenate(c1, c2);
+ }
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int c1Len = (n + 7) >> 3;
+ int c2Len = input.length - c1Len;
+
+ // split ciphertext (c1||c2)
+ byte[][] c1c2 = ByteUtils.split(input, c1Len);
+ byte[] c1 = c1c2[0];
+ byte[] c2 = c1c2[1];
+
+ // decrypt c1 ...
+ GF2Vector hrmVec = GF2Vector.OS2VP(n, c1);
+ GF2Vector[] decC1 = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ hrmVec);
+ byte[] rBytes = decC1[0].getEncoded();
+ // ... and obtain error vector z
+ GF2Vector z = decC1[1];
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rBytes);
+
+ // generate random sequence
+ byte[] mBytes = new byte[c2Len];
+ sr0.nextBytes(mBytes);
+
+ // XOR with c2 to obtain m
+ for (int i = 0; i < c2Len; i++)
+ {
+ mBytes[i] ^= c2[i];
+ }
+
+ // compute H(r||m)
+ byte[] rmBytes = ByteUtils.concatenate(rBytes, mBytes);
+ byte[] hrm = new byte[messDigest.getDigestSize()];
+ messDigest.update(rmBytes, 0, rmBytes.length);
+ messDigest.doFinal(hrm, 0);
+
+
+ // compute Conv(H(r||m))
+ hrmVec = Conversions.encode(n, t, hrm);
+
+ // check that Conv(H(m||r)) = z
+ if (!hrmVec.equals(z))
+ {
+
+ throw new Exception("Bad Padding: invalid ciphertext");
+
+ }
+
+ // return plaintext m
+ return mBytes;
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java
new file mode 100644
index 000000000..9a5577ce0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McElieceFujisakiDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McElieceFujisakiDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McElieceFujisakiDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McElieceFujisakiDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java
new file mode 100644
index 000000000..129a704c6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class McElieceKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private McElieceParameters params;
+
+ public McElieceKeyGenerationParameters(
+ SecureRandom random,
+ McElieceParameters params)
+ {
+ // XXX key size?
+ super(random, 256);
+ this.params = params;
+ }
+
+ public McElieceParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
new file mode 100644
index 000000000..07db4cd6d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
@@ -0,0 +1,151 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialRingGF2m;
+
+
+/**
+ * This class implements key pair generation of the McEliece Public Key
+ * Cryptosystem (McEliecePKC).
+ */
+public class McElieceKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+
+ public McElieceKeyPairGenerator()
+ {
+
+ }
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ private static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";
+
+ private McElieceKeyGenerationParameters mcElieceParams;
+
+ // the extension degree of the finite field GF(2^m)
+ private int m;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability
+ private int t;
+
+ // the field polynomial
+ private int fieldPoly;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ // flag indicating whether the key pair generator has been initialized
+ private boolean initialized = false;
+
+
+ /**
+ * Default initialization of the key pair generator.
+ */
+ private void initializeDefault()
+ {
+ McElieceKeyGenerationParameters mcParams = new McElieceKeyGenerationParameters(new SecureRandom(), new McElieceParameters());
+ initialize(mcParams);
+ }
+
+ private void initialize(
+ KeyGenerationParameters param)
+ {
+ this.mcElieceParams = (McElieceKeyGenerationParameters)param;
+
+ // set source of randomness
+ this.random = new SecureRandom();
+
+ this.m = this.mcElieceParams.getParameters().getM();
+ this.n = this.mcElieceParams.getParameters().getN();
+ this.t = this.mcElieceParams.getParameters().getT();
+ this.fieldPoly = this.mcElieceParams.getParameters().getFieldPoly();
+ this.initialized = true;
+ }
+
+
+ private AsymmetricCipherKeyPair genKeyPair()
+ {
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // finite field GF(2^m)
+ GF2mField field = new GF2mField(m, fieldPoly);
+
+ // irreducible Goppa polynomial
+ PolynomialGF2mSmallM gp = new PolynomialGF2mSmallM(field, t,
+ PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL, random);
+ PolynomialRingGF2m ring = new PolynomialRingGF2m(field, gp);
+
+ // matrix used to compute square roots in (GF(2^m))^t
+ PolynomialGF2mSmallM[] sqRootMatrix = ring.getSquareRootMatrix();
+
+ // generate canonical check matrix
+ GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
+
+ // compute short systematic form of check matrix
+ MaMaPe mmp = GoppaCode.computeSystematicForm(h, random);
+ GF2Matrix shortH = mmp.getSecondMatrix();
+ Permutation p1 = mmp.getPermutation();
+
+ // compute short systematic form of generator matrix
+ GF2Matrix shortG = (GF2Matrix)shortH.computeTranspose();
+
+ // extend to full systematic form
+ GF2Matrix gPrime = shortG.extendLeftCompactForm();
+
+ // obtain number of rows of G (= dimension of the code)
+ int k = shortG.getNumRows();
+
+ // generate random invertible (k x k)-matrix S and its inverse S^-1
+ GF2Matrix[] matrixSandInverse = GF2Matrix
+ .createRandomRegularMatrixAndItsInverse(k, random);
+
+ // generate random permutation P2
+ Permutation p2 = new Permutation(n, random);
+
+ // compute public matrix G=S*G'*P2
+ GF2Matrix g = (GF2Matrix)matrixSandInverse[0].rightMultiply(gPrime);
+ g = (GF2Matrix)g.rightMultiply(p2);
+
+
+ // generate keys
+ McEliecePublicKeyParameters pubKey = new McEliecePublicKeyParameters(OID, n, t, g, mcElieceParams.getParameters());
+ McEliecePrivateKeyParameters privKey = new McEliecePrivateKeyParameters(OID, n, k,
+ field, gp, matrixSandInverse[1], p1, p2, h, sqRootMatrix, mcElieceParams.getParameters());
+
+ // return key pair
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java
new file mode 100644
index 000000000..a8b2ce841
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+
+public class McElieceKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private McElieceParameters params;
+
+ public McElieceKeyParameters(
+ boolean isPrivate,
+ McElieceParameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+
+ public McElieceParameters getParameters()
+ {
+ return params;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
new file mode 100644
index 000000000..0be981be3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
@@ -0,0 +1,319 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.IntegerFunctions;
+
+/**
+ * This class implements the Kobara/Imai conversion of the McEliecePKCS. This is
+ * a conversion of the McEliecePKCS which is CCA2-secure. For details, see D.
+ * Engelbert, R. Overbeck, A. Schmidt, "A summary of the development of the
+ * McEliece Cryptosystem", technical report.
+ */
+public class McElieceKobaraImaiCipher
+ implements MessageEncryptor
+{
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.3";
+
+ private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
+
+ /**
+ * A predetermined public constant.
+ */
+ public static final byte[] PUBLIC_CONSTANT = "a predetermined public constant"
+ .getBytes();
+
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ McElieceCCA2KeyParameters key;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceCCA2KeyParameters object
+ * @return the key size of the given key object
+ */
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ {
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+ }
+
+ private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+
+ }
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ k = privKey.getK();
+ t = privKey.getT();
+ }
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ int c2Len = messDigest.getDigestSize();
+ int c4Len = k >> 3;
+ int c5Len = (IntegerFunctions.binomial(n, t).bitLength() - 1) >> 3;
+
+
+ int mLen = c4Len + c5Len - c2Len - PUBLIC_CONSTANT.length;
+ if (input.length > mLen)
+ {
+ mLen = input.length;
+ }
+
+ int c1Len = mLen + PUBLIC_CONSTANT.length;
+ int c6Len = c1Len + c2Len - c4Len - c5Len;
+
+ // compute (m||const)
+ byte[] mConst = new byte[c1Len];
+ System.arraycopy(input, 0, mConst, 0, input.length);
+ System.arraycopy(PUBLIC_CONSTANT, 0, mConst, mLen,
+ PUBLIC_CONSTANT.length);
+
+ // generate random r of length c2Len bytes
+ byte[] r = new byte[c2Len];
+ sr.nextBytes(r);
+
+ // get PRNG object
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(r);
+
+ // generate random sequence ...
+ byte[] c1 = new byte[c1Len];
+ sr0.nextBytes(c1);
+
+ // ... and XOR with (m||const) to obtain c1
+ for (int i = c1Len - 1; i >= 0; i--)
+ {
+ c1[i] ^= mConst[i];
+ }
+
+ // compute H(c1) ...
+ byte[] c2 = new byte[messDigest.getDigestSize()];
+ messDigest.update(c1, 0, c1.length);
+ messDigest.doFinal(c2, 0);
+
+ // ... and XOR with r
+ for (int i = c2Len - 1; i >= 0; i--)
+ {
+ c2[i] ^= r[i];
+ }
+
+ // compute (c2||c1)
+ byte[] c2c1 = ByteUtils.concatenate(c2, c1);
+
+ // split (c2||c1) into (c6||c5||c4), where c4Len is k/8 bytes, c5Len is
+ // floor[log(n|t)]/8 bytes, and c6Len is c1Len+c2Len-c4Len-c5Len (may be
+ // 0).
+ byte[] c6 = new byte[0];
+ if (c6Len > 0)
+ {
+ c6 = new byte[c6Len];
+ System.arraycopy(c2c1, 0, c6, 0, c6Len);
+ }
+
+ byte[] c5 = new byte[c5Len];
+ System.arraycopy(c2c1, c6Len, c5, 0, c5Len);
+
+ byte[] c4 = new byte[c4Len];
+ System.arraycopy(c2c1, c6Len + c5Len, c4, 0, c4Len);
+
+ // convert c4 to vector over GF(2)
+ GF2Vector c4Vec = GF2Vector.OS2VP(k, c4);
+
+ // convert c5 to error vector z
+ GF2Vector z = Conversions.encode(n, t, c5);
+
+ // compute encC4 = E(c4, z)
+ byte[] encC4 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key,
+ c4Vec, z).getEncoded();
+
+ // if c6Len > 0
+ if (c6Len > 0)
+ {
+ // return (c6||encC4)
+ return ByteUtils.concatenate(c6, encC4);
+ }
+ // else, return encC4
+ return encC4;
+ }
+
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int nDiv8 = n >> 3;
+
+ if (input.length < nDiv8)
+ {
+ throw new Exception("Bad Padding: Ciphertext too short.");
+ }
+
+ int c2Len = messDigest.getDigestSize();
+ int c4Len = k >> 3;
+ int c6Len = input.length - nDiv8;
+
+ // split cipher text (c6||encC4), where c6 may be empty
+ byte[] c6, encC4;
+ if (c6Len > 0)
+ {
+ byte[][] c6EncC4 = ByteUtils.split(input, c6Len);
+ c6 = c6EncC4[0];
+ encC4 = c6EncC4[1];
+ }
+ else
+ {
+ c6 = new byte[0];
+ encC4 = input;
+ }
+
+ // convert encC4 into vector over GF(2)
+ GF2Vector encC4Vec = GF2Vector.OS2VP(n, encC4);
+
+ // decrypt encC4Vec to obtain c4 and error vector z
+ GF2Vector[] c4z = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ encC4Vec);
+ byte[] c4 = c4z[0].getEncoded();
+ GF2Vector z = c4z[1];
+
+ // if length of c4 is greater than c4Len (because of padding) ...
+ if (c4.length > c4Len)
+ {
+ // ... truncate the padding bytes
+ c4 = ByteUtils.subArray(c4, 0, c4Len);
+ }
+
+ // compute c5 = Conv^-1(z)
+ byte[] c5 = Conversions.decode(n, t, z);
+
+ // compute (c6||c5||c4)
+ byte[] c6c5c4 = ByteUtils.concatenate(c6, c5);
+ c6c5c4 = ByteUtils.concatenate(c6c5c4, c4);
+
+ // split (c6||c5||c4) into (c2||c1), where c2Len = mdLen and c1Len =
+ // input.length-c2Len bytes.
+ int c1Len = c6c5c4.length - c2Len;
+ byte[][] c2c1 = ByteUtils.split(c6c5c4, c2Len);
+ byte[] c2 = c2c1[0];
+ byte[] c1 = c2c1[1];
+
+ // compute H(c1) ...
+ byte[] rPrime = new byte[messDigest.getDigestSize()];
+ messDigest.update(c1, 0, c1.length);
+ messDigest.doFinal(rPrime, 0);
+
+ // ... and XOR with c2 to obtain r'
+ for (int i = c2Len - 1; i >= 0; i--)
+ {
+ rPrime[i] ^= c2[i];
+ }
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrime);
+
+ // generate random sequence R(r') ...
+ byte[] mConstPrime = new byte[c1Len];
+ sr0.nextBytes(mConstPrime);
+
+ // ... and XOR with c1 to obtain (m||const')
+ for (int i = c1Len - 1; i >= 0; i--)
+ {
+ mConstPrime[i] ^= c1[i];
+ }
+
+ if (mConstPrime.length < c1Len)
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ byte[][] temp = ByteUtils.split(mConstPrime, c1Len
+ - PUBLIC_CONSTANT.length);
+ byte[] mr = temp[0];
+ byte[] constPrime = temp[1];
+
+ if (!ByteUtils.equals(constPrime, PUBLIC_CONSTANT))
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ return mr;
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java
new file mode 100644
index 000000000..7df9cc0d3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McElieceKobaraImaiDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McElieceKobaraImaiDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McElieceKobaraImaiDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McElieceKobaraImaiDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
new file mode 100644
index 000000000..2e843aa07
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
@@ -0,0 +1,224 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.Vector;
+
+/**
+ * This class implements the McEliece Public Key cryptosystem (McEliecePKCS). It
+ * was first described in R.J. McEliece, "A public key cryptosystem based on
+ * algebraic coding theory", DSN progress report, 42-44:114-116, 1978. The
+ * McEliecePKCS is the first cryptosystem which is based on error correcting
+ * codes. The trapdoor for the McEliece cryptosystem using Goppa codes is the
+ * knowledge of the Goppa polynomial used to generate the code.
+ */
+public class McEliecePKCSCipher
+ implements MessageEncryptor
+{
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";
+
+
+ // the source of randomness
+ private SecureRandom sr;
+
+ // the McEliece main parameters
+ private int n, k, t;
+
+ // The maximum number of bytes the cipher can decrypt
+ public int maxPlainTextSize;
+
+ // The maximum number of bytes the cipher can encrypt
+ public int cipherTextSize;
+
+ McElieceKeyParameters key;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McEliecePublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McEliecePublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McEliecePublicKeyParameters)param;
+ this.initCipherEncrypt((McEliecePublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McEliecePrivateKeyParameters)param;
+ this.initCipherDecrypt((McEliecePrivateKeyParameters)key);
+ }
+
+ }
+
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceKeyParameters object
+ * @return the keysize of the given key object
+ */
+
+ public int getKeySize(McElieceKeyParameters key)
+ {
+
+ if (key instanceof McEliecePublicKeyParameters)
+ {
+ return ((McEliecePublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McEliecePrivateKeyParameters)
+ {
+ return ((McEliecePrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ public void initCipherEncrypt(McEliecePublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ cipherTextSize = n >> 3;
+ maxPlainTextSize = (k >> 3);
+ }
+
+
+ public void initCipherDecrypt(McEliecePrivateKeyParameters privKey)
+ {
+ n = privKey.getN();
+ k = privKey.getK();
+
+ maxPlainTextSize = (k >> 3);
+ cipherTextSize = n >> 3;
+ }
+
+ /**
+ * Encrypt a plain text.
+ *
+ * @param input the plain text
+ * @return the cipher text
+ */
+ public byte[] messageEncrypt(byte[] input)
+ {
+ GF2Vector m = computeMessageRepresentative(input);
+ GF2Vector z = new GF2Vector(n, t, sr);
+
+ GF2Matrix g = ((McEliecePublicKeyParameters)key).getG();
+ Vector mG = g.leftMultiply(m);
+ GF2Vector mGZ = (GF2Vector)mG.add(z);
+
+ return mGZ.getEncoded();
+ }
+
+ private GF2Vector computeMessageRepresentative(byte[] input)
+ {
+ byte[] data = new byte[maxPlainTextSize + ((k & 0x07) != 0 ? 1 : 0)];
+ System.arraycopy(input, 0, data, 0, input.length);
+ data[input.length] = 0x01;
+ return GF2Vector.OS2VP(k, data);
+ }
+
+ /**
+ * Decrypt a cipher text.
+ *
+ * @param input the cipher text
+ * @return the plain text
+ * @throws Exception if the cipher text is invalid.
+ */
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+ GF2Vector vec = GF2Vector.OS2VP(n, input);
+ McEliecePrivateKeyParameters privKey = (McEliecePrivateKeyParameters)key;
+ GF2mField field = privKey.getField();
+ PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
+ GF2Matrix sInv = privKey.getSInv();
+ Permutation p1 = privKey.getP1();
+ Permutation p2 = privKey.getP2();
+ GF2Matrix h = privKey.getH();
+ PolynomialGF2mSmallM[] qInv = privKey.getQInv();
+
+ // compute permutation P = P1 * P2
+ Permutation p = p1.rightMultiply(p2);
+
+ // compute P^-1
+ Permutation pInv = p.computeInverse();
+
+ // compute c P^-1
+ GF2Vector cPInv = (GF2Vector)vec.multiply(pInv);
+
+ // compute syndrome of c P^-1
+ GF2Vector syndrome = (GF2Vector)h.rightMultiply(cPInv);
+
+ // decode syndrome
+ GF2Vector z = GoppaCode.syndromeDecode(syndrome, field, gp, qInv);
+ GF2Vector mSG = (GF2Vector)cPInv.add(z);
+
+ // multiply codeword with P1 and error vector with P
+ mSG = (GF2Vector)mSG.multiply(p1);
+ z = (GF2Vector)z.multiply(p);
+
+ // extract mS (last k columns of mSG)
+ GF2Vector mS = mSG.extractRightVector(k);
+
+ // compute plaintext vector
+ GF2Vector mVec = (GF2Vector)sInv.leftMultiply(mS);
+
+ // compute and return plaintext
+ return computeMessage(mVec);
+ }
+
+ private byte[] computeMessage(GF2Vector mr)
+ throws Exception
+ {
+ byte[] mrBytes = mr.getEncoded();
+ // find first non-zero byte
+ int index;
+ for (index = mrBytes.length - 1; index >= 0 && mrBytes[index] == 0; index--)
+ {
+ ;
+ }
+
+ // check if padding byte is valid
+ if (mrBytes[index] != 0x01)
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ // extract and return message
+ byte[] mBytes = new byte[index];
+ System.arraycopy(mrBytes, 0, mBytes, 0, index);
+ return mBytes;
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java
new file mode 100644
index 000000000..901fe6883
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McEliecePKCSDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCipher;
+
+ private boolean forEncrypting;
+
+
+ public McEliecePKCSDigestCipher(MessageEncryptor mcElieceCipher, Digest messDigest)
+ {
+ this.mcElieceCipher = mcElieceCipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePKCSDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePKCSDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java
new file mode 100644
index 000000000..d49f92793
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java
@@ -0,0 +1,181 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialRingGF2;
+
+public class McElieceParameters
+ implements CipherParameters
+{
+
+ /**
+ * The default extension degree
+ */
+ public static final int DEFAULT_M = 11;
+
+ /**
+ * The default error correcting capability.
+ */
+ public static final int DEFAULT_T = 50;
+
+ /**
+ * extension degree of the finite field GF(2^m)
+ */
+ private int m;
+
+ /**
+ * error correction capability of the code
+ */
+ private int t;
+
+ /**
+ * length of the code
+ */
+ private int n;
+
+ /**
+ * the field polynomial
+ */
+ private int fieldPoly;
+
+ /**
+ * Constructor. Set the default parameters: extension degree.
+ */
+ public McElieceParameters()
+ {
+ this(DEFAULT_M, DEFAULT_T);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param keysize the length of a Goppa code
+ * @throws IllegalArgumentException if keysize < 1.
+ */
+ public McElieceParameters(int keysize)
+ throws IllegalArgumentException
+ {
+ if (keysize < 1)
+ {
+ throw new IllegalArgumentException("key size must be positive");
+ }
+ m = 0;
+ n = 1;
+ while (n < keysize)
+ {
+ n <<= 1;
+ m++;
+ }
+ t = n >>> 1;
+ t /= m;
+ fieldPoly = PolynomialRingGF2.getIrreduciblePolynomial(m);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param m degree of the finite field GF(2^m)
+ * @param t error correction capability of the code
+ * @throws IllegalArgumentException if m < 1 or m > 32 or
+ * t < 0 or t > n.
+ */
+ public McElieceParameters(int m, int t)
+ throws IllegalArgumentException
+ {
+ if (m < 1)
+ {
+ throw new IllegalArgumentException("m must be positive");
+ }
+ if (m > 32)
+ {
+ throw new IllegalArgumentException("m is too large");
+ }
+ this.m = m;
+ n = 1 << m;
+ if (t < 0)
+ {
+ throw new IllegalArgumentException("t must be positive");
+ }
+ if (t > n)
+ {
+ throw new IllegalArgumentException("t must be less than n = 2^m");
+ }
+ this.t = t;
+ fieldPoly = PolynomialRingGF2.getIrreduciblePolynomial(m);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param m degree of the finite field GF(2^m)
+ * @param t error correction capability of the code
+ * @param poly the field polynomial
+ * @throws IllegalArgumentException if m < 1 or m > 32 or
+ * t < 0 or t > n or
+ * poly is not an irreducible field polynomial.
+ */
+ public McElieceParameters(int m, int t, int poly)
+ throws IllegalArgumentException
+ {
+ this.m = m;
+ if (m < 1)
+ {
+ throw new IllegalArgumentException("m must be positive");
+ }
+ if (m > 32)
+ {
+ throw new IllegalArgumentException(" m is too large");
+ }
+ this.n = 1 << m;
+ this.t = t;
+ if (t < 0)
+ {
+ throw new IllegalArgumentException("t must be positive");
+ }
+ if (t > n)
+ {
+ throw new IllegalArgumentException("t must be less than n = 2^m");
+ }
+ if ((PolynomialRingGF2.degree(poly) == m)
+ && (PolynomialRingGF2.isIrreducible(poly)))
+ {
+ this.fieldPoly = poly;
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "polynomial is not a field polynomial for GF(2^m)");
+ }
+ }
+
+ /**
+ * @return the extension degree of the finite field GF(2^m)
+ */
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the field polynomial
+ */
+ public int getFieldPoly()
+ {
+ return fieldPoly;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
new file mode 100644
index 000000000..ba58258bd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
@@ -0,0 +1,241 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+
+/**
+ * This class implements the Pointcheval conversion of the McEliecePKCS.
+ * Pointcheval presents a generic technique to make a CCA2-secure cryptosystem
+ * from any partially trapdoor one-way function in the random oracle model. For
+ * details, see D. Engelbert, R. Overbeck, A. Schmidt, "A summary of the
+ * development of the McEliece Cryptosystem", technical report.
+ */
+public class McEliecePointchevalCipher
+ implements MessageEncryptor
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.2";
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+ McElieceCCA2KeyParameters key;
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceCCA2KeyParameters object
+ * @return the key size of the given key object
+ * @throws IllegalArgumentException if the key is invalid
+ */
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ throws IllegalArgumentException
+ {
+
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ protected int decryptOutputSize(int inLen)
+ {
+ return 0;
+ }
+
+ protected int encryptOutputSize(int inLen)
+ {
+ return 0;
+ }
+
+
+ public void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ }
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ k = privKey.getK();
+ t = privKey.getT();
+ }
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ int kDiv8 = k >> 3;
+
+ // generate random r of length k div 8 bytes
+ byte[] r = new byte[kDiv8];
+ sr.nextBytes(r);
+
+ // generate random vector r' of length k bits
+ GF2Vector rPrime = new GF2Vector(k, sr);
+
+ // convert r' to byte array
+ byte[] rPrimeBytes = rPrime.getEncoded();
+
+ // compute (input||r)
+ byte[] mr = ByteUtils.concatenate(input, r);
+
+ // compute H(input||r)
+ messDigest.update(mr, 0, mr.length);
+ byte[] hmr = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hmr, 0);
+
+
+ // convert H(input||r) to error vector z
+ GF2Vector z = Conversions.encode(n, t, hmr);
+
+ // compute c1 = E(rPrime, z)
+ byte[] c1 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key, rPrime,
+ z).getEncoded();
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrimeBytes);
+
+ // generate random c2
+ byte[] c2 = new byte[input.length + kDiv8];
+ sr0.nextBytes(c2);
+
+ // XOR with input
+ for (int i = 0; i < input.length; i++)
+ {
+ c2[i] ^= input[i];
+ }
+ // XOR with r
+ for (int i = 0; i < kDiv8; i++)
+ {
+ c2[input.length + i] ^= r[i];
+ }
+
+ // return (c1||c2)
+ return ByteUtils.concatenate(c1, c2);
+ }
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int c1Len = (n + 7) >> 3;
+ int c2Len = input.length - c1Len;
+
+ // split cipher text (c1||c2)
+ byte[][] c1c2 = ByteUtils.split(input, c1Len);
+ byte[] c1 = c1c2[0];
+ byte[] c2 = c1c2[1];
+
+ // decrypt c1 ...
+ GF2Vector c1Vec = GF2Vector.OS2VP(n, c1);
+ GF2Vector[] c1Dec = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ c1Vec);
+ byte[] rPrimeBytes = c1Dec[0].getEncoded();
+ // ... and obtain error vector z
+ GF2Vector z = c1Dec[1];
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrimeBytes);
+
+ // generate random sequence
+ byte[] mrBytes = new byte[c2Len];
+ sr0.nextBytes(mrBytes);
+
+ // XOR with c2 to obtain (m||r)
+ for (int i = 0; i < c2Len; i++)
+ {
+ mrBytes[i] ^= c2[i];
+ }
+
+ // compute H(m||r)
+ messDigest.update(mrBytes, 0, mrBytes.length);
+ byte[] hmr = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hmr, 0);
+
+ // compute Conv(H(m||r))
+ c1Vec = Conversions.encode(n, t, hmr);
+
+ // check that Conv(H(m||r)) = z
+ if (!c1Vec.equals(z))
+ {
+ throw new Exception("Bad Padding: Invalid ciphertext.");
+ }
+
+ // split (m||r) to obtain m
+ int kDiv8 = k >> 3;
+ byte[][] mr = ByteUtils.split(mrBytes, c2Len - kDiv8);
+
+ // return plain text m
+ return mr[0];
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java
new file mode 100644
index 000000000..bcfd1d875
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McEliecePointchevalDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McEliecePointchevalDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePointchevalDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePointchevalDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java
new file mode 100644
index 000000000..3c4ef3247
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java
@@ -0,0 +1,197 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+
+public class McEliecePrivateKeyParameters
+ extends McElieceKeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the dimension of the code, where k >= n - mt
+ private int k;
+
+ // the underlying finite field
+ private GF2mField field;
+
+ // the irreducible Goppa polynomial
+ private PolynomialGF2mSmallM goppaPoly;
+
+ // a k x k random binary non-singular matrix
+ private GF2Matrix sInv;
+
+ // the permutation used to generate the systematic check matrix
+ private Permutation p1;
+
+ // the permutation used to compute the public generator matrix
+ private Permutation p2;
+
+ // the canonical check matrix of the code
+ private GF2Matrix h;
+
+ // the matrix used to compute square roots in (GF(2^m))^t
+ private PolynomialGF2mSmallM[] qInv;
+
+ /**
+ * Constructor.
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param field the field polynomial defining the finite field
+ * GF(2m)
+ * @param goppaPoly the irreducible Goppa polynomial
+ * @param sInv the matrix S-1
+ * @param p1 the permutation used to generate the systematic check
+ * matrix
+ * @param p2 the permutation used to compute the public generator
+ * matrix
+ * @param h the canonical check matrix
+ * @param qInv the matrix used to compute square roots in
+ * (GF(2m))t
+ * @param params McElieceParameters
+ */
+ public McEliecePrivateKeyParameters(String oid, int n, int k, GF2mField field,
+ PolynomialGF2mSmallM goppaPoly, GF2Matrix sInv, Permutation p1,
+ Permutation p2, GF2Matrix h, PolynomialGF2mSmallM[] qInv, McElieceParameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.k = k;
+ this.n = n;
+ this.field = field;
+ this.goppaPoly = goppaPoly;
+ this.sInv = sInv;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.h = h;
+ this.qInv = qInv;
+ }
+
+ /**
+ * Constructor (used by the {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param encField the encoded field polynomial defining the finite field
+ * GF(2m)
+ * @param encGoppaPoly the encoded irreducible Goppa polynomial
+ * @param encSInv the encoded matrix S-1
+ * @param encP1 the encoded permutation used to generate the systematic
+ * check matrix
+ * @param encP2 the encoded permutation used to compute the public
+ * generator matrix
+ * @param encH the encoded canonical check matrix
+ * @param encQInv the encoded matrix used to compute square roots in
+ * (GF(2m))t
+ * @param params McElieceParameters
+ */
+ public McEliecePrivateKeyParameters(String oid, int n, int k, byte[] encField,
+ byte[] encGoppaPoly, byte[] encSInv, byte[] encP1, byte[] encP2,
+ byte[] encH, byte[][] encQInv, McElieceParameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ field = new GF2mField(encField);
+ goppaPoly = new PolynomialGF2mSmallM(field, encGoppaPoly);
+ sInv = new GF2Matrix(encSInv);
+ p1 = new Permutation(encP1);
+ p2 = new Permutation(encP2);
+ h = new GF2Matrix(encH);
+ qInv = new PolynomialGF2mSmallM[encQInv.length];
+ for (int i = 0; i < encQInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encQInv[i]);
+ }
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return k;
+ }
+
+ /**
+ * @return the finite field GF(2m)
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return the irreducible Goppa polynomial
+ */
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return goppaPoly;
+ }
+
+ /**
+ * @return the k x k random binary non-singular matrix S^-1
+ */
+ public GF2Matrix getSInv()
+ {
+ return sInv;
+ }
+
+ /**
+ * @return the permutation used to generate the systematic check matrix
+ */
+ public Permutation getP1()
+ {
+ return p1;
+ }
+
+ /**
+ * @return the permutation used to compute the public generator matrix
+ */
+ public Permutation getP2()
+ {
+ return p2;
+ }
+
+ /**
+ * @return the canonical check matrix H
+ */
+ public GF2Matrix getH()
+ {
+ return h;
+ }
+
+ /**
+ * @return the matrix used to compute square roots in
+ * (GF(2m))t
+ */
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ return qInv;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java
new file mode 100644
index 000000000..9f0112434
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java
@@ -0,0 +1,96 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+
+public class McEliecePublicKeyParameters
+ extends McElieceKeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability of the code
+ private int t;
+
+ // the generator matrix
+ private GF2Matrix g;
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param t the error correction capability of the code
+ * @param g the generator matrix
+ * @param params McElieceParameters
+ */
+ public McEliecePublicKeyParameters(String oid, int n, int t, GF2Matrix g, McElieceParameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.g = new GF2Matrix(g);
+ }
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param t the error correction capability of the code
+ * @param encG the encoded generator matrix
+ * @param params McElieceParameters
+ */
+ public McEliecePublicKeyParameters(String oid, int t, int n, byte[] encG, McElieceParameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.g = new GF2Matrix(encG);
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getG()
+ {
+ return g;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return g.getNumRows();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java
new file mode 100644
index 000000000..01caf9700
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java
@@ -0,0 +1,239 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.util.Arrays;
+
+/**
+ * An implementation of the Index Generation Function in IEEE P1363.1.
+ */
+public class IndexGenerator
+{
+ private byte[] seed;
+ private int N;
+ private int c;
+ private int minCallsR;
+ private int totLen;
+ private int remLen;
+ private BitString buf;
+ private int counter;
+ private boolean initialized;
+ private Digest hashAlg;
+ private int hLen;
+
+ /**
+ * Constructs a new index generator.
+ *
+ * @param seed a seed of arbitrary length to initialize the index generator with
+ * @param params NtruEncrypt parameters
+ */
+ IndexGenerator(byte[] seed, NTRUEncryptionParameters params)
+ {
+ this.seed = seed;
+ N = params.N;
+ c = params.c;
+ minCallsR = params.minCallsR;
+
+ totLen = 0;
+ remLen = 0;
+ counter = 0;
+ hashAlg = params.hashAlg;
+
+ hLen = hashAlg.getDigestSize(); // hash length
+ initialized = false;
+ }
+
+ /**
+ * Returns a number i
such that 0 <= i < N
.
+ *
+ * @return
+ */
+ int nextIndex()
+ {
+ if (!initialized)
+ {
+ buf = new BitString();
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+ while (counter < minCallsR)
+ {
+ appendHash(buf, hash);
+ counter++;
+ }
+ totLen = minCallsR * 8 * hLen;
+ remLen = totLen;
+ initialized = true;
+ }
+
+ while (true)
+ {
+ totLen += c;
+ BitString M = buf.getTrailing(remLen);
+ if (remLen < c)
+ {
+ int tmpLen = c - remLen;
+ int cThreshold = counter + (tmpLen + hLen - 1) / hLen;
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+ while (counter < cThreshold)
+ {
+ appendHash(M, hash);
+ counter++;
+ if (tmpLen > 8 * hLen)
+ {
+ tmpLen -= 8 * hLen;
+ }
+ }
+ remLen = 8 * hLen - tmpLen;
+ buf = new BitString();
+ buf.appendBits(hash);
+ }
+ else
+ {
+ remLen -= c;
+ }
+
+ int i = M.getLeadingAsInt(c); // assume c<32
+ if (i < (1 << c) - ((1 << c) % N))
+ {
+ return i % N;
+ }
+ }
+ }
+
+ private void appendHash(BitString m, byte[] hash)
+ {
+ hashAlg.update(seed, 0, seed.length);
+
+ putInt(hashAlg, counter);
+
+ hashAlg.doFinal(hash, 0);
+
+ m.appendBits(hash);
+ }
+
+ private void putInt(Digest hashAlg, int counter)
+ {
+ hashAlg.update((byte)(counter >> 24));
+ hashAlg.update((byte)(counter >> 16));
+ hashAlg.update((byte)(counter >> 8));
+ hashAlg.update((byte)counter);
+ }
+
+ /**
+ * Represents a string of bits and supports appending, reading the head, and reading the tail.
+ */
+ public static class BitString
+ {
+ byte[] bytes = new byte[4];
+ int numBytes; // includes the last byte even if only some of its bits are used
+ int lastByteBits; // lastByteBits <= 8
+
+ /**
+ * Appends all bits in a byte array to the end of the bit string.
+ *
+ * @param bytes a byte array
+ */
+ void appendBits(byte[] bytes)
+ {
+ for (int i = 0; i != bytes.length; i++)
+ {
+ appendBits(bytes[i]);
+ }
+ }
+
+ /**
+ * Appends all bits in a byte to the end of the bit string.
+ *
+ * @param b a byte
+ */
+ public void appendBits(byte b)
+ {
+ if (numBytes == bytes.length)
+ {
+ bytes = copyOf(bytes, 2 * bytes.length);
+ }
+
+ if (numBytes == 0)
+ {
+ numBytes = 1;
+ bytes[0] = b;
+ lastByteBits = 8;
+ }
+ else if (lastByteBits == 8)
+ {
+ bytes[numBytes++] = b;
+ }
+ else
+ {
+ int s = 8 - lastByteBits;
+ bytes[numBytes - 1] |= (b & 0xFF) << lastByteBits;
+ bytes[numBytes++] = (byte)((b & 0xFF) >> s);
+ }
+ }
+
+ /**
+ * Returns the last numBits
bits from the end of the bit string.
+ *
+ * @param numBits number of bits
+ * @return a new BitString
of length numBits
+ */
+ public BitString getTrailing(int numBits)
+ {
+ BitString newStr = new BitString();
+ newStr.numBytes = (numBits + 7) / 8;
+ newStr.bytes = new byte[newStr.numBytes];
+ for (int i = 0; i < newStr.numBytes; i++)
+ {
+ newStr.bytes[i] = bytes[i];
+ }
+
+ newStr.lastByteBits = numBits % 8;
+ if (newStr.lastByteBits == 0)
+ {
+ newStr.lastByteBits = 8;
+ }
+ else
+ {
+ int s = 32 - newStr.lastByteBits;
+ newStr.bytes[newStr.numBytes - 1] = (byte)(newStr.bytes[newStr.numBytes - 1] << s >>> s);
+ }
+
+ return newStr;
+ }
+
+ /**
+ * Returns up to 32 bits from the beginning of the bit string.
+ *
+ * @param numBits number of bits
+ * @return an int
whose lower numBits
bits are the beginning of the bit string
+ */
+ public int getLeadingAsInt(int numBits)
+ {
+ int startBit = (numBytes - 1) * 8 + lastByteBits - numBits;
+ int startByte = startBit / 8;
+
+ int startBitInStartByte = startBit % 8;
+ int sum = (bytes[startByte] & 0xFF) >>> startBitInStartByte;
+ int shift = 8 - startBitInStartByte;
+ for (int i = startByte + 1; i < numBytes; i++)
+ {
+ sum |= (bytes[i] & 0xFF) << shift;
+ shift += 8;
+ }
+
+ return sum;
+ }
+
+ public byte[] getBytes()
+ {
+ return Arrays.clone(bytes);
+ }
+ }
+
+ private static byte[] copyOf(byte[] src, int len)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(src, 0, tmp, 0, len < src.length ? len : src.length);
+
+ return tmp;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
new file mode 100644
index 000000000..ce9d813f9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
@@ -0,0 +1,463 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruEncrypt. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUEncryptionKeyGenerationParameters
+ extends KeyGenerationParameters
+ implements Cloneable
+{
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is optimized for key size.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1087EP2 = new NTRUEncryptionKeyGenerationParameters(1087, 2048, 120, 120, 256, 13, 25, 14, true, new byte[]{0, 6, 3}, true, false, new SHA512Digest());
+
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is a tradeoff between key size and encryption/decryption speed.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1171EP1 = new NTRUEncryptionKeyGenerationParameters(1171, 2048, 106, 106, 256, 13, 20, 15, true, new byte[]{0, 6, 4}, true, false, new SHA512Digest());
+
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is optimized for encryption/decryption speed.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1499EP1 = new NTRUEncryptionKeyGenerationParameters(1499, 2048, 79, 79, 256, 13, 17, 19, true, new byte[]{0, 6, 5}, true, false, new SHA512Digest());
+
+ /**
+ * A parameter set that gives 128 bits of security and uses simple ternary polynomials.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_439 = new NTRUEncryptionKeyGenerationParameters(439, 2048, 146, 130, 128, 9, 32, 9, true, new byte[]{0, 7, 101}, true, false, new SHA256Digest());
+
+ /**
+ * Like APR2011_439
, this parameter set gives 128 bits of security but uses product-form polynomials and f=1+pF
.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_439_FAST = new NTRUEncryptionKeyGenerationParameters(439, 2048, 9, 8, 5, 130, 128, 9, 32, 9, true, new byte[]{0, 7, 101}, true, true, new SHA256Digest());
+
+ /**
+ * A parameter set that gives 256 bits of security and uses simple ternary polynomials.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_743 = new NTRUEncryptionKeyGenerationParameters(743, 2048, 248, 220, 256, 10, 27, 14, true, new byte[]{0, 7, 105}, false, false, new SHA512Digest());
+
+ /**
+ * Like APR2011_743
, this parameter set gives 256 bits of security but uses product-form polynomials and f=1+pF
.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_743_FAST = new NTRUEncryptionKeyGenerationParameters(743, 2048, 11, 11, 15, 220, 256, 10, 27, 14, true, new byte[]{0, 7, 105}, false, true, new SHA512Digest());
+
+ public int N, q, df, df1, df2, df3;
+ public int dr;
+ public int dr1;
+ public int dr2;
+ public int dr3;
+ public int dg;
+ int llen;
+ public int maxMsgLenBytes;
+ public int db;
+ public int bufferLenBits;
+ int bufferLenTrits;
+ public int dm0;
+ public int pkLen;
+ public int c;
+ public int minCallsR;
+ public int minCallsMask;
+ public boolean hashSeed;
+ public byte[] oid;
+ public boolean sparse;
+ public boolean fastFp;
+ public int polyType;
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. polyType=SIMPLE).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df number of ones in the private polynomial f
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial m'
in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether f=1+p*F
for a ternary F
(true) or f
is ternary (false)
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
. The MessageDigest
must support the getDigestLength()
method.
+ */
+ public NTRUEncryptionKeyGenerationParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ super(new SecureRandom(), db);
+ this.N = N;
+ this.q = q;
+ this.df = df;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. polyType=PRODUCT).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df1 number of ones in the private polynomial f1
+ * @param df2 number of ones in the private polynomial f2
+ * @param df3 number of ones in the private polynomial f3
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial m'
in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether f=1+p*F
for a ternary F
(true) or f
is ternary (false)
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
+ */
+ public NTRUEncryptionKeyGenerationParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ super(new SecureRandom(), db);
+
+ this.N = N;
+ this.q = q;
+ this.df1 = df1;
+ this.df2 = df2;
+ this.df3 = df3;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ dr = df;
+ dr1 = df1;
+ dr2 = df2;
+ dr3 = df3;
+ dg = N / 3;
+ llen = 1; // ceil(log2(maxMsgLenBytes))
+ maxMsgLenBytes = N * 3 / 2 / 8 - llen - db / 8 - 1;
+ bufferLenBits = (N * 3 / 2 + 7) / 8 * 8 + 1;
+ bufferLenTrits = N - 1;
+ pkLen = db;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws java.io.IOException
+ */
+ public NTRUEncryptionKeyGenerationParameters(InputStream is)
+ throws IOException
+ {
+ super(new SecureRandom(), -1);
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ df = dis.readInt();
+ df1 = dis.readInt();
+ df2 = dis.readInt();
+ df3 = dis.readInt();
+ db = dis.readInt();
+ dm0 = dis.readInt();
+ c = dis.readInt();
+ minCallsR = dis.readInt();
+ minCallsMask = dis.readInt();
+ hashSeed = dis.readBoolean();
+ oid = new byte[3];
+ dis.read(oid);
+ sparse = dis.readBoolean();
+ fastFp = dis.readBoolean();
+ polyType = dis.read();
+
+ String alg = dis.readUTF();
+
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+
+ init();
+ }
+
+ public NTRUEncryptionParameters getEncryptionParameters()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ public NTRUEncryptionKeyGenerationParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionKeyGenerationParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionKeyGenerationParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ /**
+ * Returns the maximum length a plaintext message can be with this parameter set.
+ *
+ * @return the maximum length in bytes
+ */
+ public int getMaxMessageLength()
+ {
+ return maxMsgLenBytes;
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws java.io.IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(df);
+ dos.writeInt(df1);
+ dos.writeInt(df2);
+ dos.writeInt(df3);
+ dos.writeInt(db);
+ dos.writeInt(dm0);
+ dos.writeInt(c);
+ dos.writeInt(minCallsR);
+ dos.writeInt(minCallsMask);
+ dos.writeBoolean(hashSeed);
+ dos.write(oid);
+ dos.writeBoolean(sparse);
+ dos.writeBoolean(fastFp);
+ dos.write(polyType);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + bufferLenBits;
+ result = prime * result + bufferLenTrits;
+ result = prime * result + c;
+ result = prime * result + db;
+ result = prime * result + df;
+ result = prime * result + df1;
+ result = prime * result + df2;
+ result = prime * result + df3;
+ result = prime * result + dg;
+ result = prime * result + dm0;
+ result = prime * result + dr;
+ result = prime * result + dr1;
+ result = prime * result + dr2;
+ result = prime * result + dr3;
+ result = prime * result + (fastFp ? 1231 : 1237);
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + (hashSeed ? 1231 : 1237);
+ result = prime * result + llen;
+ result = prime * result + maxMsgLenBytes;
+ result = prime * result + minCallsMask;
+ result = prime * result + minCallsR;
+ result = prime * result + Arrays.hashCode(oid);
+ result = prime * result + pkLen;
+ result = prime * result + polyType;
+ result = prime * result + q;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUEncryptionKeyGenerationParameters other = (NTRUEncryptionKeyGenerationParameters)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (bufferLenBits != other.bufferLenBits)
+ {
+ return false;
+ }
+ if (bufferLenTrits != other.bufferLenTrits)
+ {
+ return false;
+ }
+ if (c != other.c)
+ {
+ return false;
+ }
+ if (db != other.db)
+ {
+ return false;
+ }
+ if (df != other.df)
+ {
+ return false;
+ }
+ if (df1 != other.df1)
+ {
+ return false;
+ }
+ if (df2 != other.df2)
+ {
+ return false;
+ }
+ if (df3 != other.df3)
+ {
+ return false;
+ }
+ if (dg != other.dg)
+ {
+ return false;
+ }
+ if (dm0 != other.dm0)
+ {
+ return false;
+ }
+ if (dr != other.dr)
+ {
+ return false;
+ }
+ if (dr1 != other.dr1)
+ {
+ return false;
+ }
+ if (dr2 != other.dr2)
+ {
+ return false;
+ }
+ if (dr3 != other.dr3)
+ {
+ return false;
+ }
+ if (fastFp != other.fastFp)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (hashSeed != other.hashSeed)
+ {
+ return false;
+ }
+ if (llen != other.llen)
+ {
+ return false;
+ }
+ if (maxMsgLenBytes != other.maxMsgLenBytes)
+ {
+ return false;
+ }
+ if (minCallsMask != other.minCallsMask)
+ {
+ return false;
+ }
+ if (minCallsR != other.minCallsR)
+ {
+ return false;
+ }
+ if (!Arrays.equals(oid, other.oid))
+ {
+ return false;
+ }
+ if (pkLen != other.pkLen)
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder output = new StringBuilder("EncryptionParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE df=" + df);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT df1=" + df1 + " df2=" + df2 + " df3=" + df3);
+ }
+ output.append(" dm0=" + dm0 + " db=" + db + " c=" + c + " minCallsR=" + minCallsR + " minCallsMask=" + minCallsMask +
+ " hashSeed=" + hashSeed + " hashAlg=" + hashAlg + " oid=" + Arrays.toString(oid) + " sparse=" + sparse + ")");
+ return output.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java
new file mode 100644
index 000000000..bf63c33f0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java
@@ -0,0 +1,113 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.util.Util;
+
+/**
+ * Generates key pairs.
+ * The parameter p is hardcoded to 3.
+ */
+public class NTRUEncryptionKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private NTRUEncryptionKeyGenerationParameters params;
+
+ /**
+ * Constructs a new instance with a set of encryption parameters.
+ *
+ * @param param encryption parameters
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ this.params = (NTRUEncryptionKeyGenerationParameters)param;
+ }
+
+ /**
+ * Generates a new encryption key pair.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ int N = params.N;
+ int q = params.q;
+ int df = params.df;
+ int df1 = params.df1;
+ int df2 = params.df2;
+ int df3 = params.df3;
+ int dg = params.dg;
+ boolean fastFp = params.fastFp;
+ boolean sparse = params.sparse;
+
+ Polynomial t;
+ IntegerPolynomial fq;
+ IntegerPolynomial fp = null;
+
+ // choose a random f that is invertible mod 3 and q
+ while (true)
+ {
+ IntegerPolynomial f;
+
+ // choose random t, calculate f and fp
+ if (fastFp)
+ {
+ // if fastFp=true, f is always invertible mod 3
+ t = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? Util.generateRandomTernary(N, df, df, sparse, params.getRandom()) : ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3, params.getRandom());
+ f = t.toIntegerPolynomial();
+ f.mult(3);
+ f.coeffs[0] += 1;
+ }
+ else
+ {
+ t = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? Util.generateRandomTernary(N, df, df - 1, sparse, params.getRandom()) : ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3 - 1, params.getRandom());
+ f = t.toIntegerPolynomial();
+ fp = f.invertF3();
+ if (fp == null)
+ {
+ continue;
+ }
+ }
+
+ fq = f.invertFq(q);
+ if (fq == null)
+ {
+ continue;
+ }
+ break;
+ }
+
+ // if fastFp=true, fp=1
+ if (fastFp)
+ {
+ fp = new IntegerPolynomial(N);
+ fp.coeffs[0] = 1;
+ }
+
+ // choose a random g that is invertible mod q
+ DenseTernaryPolynomial g;
+ while (true)
+ {
+ g = DenseTernaryPolynomial.generateRandom(N, dg, dg - 1, params.getRandom());
+ if (g.invertFq(q) != null)
+ {
+ break;
+ }
+ }
+
+ IntegerPolynomial h = g.mult(fq, q);
+ h.mult3(q);
+ h.ensurePositive(q);
+ g.clear();
+ fq.clear();
+
+ NTRUEncryptionPrivateKeyParameters priv = new NTRUEncryptionPrivateKeyParameters(h, t, fp, params.getEncryptionParameters());
+ NTRUEncryptionPublicKeyParameters pub = new NTRUEncryptionPublicKeyParameters(h, params.getEncryptionParameters());
+ return new AsymmetricCipherKeyPair(pub, priv);
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java
new file mode 100644
index 000000000..a22eb286a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java
@@ -0,0 +1,20 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+public class NTRUEncryptionKeyParameters
+ extends AsymmetricKeyParameter
+{
+ final protected NTRUEncryptionParameters params;
+
+ public NTRUEncryptionKeyParameters(boolean privateKey, NTRUEncryptionParameters params)
+ {
+ super(privateKey);
+ this.params = params;
+ }
+
+ public NTRUEncryptionParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java
new file mode 100644
index 000000000..8f5f9a9b6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java
@@ -0,0 +1,410 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruEncrypt. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUEncryptionParameters
+ implements Cloneable
+{
+
+ public int N, q, df, df1, df2, df3;
+ public int dr;
+ public int dr1;
+ public int dr2;
+ public int dr3;
+ public int dg;
+ int llen;
+ public int maxMsgLenBytes;
+ public int db;
+ public int bufferLenBits;
+ int bufferLenTrits;
+ public int dm0;
+ public int pkLen;
+ public int c;
+ public int minCallsR;
+ public int minCallsMask;
+ public boolean hashSeed;
+ public byte[] oid;
+ public boolean sparse;
+ public boolean fastFp;
+ public int polyType;
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. polyType=SIMPLE).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df number of ones in the private polynomial f
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial m'
in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether f=1+p*F
for a ternary F
(true) or f
is ternary (false)
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
. The MessageDigest
must support the getDigestLength()
method.
+ */
+ public NTRUEncryptionParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.df = df;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. polyType=PRODUCT).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df1 number of ones in the private polynomial f1
+ * @param df2 number of ones in the private polynomial f2
+ * @param df3 number of ones in the private polynomial f3
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial m'
in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether f=1+p*F
for a ternary F
(true) or f
is ternary (false)
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
+ */
+ public NTRUEncryptionParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.df1 = df1;
+ this.df2 = df2;
+ this.df3 = df3;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ dr = df;
+ dr1 = df1;
+ dr2 = df2;
+ dr3 = df3;
+ dg = N / 3;
+ llen = 1; // ceil(log2(maxMsgLenBytes))
+ maxMsgLenBytes = N * 3 / 2 / 8 - llen - db / 8 - 1;
+ bufferLenBits = (N * 3 / 2 + 7) / 8 * 8 + 1;
+ bufferLenTrits = N - 1;
+ pkLen = db;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws IOException
+ */
+ public NTRUEncryptionParameters(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ df = dis.readInt();
+ df1 = dis.readInt();
+ df2 = dis.readInt();
+ df3 = dis.readInt();
+ db = dis.readInt();
+ dm0 = dis.readInt();
+ c = dis.readInt();
+ minCallsR = dis.readInt();
+ minCallsMask = dis.readInt();
+ hashSeed = dis.readBoolean();
+ oid = new byte[3];
+ dis.read(oid);
+ sparse = dis.readBoolean();
+ fastFp = dis.readBoolean();
+ polyType = dis.read();
+
+ String alg = dis.readUTF();
+
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+
+ init();
+ }
+
+ public NTRUEncryptionParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ /**
+ * Returns the maximum length a plaintext message can be with this parameter set.
+ *
+ * @return the maximum length in bytes
+ */
+ public int getMaxMessageLength()
+ {
+ return maxMsgLenBytes;
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(df);
+ dos.writeInt(df1);
+ dos.writeInt(df2);
+ dos.writeInt(df3);
+ dos.writeInt(db);
+ dos.writeInt(dm0);
+ dos.writeInt(c);
+ dos.writeInt(minCallsR);
+ dos.writeInt(minCallsMask);
+ dos.writeBoolean(hashSeed);
+ dos.write(oid);
+ dos.writeBoolean(sparse);
+ dos.writeBoolean(fastFp);
+ dos.write(polyType);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + bufferLenBits;
+ result = prime * result + bufferLenTrits;
+ result = prime * result + c;
+ result = prime * result + db;
+ result = prime * result + df;
+ result = prime * result + df1;
+ result = prime * result + df2;
+ result = prime * result + df3;
+ result = prime * result + dg;
+ result = prime * result + dm0;
+ result = prime * result + dr;
+ result = prime * result + dr1;
+ result = prime * result + dr2;
+ result = prime * result + dr3;
+ result = prime * result + (fastFp ? 1231 : 1237);
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + (hashSeed ? 1231 : 1237);
+ result = prime * result + llen;
+ result = prime * result + maxMsgLenBytes;
+ result = prime * result + minCallsMask;
+ result = prime * result + minCallsR;
+ result = prime * result + Arrays.hashCode(oid);
+ result = prime * result + pkLen;
+ result = prime * result + polyType;
+ result = prime * result + q;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUEncryptionParameters other = (NTRUEncryptionParameters)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (bufferLenBits != other.bufferLenBits)
+ {
+ return false;
+ }
+ if (bufferLenTrits != other.bufferLenTrits)
+ {
+ return false;
+ }
+ if (c != other.c)
+ {
+ return false;
+ }
+ if (db != other.db)
+ {
+ return false;
+ }
+ if (df != other.df)
+ {
+ return false;
+ }
+ if (df1 != other.df1)
+ {
+ return false;
+ }
+ if (df2 != other.df2)
+ {
+ return false;
+ }
+ if (df3 != other.df3)
+ {
+ return false;
+ }
+ if (dg != other.dg)
+ {
+ return false;
+ }
+ if (dm0 != other.dm0)
+ {
+ return false;
+ }
+ if (dr != other.dr)
+ {
+ return false;
+ }
+ if (dr1 != other.dr1)
+ {
+ return false;
+ }
+ if (dr2 != other.dr2)
+ {
+ return false;
+ }
+ if (dr3 != other.dr3)
+ {
+ return false;
+ }
+ if (fastFp != other.fastFp)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (hashSeed != other.hashSeed)
+ {
+ return false;
+ }
+ if (llen != other.llen)
+ {
+ return false;
+ }
+ if (maxMsgLenBytes != other.maxMsgLenBytes)
+ {
+ return false;
+ }
+ if (minCallsMask != other.minCallsMask)
+ {
+ return false;
+ }
+ if (minCallsR != other.minCallsR)
+ {
+ return false;
+ }
+ if (!Arrays.equals(oid, other.oid))
+ {
+ return false;
+ }
+ if (pkLen != other.pkLen)
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder output = new StringBuilder("EncryptionParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE df=" + df);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT df1=" + df1 + " df2=" + df2 + " df3=" + df3);
+ }
+ output.append(" dm0=" + dm0 + " db=" + db + " c=" + c + " minCallsR=" + minCallsR + " minCallsMask=" + minCallsMask +
+ " hashSeed=" + hashSeed + " hashAlg=" + hashAlg + " oid=" + Arrays.toString(oid) + " sparse=" + sparse + ")");
+ return output.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java
new file mode 100644
index 000000000..9fe8259f3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java
@@ -0,0 +1,199 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+/**
+ * A NtruEncrypt private key is essentially a polynomial named f
+ * which takes different forms depending on whether product-form polynomials are used,
+ * and on fastP
+ * The inverse of f
modulo p
is precomputed on initialization.
+ */
+public class NTRUEncryptionPrivateKeyParameters
+ extends NTRUEncryptionKeyParameters
+{
+ public Polynomial t;
+ public IntegerPolynomial fp;
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new private key from a polynomial
+ *
+ * @param h the public polynomial for the key.
+ * @param t the polynomial which determines the key: if fastFp=true
, f=1+3t
; otherwise, f=t
+ * @param fp the inverse of f
+ * @param params the NtruEncrypt parameters to use
+ */
+ public NTRUEncryptionPrivateKeyParameters(IntegerPolynomial h, Polynomial t, IntegerPolynomial fp, NTRUEncryptionParameters params)
+ {
+ super(true, params);
+
+ this.h = h;
+ this.t = t;
+ this.fp = fp;
+ }
+
+ /**
+ * Converts a byte array to a polynomial f
and constructs a new private key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruEncrypt parameters to use
+ * @see #getEncoded()
+ */
+ public NTRUEncryptionPrivateKeyParameters(byte[] b, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ this(new ByteArrayInputStream(b), params);
+ }
+
+ /**
+ * Reads a polynomial f
from an input stream and constructs a new private key
+ *
+ * @param is an input stream
+ * @param params the NtruEncrypt parameters to use
+ * @see #writeTo(OutputStream)
+ */
+ public NTRUEncryptionPrivateKeyParameters(InputStream is, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ super(true, params);
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ int N = params.N;
+ int df1 = params.df1;
+ int df2 = params.df2;
+ int df3Ones = params.df3;
+ int df3NegOnes = params.fastFp ? params.df3 : params.df3 - 1;
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ t = ProductFormPolynomial.fromBinary(is, N, df1, df2, df3Ones, df3NegOnes);
+ }
+ else
+ {
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, params.N);
+ t = params.sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
+ }
+
+ init();
+ }
+
+ /**
+ * Initializes fp
from t.
+ */
+ private void init()
+ {
+ if (params.fastFp)
+ {
+ fp = new IntegerPolynomial(params.N);
+ fp.coeffs[0] = 1;
+ }
+ else
+ {
+ fp = t.toIntegerPolynomial().invertF3();
+ }
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ * @see #NTRUEncryptionPrivateKeyParameters(byte[], NTRUEncryptionParameters)
+ */
+ public byte[] getEncoded()
+ {
+ byte[] hBytes = h.toBinary(params.q);
+ byte[] tBytes;
+
+ if (t instanceof ProductFormPolynomial)
+ {
+ tBytes = ((ProductFormPolynomial)t).toBinary();
+ }
+ else
+ {
+ tBytes = t.toIntegerPolynomial().toBinary3Tight();
+ }
+
+ byte[] res = new byte[hBytes.length + tBytes.length];
+
+ System.arraycopy(hBytes, 0, res, 0, hBytes.length);
+ System.arraycopy(tBytes, 0, res, hBytes.length, tBytes.length);
+
+ return res;
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ * @see #NTRUEncryptionPrivateKeyParameters(InputStream, NTRUEncryptionParameters)
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ result = prime * result + ((t == null) ? 0 : t.hashCode());
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUEncryptionPrivateKeyParameters))
+ {
+ return false;
+ }
+ NTRUEncryptionPrivateKeyParameters other = (NTRUEncryptionPrivateKeyParameters)obj;
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ if (t == null)
+ {
+ if (other.t != null)
+ {
+ return false;
+ }
+ }
+ else if (!t.equals(other.t))
+ {
+ return false;
+ }
+ if (!h.equals(other.h))
+ {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java
new file mode 100644
index 000000000..0f3abd9cf
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java
@@ -0,0 +1,131 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+
+/**
+ * A NtruEncrypt public key is essentially a polynomial named h
.
+ */
+public class NTRUEncryptionPublicKeyParameters
+ extends NTRUEncryptionKeyParameters
+{
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new public key from a polynomial
+ *
+ * @param h the polynomial h
which determines the key
+ * @param params the NtruEncrypt parameters to use
+ */
+ public NTRUEncryptionPublicKeyParameters(IntegerPolynomial h, NTRUEncryptionParameters params)
+ {
+ super(false, params);
+
+ this.h = h;
+ }
+
+ /**
+ * Converts a byte array to a polynomial h
and constructs a new public key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruEncrypt parameters to use
+ * @see #getEncoded()
+ */
+ public NTRUEncryptionPublicKeyParameters(byte[] b, NTRUEncryptionParameters params)
+ {
+ super(false, params);
+
+ h = IntegerPolynomial.fromBinary(b, params.N, params.q);
+ }
+
+ /**
+ * Reads a polynomial h
from an input stream and constructs a new public key
+ *
+ * @param is an input stream
+ * @param params the NtruEncrypt parameters to use
+ * @see #writeTo(OutputStream)
+ */
+ public NTRUEncryptionPublicKeyParameters(InputStream is, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ super(false, params);
+
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ * @see #NTRUEncryptionPublicKeyParameters(byte[], NTRUEncryptionParameters)
+ */
+ public byte[] getEncoded()
+ {
+ return h.toBinary(params.q);
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ * @see #NTRUEncryptionPublicKeyParameters(InputStream, NTRUEncryptionParameters)
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUEncryptionPublicKeyParameters))
+ {
+ return false;
+ }
+ NTRUEncryptionPublicKeyParameters other = (NTRUEncryptionPublicKeyParameters)obj;
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java
new file mode 100644
index 000000000..ff7746204
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java
@@ -0,0 +1,495 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.TernaryPolynomial;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Encrypts, decrypts data and generates key pairs.
+ * The parameter p is hardcoded to 3.
+ */
+public class NTRUEngine
+ implements AsymmetricBlockCipher
+{
+ private boolean forEncryption;
+ private NTRUEncryptionParameters params;
+ private NTRUEncryptionPublicKeyParameters pubKey;
+ private NTRUEncryptionPrivateKeyParameters privKey;
+ private SecureRandom random;
+
+ /**
+ * Constructs a new instance with a set of encryption parameters.
+ *
+ */
+ public NTRUEngine()
+ {
+ }
+
+ public void init(boolean forEncryption, CipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+ if (forEncryption)
+ {
+ if (parameters instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)parameters;
+
+ this.random = p.getRandom();
+ this.pubKey = (NTRUEncryptionPublicKeyParameters)p.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.pubKey = (NTRUEncryptionPublicKeyParameters)parameters;
+ }
+
+ this.params = pubKey.getParameters();
+ }
+ else
+ {
+ this.privKey = (NTRUEncryptionPrivateKeyParameters)parameters;
+ this.params = privKey.getParameters();
+ }
+ }
+
+ public int getInputBlockSize()
+ {
+ return params.maxMsgLenBytes;
+ }
+
+ public int getOutputBlockSize()
+ {
+ return ((params.N * log2(params.q)) + 7) / 8;
+ }
+
+ public byte[] processBlock(byte[] in, int inOff, int len)
+ throws InvalidCipherTextException
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(in, inOff, tmp, 0, len);
+
+ if (forEncryption)
+ {
+ return encrypt(tmp, pubKey);
+ }
+ else
+ {
+ return decrypt(tmp, privKey);
+ }
+ }
+
+ /**
+ * Encrypts a message.
+ * See P1363.1 section 9.2.2.
+ *
+ * @param m The message to encrypt
+ * @param pubKey the public key to encrypt the message with
+ * @return the encrypted message
+ */
+ private byte[] encrypt(byte[] m, NTRUEncryptionPublicKeyParameters pubKey)
+ {
+ IntegerPolynomial pub = pubKey.h;
+ int N = params.N;
+ int q = params.q;
+
+ int maxLenBytes = params.maxMsgLenBytes;
+ int db = params.db;
+ int bufferLenBits = params.bufferLenBits;
+ int dm0 = params.dm0;
+ int pkLen = params.pkLen;
+ int minCallsMask = params.minCallsMask;
+ boolean hashSeed = params.hashSeed;
+ byte[] oid = params.oid;
+
+ int l = m.length;
+ if (maxLenBytes > 255)
+ {
+ throw new IllegalArgumentException("llen values bigger than 1 are not supported");
+ }
+ if (l > maxLenBytes)
+ {
+ throw new DataLengthException("Message too long: " + l + ">" + maxLenBytes);
+ }
+
+ while (true)
+ {
+ // M = b|octL|m|p0
+ byte[] b = new byte[db / 8];
+ random.nextBytes(b);
+ byte[] p0 = new byte[maxLenBytes + 1 - l];
+ byte[] M = new byte[bufferLenBits / 8];
+
+ System.arraycopy(b, 0, M, 0, b.length);
+ M[b.length] = (byte)l;
+ System.arraycopy(m, 0, M, b.length + 1, m.length);
+ System.arraycopy(p0, 0, M, b.length + 1 + m.length, p0.length);
+
+ IntegerPolynomial mTrin = IntegerPolynomial.fromBinary3Sves(M, N);
+
+ // sData = OID|m|b|hTrunc
+ byte[] bh = pub.toBinary(q);
+ byte[] hTrunc = copyOf(bh, pkLen / 8);
+ byte[] sData = buildSData(oid, m, l, b, hTrunc);
+
+ Polynomial r = generateBlindingPoly(sData, M);
+ IntegerPolynomial R = r.mult(pub, q);
+ IntegerPolynomial R4 = (IntegerPolynomial)R.clone();
+ R4.modPositive(4);
+ byte[] oR4 = R4.toBinary(4);
+ IntegerPolynomial mask = MGF(oR4, N, minCallsMask, hashSeed);
+ mTrin.add(mask);
+ mTrin.mod3();
+
+ if (mTrin.count(-1) < dm0)
+ {
+ continue;
+ }
+ if (mTrin.count(0) < dm0)
+ {
+ continue;
+ }
+ if (mTrin.count(1) < dm0)
+ {
+ continue;
+ }
+
+ R.add(mTrin, q);
+ R.ensurePositive(q);
+ return R.toBinary(q);
+ }
+ }
+
+ private byte[] buildSData(byte[] oid, byte[] m, int l, byte[] b, byte[] hTrunc)
+ {
+ byte[] sData = new byte[oid.length + l + b.length + hTrunc.length];
+
+ System.arraycopy(oid, 0, sData, 0, oid.length);
+ System.arraycopy(m, 0, sData, oid.length, m.length);
+ System.arraycopy(b, 0, sData, oid.length + m.length, b.length);
+ System.arraycopy(hTrunc, 0, sData, oid.length + m.length + b.length, hTrunc.length);
+ return sData;
+ }
+
+ protected IntegerPolynomial encrypt(IntegerPolynomial m, TernaryPolynomial r, IntegerPolynomial pubKey)
+ {
+ IntegerPolynomial e = r.mult(pubKey, params.q);
+ e.add(m, params.q);
+ e.ensurePositive(params.q);
+ return e;
+ }
+
+ /**
+ * Deterministically generates a blinding polynomial from a seed and a message representative.
+ *
+ * @param seed
+ * @param M message representative
+ * @return a blinding polynomial
+ */
+ private Polynomial generateBlindingPoly(byte[] seed, byte[] M)
+ {
+ IndexGenerator ig = new IndexGenerator(seed, params);
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ SparseTernaryPolynomial r1 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr1));
+ SparseTernaryPolynomial r2 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr2));
+ SparseTernaryPolynomial r3 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr3));
+ return new ProductFormPolynomial(r1, r2, r3);
+ }
+ else
+ {
+ int dr = params.dr;
+ boolean sparse = params.sparse;
+ int[] r = generateBlindingCoeffs(ig, dr);
+ if (sparse)
+ {
+ return new SparseTernaryPolynomial(r);
+ }
+ else
+ {
+ return new DenseTernaryPolynomial(r);
+ }
+ }
+ }
+
+ /**
+ * Generates an int
array containing dr
elements equal to 1
+ * and dr
elements equal to -1
using an index generator.
+ *
+ * @param ig an index generator
+ * @param dr number of ones / negative ones
+ * @return an array containing numbers between -1
and 1
+ */
+ private int[] generateBlindingCoeffs(IndexGenerator ig, int dr)
+ {
+ int N = params.N;
+
+ int[] r = new int[N];
+ for (int coeff = -1; coeff <= 1; coeff += 2)
+ {
+ int t = 0;
+ while (t < dr)
+ {
+ int i = ig.nextIndex();
+ if (r[i] == 0)
+ {
+ r[i] = coeff;
+ t++;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ /**
+ * An implementation of MGF-TP-1 from P1363.1 section 8.4.1.1.
+ *
+ * @param seed
+ * @param N
+ * @param minCallsR
+ * @param hashSeed whether to hash the seed
+ * @return
+ */
+ private IntegerPolynomial MGF(byte[] seed, int N, int minCallsR, boolean hashSeed)
+ {
+ Digest hashAlg = params.hashAlg;
+ int hashLen = hashAlg.getDigestSize();
+ byte[] buf = new byte[minCallsR * hashLen];
+ byte[] Z = hashSeed ? calcHash(hashAlg, seed) : seed;
+ int counter = 0;
+ while (counter < minCallsR)
+ {
+ hashAlg.update(Z, 0, Z.length);
+ putInt(hashAlg, counter);
+
+ byte[] hash = calcHash(hashAlg);
+ System.arraycopy(hash, 0, buf, counter * hashLen, hashLen);
+ counter++;
+ }
+
+ IntegerPolynomial i = new IntegerPolynomial(N);
+ while (true)
+ {
+ int cur = 0;
+ for (int index = 0; index != buf.length; index++)
+ {
+ int O = (int)buf[index] & 0xFF;
+ if (O >= 243) // 243 = 3^5
+ {
+ continue;
+ }
+
+ for (int terIdx = 0; terIdx < 4; terIdx++)
+ {
+ int rem3 = O % 3;
+ i.coeffs[cur] = rem3 - 1;
+ cur++;
+ if (cur == N)
+ {
+ return i;
+ }
+ O = (O - rem3) / 3;
+ }
+
+ i.coeffs[cur] = O - 1;
+ cur++;
+ if (cur == N)
+ {
+ return i;
+ }
+ }
+
+ if (cur >= N)
+ {
+ return i;
+ }
+
+ hashAlg.update(Z, 0, Z.length);
+ putInt(hashAlg, counter);
+
+ byte[] hash = calcHash(hashAlg);
+
+ buf = hash;
+
+ counter++;
+ }
+ }
+
+ private void putInt(Digest hashAlg, int counter)
+ {
+ hashAlg.update((byte)(counter >> 24));
+ hashAlg.update((byte)(counter >> 16));
+ hashAlg.update((byte)(counter >> 8));
+ hashAlg.update((byte)counter);
+ }
+
+ private byte[] calcHash(Digest hashAlg)
+ {
+ byte[] tmp = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(tmp, 0);
+
+ return tmp;
+ }
+
+ private byte[] calcHash(Digest hashAlg, byte[] input)
+ {
+ byte[] tmp = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.update(input, 0, input.length);
+ hashAlg.doFinal(tmp, 0);
+
+ return tmp;
+ }
+ /**
+ * Decrypts a message.
+ * See P1363.1 section 9.2.3.
+ *
+ * @param data The message to decrypt
+ * @param privKey the corresponding private key
+ * @return the decrypted message
+ * @throws InvalidCipherTextException if the encrypted data is invalid, or maxLenBytes
is greater than 255
+ */
+ private byte[] decrypt(byte[] data, NTRUEncryptionPrivateKeyParameters privKey)
+ throws InvalidCipherTextException
+ {
+ Polynomial priv_t = privKey.t;
+ IntegerPolynomial priv_fp = privKey.fp;
+ IntegerPolynomial pub = privKey.h;
+ int N = params.N;
+ int q = params.q;
+ int db = params.db;
+ int maxMsgLenBytes = params.maxMsgLenBytes;
+ int dm0 = params.dm0;
+ int pkLen = params.pkLen;
+ int minCallsMask = params.minCallsMask;
+ boolean hashSeed = params.hashSeed;
+ byte[] oid = params.oid;
+
+ if (maxMsgLenBytes > 255)
+ {
+ throw new DataLengthException("maxMsgLenBytes values bigger than 255 are not supported");
+ }
+
+ int bLen = db / 8;
+
+ IntegerPolynomial e = IntegerPolynomial.fromBinary(data, N, q);
+ IntegerPolynomial ci = decrypt(e, priv_t, priv_fp);
+
+ if (ci.count(-1) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal -1");
+ }
+ if (ci.count(0) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal 0");
+ }
+ if (ci.count(1) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal 1");
+ }
+
+ IntegerPolynomial cR = (IntegerPolynomial)e.clone();
+ cR.sub(ci);
+ cR.modPositive(q);
+ IntegerPolynomial cR4 = (IntegerPolynomial)cR.clone();
+ cR4.modPositive(4);
+ byte[] coR4 = cR4.toBinary(4);
+ IntegerPolynomial mask = MGF(coR4, N, minCallsMask, hashSeed);
+ IntegerPolynomial cMTrin = ci;
+ cMTrin.sub(mask);
+ cMTrin.mod3();
+ byte[] cM = cMTrin.toBinary3Sves();
+
+ byte[] cb = new byte[bLen];
+ System.arraycopy(cM, 0, cb, 0, bLen);
+ int cl = cM[bLen] & 0xFF; // llen=1, so read one byte
+ if (cl > maxMsgLenBytes)
+ {
+ throw new InvalidCipherTextException("Message too long: " + cl + ">" + maxMsgLenBytes);
+ }
+ byte[] cm = new byte[cl];
+ System.arraycopy(cM, bLen + 1, cm, 0, cl);
+ byte[] p0 = new byte[cM.length - (bLen + 1 + cl)];
+ System.arraycopy(cM, bLen + 1 + cl, p0, 0, p0.length);
+ if (!Arrays.areEqual(p0, new byte[p0.length]))
+ {
+ throw new InvalidCipherTextException("The message is not followed by zeroes");
+ }
+
+ // sData = OID|m|b|hTrunc
+ byte[] bh = pub.toBinary(q);
+ byte[] hTrunc = copyOf(bh, pkLen / 8);
+ byte[] sData = buildSData(oid, cm, cl, cb, hTrunc);
+
+ Polynomial cr = generateBlindingPoly(sData, cm);
+ IntegerPolynomial cRPrime = cr.mult(pub);
+ cRPrime.modPositive(q);
+ if (!cRPrime.equals(cR))
+ {
+ throw new InvalidCipherTextException("Invalid message encoding");
+ }
+
+ return cm;
+ }
+
+ /**
+ * @param e
+ * @param priv_t a polynomial such that if fastFp=true
, f=1+3*priv_t
; otherwise, f=priv_t
+ * @param priv_fp
+ * @return
+ */
+ protected IntegerPolynomial decrypt(IntegerPolynomial e, Polynomial priv_t, IntegerPolynomial priv_fp)
+ {
+ IntegerPolynomial a;
+ if (params.fastFp)
+ {
+ a = priv_t.mult(e, params.q);
+ a.mult(3);
+ a.add(e);
+ }
+ else
+ {
+ a = priv_t.mult(e, params.q);
+ }
+ a.center0(params.q);
+ a.mod3();
+
+ IntegerPolynomial c = params.fastFp ? a : new DenseTernaryPolynomial(a).mult(priv_fp, 3);
+ c.center0(3);
+ return c;
+ }
+
+ private byte[] copyOf(byte[] src, int len)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(src, 0, tmp, 0, len < src.length ? len : src.length);
+
+ return tmp;
+ }
+
+ private int log2(int value)
+ {
+ if (value == 2048)
+ {
+ return 11;
+ }
+
+ throw new IllegalStateException("log2 not fully implemented");
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java
new file mode 100644
index 000000000..3ce2154d1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java
@@ -0,0 +1,7 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+public class NTRUParameters
+{
+ public static final int TERNARY_POLYNOMIAL_TYPE_SIMPLE = 0;
+ public static final int TERNARY_POLYNOMIAL_TYPE_PRODUCT = 1;
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java
new file mode 100644
index 000000000..2dc19773a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java
@@ -0,0 +1,259 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.nio.ByteBuffer;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+
+/**
+ * Signs, verifies data and generates key pairs.
+ */
+public class NTRUSigner
+{
+ private NTRUSigningParameters params;
+ private Digest hashAlg;
+ private NTRUSigningPrivateKeyParameters signingKeyPair;
+ private NTRUSigningPublicKeyParameters verificationKey;
+
+ /**
+ * Constructs a new instance with a set of signature parameters.
+ *
+ * @param params signature parameters
+ */
+ public NTRUSigner(NTRUSigningParameters params)
+ {
+ this.params = params;
+ }
+
+ /**
+ * Resets the engine for signing a message.
+ *
+ * @param forSigning
+ * @param params
+ */
+ public void init(boolean forSigning, CipherParameters params)
+ {
+ if (forSigning)
+ {
+ this.signingKeyPair = (NTRUSigningPrivateKeyParameters)params;
+ }
+ else
+ {
+ this.verificationKey = (NTRUSigningPublicKeyParameters)params;
+ }
+ hashAlg = this.params.hashAlg;
+ hashAlg.reset();
+ }
+
+ /**
+ * Adds data to sign or verify.
+ *
+ * @param b data
+ */
+ public void update(byte b)
+ {
+ if (hashAlg == null)
+ {
+ throw new IllegalStateException("Call initSign or initVerify first!");
+ }
+
+ hashAlg.update(b);
+ }
+
+ /**
+ * Adds data to sign or verify.
+ *
+ * @param m data
+ * @param off offset
+ * @param length number of bytes
+ */
+ public void update(byte[] m, int off, int length)
+ {
+ if (hashAlg == null)
+ {
+ throw new IllegalStateException("Call initSign or initVerify first!");
+ }
+
+ hashAlg.update(m, off, length);
+ }
+
+ /**
+ * Adds data to sign and computes a signature over this data and any data previously added via {@link #update(byte[], int, int)}.
+ *
+ * @return a signature
+ * @throws IllegalStateException if initSign
was not called
+ */
+ public byte[] generateSignature()
+ {
+ if (hashAlg == null || signingKeyPair == null)
+ {
+ throw new IllegalStateException("Call initSign first!");
+ }
+
+ byte[] msgHash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(msgHash, 0);
+ return signHash(msgHash, signingKeyPair);
+ }
+
+ private byte[] signHash(byte[] msgHash, NTRUSigningPrivateKeyParameters kp)
+ {
+ int r = 0;
+ IntegerPolynomial s;
+ IntegerPolynomial i;
+
+ NTRUSigningPublicKeyParameters kPub = kp.getPublicKey();
+ do
+ {
+ r++;
+ if (r > params.signFailTolerance)
+ {
+ throw new IllegalStateException("Signing failed: too many retries (max=" + params.signFailTolerance + ")");
+ }
+ i = createMsgRep(msgHash, r);
+ s = sign(i, kp);
+ }
+ while (!verify(i, s, kPub.h));
+
+ byte[] rawSig = s.toBinary(params.q);
+ ByteBuffer sbuf = ByteBuffer.allocate(rawSig.length + 4);
+ sbuf.put(rawSig);
+ sbuf.putInt(r);
+ return sbuf.array();
+ }
+
+ private IntegerPolynomial sign(IntegerPolynomial i, NTRUSigningPrivateKeyParameters kp)
+ {
+ int N = params.N;
+ int q = params.q;
+ int perturbationBases = params.B;
+
+ NTRUSigningPrivateKeyParameters kPriv = kp;
+ NTRUSigningPublicKeyParameters kPub = kp.getPublicKey();
+
+ IntegerPolynomial s = new IntegerPolynomial(N);
+ int iLoop = perturbationBases;
+ while (iLoop >= 1)
+ {
+ Polynomial f = kPriv.getBasis(iLoop).f;
+ Polynomial fPrime = kPriv.getBasis(iLoop).fPrime;
+
+ IntegerPolynomial y = f.mult(i);
+ y.div(q);
+ y = fPrime.mult(y);
+
+ IntegerPolynomial x = fPrime.mult(i);
+ x.div(q);
+ x = f.mult(x);
+
+ IntegerPolynomial si = y;
+ si.sub(x);
+ s.add(si);
+
+ IntegerPolynomial hi = (IntegerPolynomial)kPriv.getBasis(iLoop).h.clone();
+ if (iLoop > 1)
+ {
+ hi.sub(kPriv.getBasis(iLoop - 1).h);
+ }
+ else
+ {
+ hi.sub(kPub.h);
+ }
+ i = si.mult(hi, q);
+
+ iLoop--;
+ }
+
+ Polynomial f = kPriv.getBasis(0).f;
+ Polynomial fPrime = kPriv.getBasis(0).fPrime;
+
+ IntegerPolynomial y = f.mult(i);
+ y.div(q);
+ y = fPrime.mult(y);
+
+ IntegerPolynomial x = fPrime.mult(i);
+ x.div(q);
+ x = f.mult(x);
+
+ y.sub(x);
+ s.add(y);
+ s.modPositive(q);
+ return s;
+ }
+
+ /**
+ * Verifies a signature for any data previously added via {@link #update(byte[], int, int)}.
+ *
+ * @param sig a signature
+ * @return whether the signature is valid
+ * @throws IllegalStateException if initVerify
was not called
+ */
+ public boolean verifySignature(byte[] sig)
+ {
+ if (hashAlg == null || verificationKey == null)
+ {
+ throw new IllegalStateException("Call initVerify first!");
+ }
+
+ byte[] msgHash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(msgHash, 0);
+
+ return verifyHash(msgHash, sig, verificationKey);
+ }
+
+ private boolean verifyHash(byte[] msgHash, byte[] sig, NTRUSigningPublicKeyParameters pub)
+ {
+ ByteBuffer sbuf = ByteBuffer.wrap(sig);
+ byte[] rawSig = new byte[sig.length - 4];
+ sbuf.get(rawSig);
+ IntegerPolynomial s = IntegerPolynomial.fromBinary(rawSig, params.N, params.q);
+ int r = sbuf.getInt();
+ return verify(createMsgRep(msgHash, r), s, pub.h);
+ }
+
+ private boolean verify(IntegerPolynomial i, IntegerPolynomial s, IntegerPolynomial h)
+ {
+ int q = params.q;
+ double normBoundSq = params.normBoundSq;
+ double betaSq = params.betaSq;
+
+ IntegerPolynomial t = h.mult(s, q);
+ t.sub(i);
+ long centeredNormSq = (long)(s.centeredNormSq(q) + betaSq * t.centeredNormSq(q));
+ return centeredNormSq <= normBoundSq;
+ }
+
+ protected IntegerPolynomial createMsgRep(byte[] msgHash, int r)
+ {
+ int N = params.N;
+ int q = params.q;
+
+ int c = 31 - Integer.numberOfLeadingZeros(q);
+ int B = (c + 7) / 8;
+ IntegerPolynomial i = new IntegerPolynomial(N);
+
+ ByteBuffer cbuf = ByteBuffer.allocate(msgHash.length + 4);
+ cbuf.put(msgHash);
+ cbuf.putInt(r);
+ NTRUSignerPrng prng = new NTRUSignerPrng(cbuf.array(), params.hashAlg);
+
+ for (int t = 0; t < N; t++)
+ {
+ byte[] o = prng.nextBytes(B);
+ int hi = o[o.length - 1];
+ hi >>= 8 * B - c;
+ hi <<= 8 * B - c;
+ o[o.length - 1] = (byte)hi;
+
+ ByteBuffer obuf = ByteBuffer.allocate(4);
+ obuf.put(o);
+ obuf.rewind();
+ // reverse byte order so it matches the endianness of java ints
+ i.coeffs[t] = Integer.reverseBytes(obuf.getInt());
+ }
+ return i;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java
new file mode 100644
index 000000000..c9278dd57
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java
@@ -0,0 +1,64 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.nio.ByteBuffer;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * An implementation of the deterministic pseudo-random generator in EESS section 3.7.3.1
+ */
+public class NTRUSignerPrng
+{
+ private int counter;
+ private byte[] seed;
+ private Digest hashAlg;
+
+ /**
+ * Constructs a new PRNG and seeds it with a byte array.
+ *
+ * @param seed a seed
+ * @param hashAlg the hash algorithm to use
+ */
+ NTRUSignerPrng(byte[] seed, Digest hashAlg)
+ {
+ counter = 0;
+ this.seed = seed;
+ this.hashAlg = hashAlg;
+ }
+
+ /**
+ * Returns n
random bytes
+ *
+ * @param n number of bytes to return
+ * @return the next n
random bytes
+ */
+ byte[] nextBytes(int n)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(n);
+
+ while (buf.hasRemaining())
+ {
+ ByteBuffer cbuf = ByteBuffer.allocate(seed.length + 4);
+ cbuf.put(seed);
+ cbuf.putInt(counter);
+ byte[] array = cbuf.array();
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.update(array, 0, array.length);
+
+ hashAlg.doFinal(hash, 0);
+
+ if (buf.remaining() < hash.length)
+ {
+ buf.put(hash, 0, buf.remaining());
+ }
+ else
+ {
+ buf.put(hash);
+ }
+ counter++;
+ }
+
+ return buf.array();
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
new file mode 100644
index 000000000..2f6eaab55
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
@@ -0,0 +1,407 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.text.DecimalFormat;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruSign. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUSigningKeyGenerationParameters
+ extends KeyGenerationParameters
+ implements Cloneable
+{
+ public static final int BASIS_TYPE_STANDARD = 0;
+ public static final int BASIS_TYPE_TRANSPOSE = 1;
+
+ public static final int KEY_GEN_ALG_RESULTANT = 0;
+ public static final int KEY_GEN_ALG_FLOAT = 1;
+
+ /**
+ * Gives 128 bits of security
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_439 = new NTRUSigningKeyGenerationParameters(439, 2048, 146, 1, BASIS_TYPE_TRANSPOSE, 0.165, 400, 280, false, true, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+ /**
+ * Like APR2011_439
, this parameter set gives 128 bits of security but uses product-form polynomials
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_439_PROD = new NTRUSigningKeyGenerationParameters(439, 2048, 9, 8, 5, 1, BASIS_TYPE_TRANSPOSE, 0.165, 400, 280, false, true, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+ /**
+ * Gives 256 bits of security
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_743 = new NTRUSigningKeyGenerationParameters(743, 2048, 248, 1, BASIS_TYPE_TRANSPOSE, 0.127, 405, 360, true, false, KEY_GEN_ALG_RESULTANT, new SHA512Digest());
+
+ /**
+ * Like APR2011_439
, this parameter set gives 256 bits of security but uses product-form polynomials
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_743_PROD = new NTRUSigningKeyGenerationParameters(743, 2048, 11, 11, 15, 1, BASIS_TYPE_TRANSPOSE, 0.127, 405, 360, true, false, KEY_GEN_ALG_RESULTANT, new SHA512Digest());
+
+ /**
+ * Generates key pairs quickly. Use for testing only.
+ */
+ public static final NTRUSigningKeyGenerationParameters TEST157 = new NTRUSigningKeyGenerationParameters(157, 256, 29, 1, BASIS_TYPE_TRANSPOSE, 0.38, 200, 80, false, false, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+ /**
+ * Generates key pairs quickly. Use for testing only.
+ */
+ public static final NTRUSigningKeyGenerationParameters TEST157_PROD = new NTRUSigningKeyGenerationParameters(157, 256, 5, 5, 8, 1, BASIS_TYPE_TRANSPOSE, 0.38, 200, 80, false, false, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+
+ public int N;
+ public int q;
+ public int d, d1, d2, d3, B;
+ double beta;
+ public double betaSq;
+ double normBound;
+ public double normBoundSq;
+ public int signFailTolerance = 100;
+ double keyNormBound;
+ public double keyNormBoundSq;
+ public boolean primeCheck; // true if N and 2N+1 are prime
+ public int basisType;
+ int bitsF = 6; // max #bits needed to encode one coefficient of the polynomial F
+ public boolean sparse; // whether to treat ternary polynomials as sparsely populated
+ public int keyGenAlg;
+ public Digest hashAlg;
+ public int polyType;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. polyType=SIMPLE).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d number of -1's in the private polynomials f
and g
+ * @param B number of perturbations
+ * @param basisType whether to use the standard or transpose lattice
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials F
and G
+ * @param primeCheck whether 2N+1
is prime
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param keyGenAlg RESULTANT
produces better bases, FLOAT
is slightly faster. RESULTANT
follows the EESS standard while FLOAT
is described in Hoffstein et al: An Introduction to Mathematical Cryptography.
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
. The MessageDigest
must support the getDigestLength()
method.
+ */
+ public NTRUSigningKeyGenerationParameters(int N, int q, int d, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
+ {
+ super(new SecureRandom(), N);
+ this.N = N;
+ this.q = q;
+ this.d = d;
+ this.B = B;
+ this.basisType = basisType;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.keyNormBound = keyNormBound;
+ this.primeCheck = primeCheck;
+ this.sparse = sparse;
+ this.keyGenAlg = keyGenAlg;
+ this.hashAlg = hashAlg;
+ polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. polyType=PRODUCT).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d1 number of -1's in the private polynomials f
and g
+ * @param d2 number of -1's in the private polynomials f
and g
+ * @param d3 number of -1's in the private polynomials f
and g
+ * @param B number of perturbations
+ * @param basisType whether to use the standard or transpose lattice
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials F
and G
+ * @param primeCheck whether 2N+1
is prime
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param keyGenAlg RESULTANT
produces better bases, FLOAT
is slightly faster. RESULTANT
follows the EESS standard while FLOAT
is described in Hoffstein et al: An Introduction to Mathematical Cryptography.
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
. The MessageDigest
must support the getDigestLength()
method.
+ */
+ public NTRUSigningKeyGenerationParameters(int N, int q, int d1, int d2, int d3, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
+ {
+ super(new SecureRandom(), N);
+ this.N = N;
+ this.q = q;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.B = B;
+ this.basisType = basisType;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.keyNormBound = keyNormBound;
+ this.primeCheck = primeCheck;
+ this.sparse = sparse;
+ this.keyGenAlg = keyGenAlg;
+ this.hashAlg = hashAlg;
+ polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ init();
+ }
+
+ private void init()
+ {
+ betaSq = beta * beta;
+ normBoundSq = normBound * normBound;
+ keyNormBoundSq = keyNormBound * keyNormBound;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws java.io.IOException
+ */
+ public NTRUSigningKeyGenerationParameters(InputStream is)
+ throws IOException
+ {
+ super(new SecureRandom(), 0); // TODO:
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ d = dis.readInt();
+ d1 = dis.readInt();
+ d2 = dis.readInt();
+ d3 = dis.readInt();
+ B = dis.readInt();
+ basisType = dis.readInt();
+ beta = dis.readDouble();
+ normBound = dis.readDouble();
+ keyNormBound = dis.readDouble();
+ signFailTolerance = dis.readInt();
+ primeCheck = dis.readBoolean();
+ sparse = dis.readBoolean();
+ bitsF = dis.readInt();
+ keyGenAlg = dis.read();
+ String alg = dis.readUTF();
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+ polyType = dis.read();
+ init();
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws java.io.IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(d);
+ dos.writeInt(d1);
+ dos.writeInt(d2);
+ dos.writeInt(d3);
+ dos.writeInt(B);
+ dos.writeInt(basisType);
+ dos.writeDouble(beta);
+ dos.writeDouble(normBound);
+ dos.writeDouble(keyNormBound);
+ dos.writeInt(signFailTolerance);
+ dos.writeBoolean(primeCheck);
+ dos.writeBoolean(sparse);
+ dos.writeInt(bitsF);
+ dos.write(keyGenAlg);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ dos.write(polyType);
+ }
+
+ public NTRUSigningParameters getSigningParameters()
+ {
+ return new NTRUSigningParameters(N, q, d, B, beta, normBound, hashAlg);
+ }
+
+ public NTRUSigningKeyGenerationParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUSigningKeyGenerationParameters(N, q, d, B, basisType, beta, normBound, keyNormBound, primeCheck, sparse, keyGenAlg, hashAlg);
+ }
+ else
+ {
+ return new NTRUSigningKeyGenerationParameters(N, q, d1, d2, d3, B, basisType, beta, normBound, keyNormBound, primeCheck, sparse, keyGenAlg, hashAlg);
+ }
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + B;
+ result = prime * result + N;
+ result = prime * result + basisType;
+ long temp;
+ temp = Double.doubleToLongBits(beta);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(betaSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + bitsF;
+ result = prime * result + d;
+ result = prime * result + d1;
+ result = prime * result + d2;
+ result = prime * result + d3;
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + keyGenAlg;
+ temp = Double.doubleToLongBits(keyNormBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(keyNormBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + polyType;
+ result = prime * result + (primeCheck ? 1231 : 1237);
+ result = prime * result + q;
+ result = prime * result + signFailTolerance;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUSigningKeyGenerationParameters))
+ {
+ return false;
+ }
+ NTRUSigningKeyGenerationParameters other = (NTRUSigningKeyGenerationParameters)obj;
+ if (B != other.B)
+ {
+ return false;
+ }
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (basisType != other.basisType)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(beta) != Double.doubleToLongBits(other.beta))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(betaSq) != Double.doubleToLongBits(other.betaSq))
+ {
+ return false;
+ }
+ if (bitsF != other.bitsF)
+ {
+ return false;
+ }
+ if (d != other.d)
+ {
+ return false;
+ }
+ if (d1 != other.d1)
+ {
+ return false;
+ }
+ if (d2 != other.d2)
+ {
+ return false;
+ }
+ if (d3 != other.d3)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (keyGenAlg != other.keyGenAlg)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(keyNormBound) != Double.doubleToLongBits(other.keyNormBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(keyNormBoundSq) != Double.doubleToLongBits(other.keyNormBoundSq))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBound) != Double.doubleToLongBits(other.normBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBoundSq) != Double.doubleToLongBits(other.normBoundSq))
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (primeCheck != other.primeCheck)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (signFailTolerance != other.signFailTolerance)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ DecimalFormat format = new DecimalFormat("0.00");
+
+ StringBuilder output = new StringBuilder("SignatureParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE d=" + d);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT d1=" + d1 + " d2=" + d2 + " d3=" + d3);
+ }
+ output.append(" B=" + B + " basisType=" + basisType + " beta=" + format.format(beta) +
+ " normBound=" + format.format(normBound) + " keyNormBound=" + format.format(keyNormBound) +
+ " prime=" + primeCheck + " sparse=" + sparse + " keyGenAlg=" + keyGenAlg + " hashAlg=" + hashAlg + ")");
+ return output.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
new file mode 100644
index 000000000..eac283aab
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
@@ -0,0 +1,357 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+import org.spongycastle.pqc.math.ntru.polynomial.BigDecimalPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.BigIntPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Resultant;
+
+import static java.math.BigInteger.ONE;
+import static java.math.BigInteger.ZERO;
+
+public class NTRUSigningKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private NTRUSigningKeyGenerationParameters params;
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.params = (NTRUSigningKeyGenerationParameters)param;
+ }
+
+ /**
+ * Generates a new signature key pair. Starts B+1
threads.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ NTRUSigningPublicKeyParameters pub = null;
+ ExecutorService executor = Executors.newCachedThreadPool();
+ List
+ * This method changes F
and g
but leaves f
and
+ * g
unchanged.
+ *
+ * @param f
+ * @param g
+ * @param F
+ * @param G
+ * @param N
+ */
+ private void minimizeFG(IntegerPolynomial f, IntegerPolynomial g, IntegerPolynomial F, IntegerPolynomial G, int N)
+ {
+ int E = 0;
+ for (int j = 0; j < N; j++)
+ {
+ E += 2 * N * (f.coeffs[j] * f.coeffs[j] + g.coeffs[j] * g.coeffs[j]);
+ }
+
+ // [f(1)+g(1)]^2 = 4
+ E -= 4;
+
+ IntegerPolynomial u = (IntegerPolynomial)f.clone();
+ IntegerPolynomial v = (IntegerPolynomial)g.clone();
+ int j = 0;
+ int k = 0;
+ int maxAdjustment = N;
+ while (k < maxAdjustment && j < N)
+ {
+ int D = 0;
+ int i = 0;
+ while (i < N)
+ {
+ int D1 = F.coeffs[i] * f.coeffs[i];
+ int D2 = G.coeffs[i] * g.coeffs[i];
+ int D3 = 4 * N * (D1 + D2);
+ D += D3;
+ i++;
+ }
+ // f(1)+g(1) = 2
+ int D1 = 4 * (F.sumCoeffs() + G.sumCoeffs());
+ D -= D1;
+
+ if (D > E)
+ {
+ F.sub(u);
+ G.sub(v);
+ k++;
+ j = 0;
+ }
+ else if (D < -E)
+ {
+ F.add(u);
+ G.add(v);
+ k++;
+ j = 0;
+ }
+ j++;
+ u.rotate1();
+ v.rotate1();
+ }
+ }
+
+ /**
+ * Creates a NTRUSigner basis consisting of polynomials f, g, F, G, h
.
+ * If KeyGenAlg=FLOAT
, the basis may not be valid and this method must be rerun if that is the case.
+ *
+ * @see #generateBoundedBasis()
+ */
+ private FGBasis generateBasis()
+ {
+ int N = params.N;
+ int q = params.q;
+ int d = params.d;
+ int d1 = params.d1;
+ int d2 = params.d2;
+ int d3 = params.d3;
+ int basisType = params.basisType;
+
+ Polynomial f;
+ IntegerPolynomial fInt;
+ Polynomial g;
+ IntegerPolynomial gInt;
+ IntegerPolynomial fq;
+ Resultant rf;
+ Resultant rg;
+ BigIntEuclidean r;
+
+ int _2n1 = 2 * N + 1;
+ boolean primeCheck = params.primeCheck;
+
+ do
+ {
+ do
+ {
+ f = params.polyType== NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+ fInt = f.toIntegerPolynomial();
+ }
+ while (primeCheck && fInt.resultant(_2n1).res.equals(ZERO));
+ fq = fInt.invertFq(q);
+ }
+ while (fq == null);
+ rf = fInt.resultant();
+
+ do
+ {
+ do
+ {
+ do
+ {
+ g = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+ gInt = g.toIntegerPolynomial();
+ }
+ while (primeCheck && gInt.resultant(_2n1).res.equals(ZERO));
+ }
+ while (gInt.invertFq(q) == null);
+ rg = gInt.resultant();
+ r = BigIntEuclidean.calculate(rf.res, rg.res);
+ }
+ while (!r.gcd.equals(ONE));
+
+ BigIntPolynomial A = (BigIntPolynomial)rf.rho.clone();
+ A.mult(r.x.multiply(BigInteger.valueOf(q)));
+ BigIntPolynomial B = (BigIntPolynomial)rg.rho.clone();
+ B.mult(r.y.multiply(BigInteger.valueOf(-q)));
+
+ BigIntPolynomial C;
+ if (params.keyGenAlg == NTRUSigningKeyGenerationParameters.KEY_GEN_ALG_RESULTANT)
+ {
+ int[] fRevCoeffs = new int[N];
+ int[] gRevCoeffs = new int[N];
+ fRevCoeffs[0] = fInt.coeffs[0];
+ gRevCoeffs[0] = gInt.coeffs[0];
+ for (int i = 1; i < N; i++)
+ {
+ fRevCoeffs[i] = fInt.coeffs[N - i];
+ gRevCoeffs[i] = gInt.coeffs[N - i];
+ }
+ IntegerPolynomial fRev = new IntegerPolynomial(fRevCoeffs);
+ IntegerPolynomial gRev = new IntegerPolynomial(gRevCoeffs);
+
+ IntegerPolynomial t = f.mult(fRev);
+ t.add(g.mult(gRev));
+ Resultant rt = t.resultant();
+ C = fRev.mult(B); // fRev.mult(B) is actually faster than new SparseTernaryPolynomial(fRev).mult(B), possibly due to cache locality?
+ C.add(gRev.mult(A));
+ C = C.mult(rt.rho);
+ C.div(rt.res);
+ }
+ else
+ { // KeyGenAlg.FLOAT
+ // calculate ceil(log10(N))
+ int log10N = 0;
+ for (int i = 1; i < N; i *= 10)
+ {
+ log10N++;
+ }
+
+ // * Cdec needs to be accurate to 1 decimal place so it can be correctly rounded;
+ // * fInv loses up to (#digits of longest coeff of B) places in fInv.mult(B);
+ // * multiplying fInv by B also multiplies the rounding error by a factor of N;
+ // so make #decimal places of fInv the sum of the above.
+ BigDecimalPolynomial fInv = rf.rho.div(new BigDecimal(rf.res), B.getMaxCoeffLength() + 1 + log10N);
+ BigDecimalPolynomial gInv = rg.rho.div(new BigDecimal(rg.res), A.getMaxCoeffLength() + 1 + log10N);
+
+ BigDecimalPolynomial Cdec = fInv.mult(B);
+ Cdec.add(gInv.mult(A));
+ Cdec.halve();
+ C = Cdec.round();
+ }
+
+ BigIntPolynomial F = (BigIntPolynomial)B.clone();
+ F.sub(f.mult(C));
+ BigIntPolynomial G = (BigIntPolynomial)A.clone();
+ G.sub(g.mult(C));
+
+ IntegerPolynomial FInt = new IntegerPolynomial(F);
+ IntegerPolynomial GInt = new IntegerPolynomial(G);
+ minimizeFG(fInt, gInt, FInt, GInt, N);
+
+ Polynomial fPrime;
+ IntegerPolynomial h;
+ if (basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ fPrime = FInt;
+ h = g.mult(fq, q);
+ }
+ else
+ {
+ fPrime = g;
+ h = FInt.mult(fq, q);
+ }
+ h.modPositive(q);
+
+ return new FGBasis(f, fPrime, h, FInt, GInt, params);
+ }
+
+ /**
+ * Creates a basis such that |F| < keyNormBound
and |G| < keyNormBound
+ *
+ * @return a NTRUSigner basis
+ */
+ public NTRUSigningPrivateKeyParameters.Basis generateBoundedBasis()
+ {
+ while (true)
+ {
+ FGBasis basis = generateBasis();
+ if (basis.isNormOk())
+ {
+ return basis;
+ }
+ }
+ }
+
+ private class BasisGenerationTask
+ implements CallableF
and G
.
+ */
+ public class FGBasis
+ extends NTRUSigningPrivateKeyParameters.Basis
+ {
+ public IntegerPolynomial F;
+ public IntegerPolynomial G;
+
+ FGBasis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, IntegerPolynomial F, IntegerPolynomial G, NTRUSigningKeyGenerationParameters params)
+ {
+ super(f, fPrime, h, params);
+ this.F = F;
+ this.G = G;
+ }
+
+ /**
+ * Returns true
if the norms of the polynomials F
and G
+ * are within {@link NTRUSigningKeyGenerationParameters#keyNormBound}.
+ *
+ * @return
+ */
+ boolean isNormOk()
+ {
+ double keyNormBoundSq = params.keyNormBoundSq;
+ int q = params.q;
+ return (F.centeredNormSq(q) < keyNormBoundSq && G.centeredNormSq(q) < keyNormBoundSq);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java
new file mode 100644
index 000000000..44c5a0a1e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java
@@ -0,0 +1,269 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DecimalFormat;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruSign. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUSigningParameters
+ implements Cloneable
+{
+ public int N;
+ public int q;
+ public int d, d1, d2, d3, B;
+ double beta;
+ public double betaSq;
+ double normBound;
+ public double normBoundSq;
+ public int signFailTolerance = 100;
+ int bitsF = 6; // max #bits needed to encode one coefficient of the polynomial F
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. polyType=SIMPLE).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d number of -1's in the private polynomials f
and g
+ * @param B number of perturbations
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
. The MessageDigest
must support the getDigestLength()
method.
+ */
+ public NTRUSigningParameters(int N, int q, int d, int B, double beta, double normBound, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.d = d;
+ this.B = B;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. polyType=PRODUCT).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d1 number of -1's in the private polynomials f
and g
+ * @param d2 number of -1's in the private polynomials f
and g
+ * @param d3 number of -1's in the private polynomials f
and g
+ * @param B number of perturbations
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials F
and G
+ * @param hashAlg a valid identifier for a java.security.MessageDigest
instance such as SHA-256
. The MessageDigest
must support the getDigestLength()
method.
+ */
+ public NTRUSigningParameters(int N, int q, int d1, int d2, int d3, int B, double beta, double normBound, double keyNormBound, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.B = B;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ betaSq = beta * beta;
+ normBoundSq = normBound * normBound;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws IOException
+ */
+ public NTRUSigningParameters(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ d = dis.readInt();
+ d1 = dis.readInt();
+ d2 = dis.readInt();
+ d3 = dis.readInt();
+ B = dis.readInt();
+ beta = dis.readDouble();
+ normBound = dis.readDouble();
+ signFailTolerance = dis.readInt();
+ bitsF = dis.readInt();
+ String alg = dis.readUTF();
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+ init();
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(d);
+ dos.writeInt(d1);
+ dos.writeInt(d2);
+ dos.writeInt(d3);
+ dos.writeInt(B);
+ dos.writeDouble(beta);
+ dos.writeDouble(normBound);
+ dos.writeInt(signFailTolerance);
+ dos.writeInt(bitsF);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+ public NTRUSigningParameters clone()
+ {
+ return new NTRUSigningParameters(N, q, d, B, beta, normBound, hashAlg);
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + B;
+ result = prime * result + N;
+ long temp;
+ temp = Double.doubleToLongBits(beta);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(betaSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + bitsF;
+ result = prime * result + d;
+ result = prime * result + d1;
+ result = prime * result + d2;
+ result = prime * result + d3;
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ temp = Double.doubleToLongBits(normBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + q;
+ result = prime * result + signFailTolerance;
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUSigningParameters))
+ {
+ return false;
+ }
+ NTRUSigningParameters other = (NTRUSigningParameters)obj;
+ if (B != other.B)
+ {
+ return false;
+ }
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(beta) != Double.doubleToLongBits(other.beta))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(betaSq) != Double.doubleToLongBits(other.betaSq))
+ {
+ return false;
+ }
+ if (bitsF != other.bitsF)
+ {
+ return false;
+ }
+ if (d != other.d)
+ {
+ return false;
+ }
+ if (d1 != other.d1)
+ {
+ return false;
+ }
+ if (d2 != other.d2)
+ {
+ return false;
+ }
+ if (d3 != other.d3)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBound) != Double.doubleToLongBits(other.normBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBoundSq) != Double.doubleToLongBits(other.normBoundSq))
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (signFailTolerance != other.signFailTolerance)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public String toString()
+ {
+ DecimalFormat format = new DecimalFormat("0.00");
+
+ StringBuilder output = new StringBuilder("SignatureParameters(N=" + N + " q=" + q);
+
+ output.append(" B=" + B + " beta=" + format.format(beta) +
+ " normBound=" + format.format(normBound) +
+ " hashAlg=" + hashAlg + ")");
+ return output.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java
new file mode 100644
index 000000000..bbbaaeef9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java
@@ -0,0 +1,385 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+/**
+ * A NtruSign private key comprises one or more {@link NTRUSigningPrivateKeyParameters.Basis} of three polynomials each,
+ * except the zeroth basis for which h
is undefined.
+ */
+public class NTRUSigningPrivateKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private Listi
-th basis
+ *
+ * @param i the index
+ * @return the basis at index i
+ */
+ public Basis getBasis(int i)
+ {
+ return bases.get(i);
+ }
+
+ public NTRUSigningPublicKeyParameters getPublicKey()
+ {
+ return publicKey;
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for (int i = 0; i < bases.size(); i++)
+ {
+ // all bases except for the first one contain a public key
+ bases.get(i).encode(os, i != 0);
+ }
+
+ os.write(publicKey.getEncoded());
+
+ return os.toByteArray();
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((bases == null) ? 0 : bases.hashCode());
+ for (Basis basis : bases)
+ {
+ result += basis.hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUSigningPrivateKeyParameters other = (NTRUSigningPrivateKeyParameters)obj;
+ if (bases == null)
+ {
+ if (other.bases != null)
+ {
+ return false;
+ }
+ }
+ if (bases.size() != other.bases.size())
+ {
+ return false;
+ }
+ for (int i = 0; i < bases.size(); i++)
+ {
+ Basis basis1 = bases.get(i);
+ Basis basis2 = other.bases.get(i);
+ if (!basis1.f.equals(basis2.f))
+ {
+ return false;
+ }
+ if (!basis1.fPrime.equals(basis2.fPrime))
+ {
+ return false;
+ }
+ if (i != 0 && !basis1.h.equals(basis2.h)) // don't compare h for the 0th basis
+ {
+ return false;
+ }
+ if (!basis1.params.equals(basis2.params))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A NtruSign basis. Contains three polynomials f, f', h
.
+ */
+ public static class Basis
+ {
+ public Polynomial f;
+ public Polynomial fPrime;
+ public IntegerPolynomial h;
+ NTRUSigningKeyGenerationParameters params;
+
+ /**
+ * Constructs a new basis from polynomials f, f', h
.
+ *
+ * @param f
+ * @param fPrime
+ * @param h
+ * @param params NtruSign parameters
+ */
+ protected Basis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, NTRUSigningKeyGenerationParameters params)
+ {
+ this.f = f;
+ this.fPrime = fPrime;
+ this.h = h;
+ this.params = params;
+ }
+
+ /**
+ * Reads a basis from an input stream and constructs a new basis.
+ *
+ * @param is an input stream
+ * @param params NtruSign parameters
+ * @param include_h whether to read the polynomial h
(true
) or only f
and f'
(false
)
+ */
+ Basis(InputStream is, NTRUSigningKeyGenerationParameters params, boolean include_h)
+ throws IOException
+ {
+ int N = params.N;
+ int q = params.q;
+ int d1 = params.d1;
+ int d2 = params.d2;
+ int d3 = params.d3;
+ boolean sparse = params.sparse;
+ this.params = params;
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ f = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
+ }
+ else
+ {
+ IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, N);
+ f = sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
+ }
+
+ if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ IntegerPolynomial fPrimeInt = IntegerPolynomial.fromBinary(is, N, q);
+ for (int i = 0; i < fPrimeInt.coeffs.length; i++)
+ {
+ fPrimeInt.coeffs[i] -= q / 2;
+ }
+ fPrime = fPrimeInt;
+ }
+ else if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ fPrime = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
+ }
+ else
+ {
+ fPrime = IntegerPolynomial.fromBinary3Tight(is, N);
+ }
+
+ if (include_h)
+ {
+ h = IntegerPolynomial.fromBinary(is, N, q);
+ }
+ }
+
+ /**
+ * Writes the basis to an output stream
+ *
+ * @param os an output stream
+ * @param include_h whether to write the polynomial h
(true
) or only f
and f'
(false
)
+ * @throws IOException
+ */
+ void encode(OutputStream os, boolean include_h)
+ throws IOException
+ {
+ int q = params.q;
+
+ os.write(getEncoded(f));
+ if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ IntegerPolynomial fPrimeInt = fPrime.toIntegerPolynomial();
+ for (int i = 0; i < fPrimeInt.coeffs.length; i++)
+ {
+ fPrimeInt.coeffs[i] += q / 2;
+ }
+ os.write(fPrimeInt.toBinary(q));
+ }
+ else
+ {
+ os.write(getEncoded(fPrime));
+ }
+ if (include_h)
+ {
+ os.write(h.toBinary(q));
+ }
+ }
+
+ private byte[] getEncoded(Polynomial p)
+ {
+ if (p instanceof ProductFormPolynomial)
+ {
+ return ((ProductFormPolynomial)p).toBinary();
+ }
+ else
+ {
+ return p.toIntegerPolynomial().toBinary3Tight();
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f == null) ? 0 : f.hashCode());
+ result = prime * result + ((fPrime == null) ? 0 : fPrime.hashCode());
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof Basis))
+ {
+ return false;
+ }
+ Basis other = (Basis)obj;
+ if (f == null)
+ {
+ if (other.f != null)
+ {
+ return false;
+ }
+ }
+ else if (!f.equals(other.f))
+ {
+ return false;
+ }
+ if (fPrime == null)
+ {
+ if (other.fPrime != null)
+ {
+ return false;
+ }
+ }
+ else if (!fPrime.equals(other.fPrime))
+ {
+ return false;
+ }
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java
new file mode 100644
index 000000000..d7431b8bf
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java
@@ -0,0 +1,132 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+
+/**
+ * A NtruSign public key is essentially a polynomial named h
.
+ */
+public class NTRUSigningPublicKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private NTRUSigningParameters params;
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new public key from a polynomial
+ *
+ * @param h the polynomial h
which determines the key
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(IntegerPolynomial h, NTRUSigningParameters params)
+ {
+ super(false);
+ this.h = h;
+ this.params = params;
+ }
+
+ /**
+ * Converts a byte array to a polynomial h
and constructs a new public key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(byte[] b, NTRUSigningParameters params)
+ {
+ super(false);
+ h = IntegerPolynomial.fromBinary(b, params.N, params.q);
+ this.params = params;
+ }
+
+ /**
+ * Reads a polynomial h
from an input stream and constructs a new public key
+ *
+ * @param is an input stream
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(InputStream is, NTRUSigningParameters params)
+ throws IOException
+ {
+ super(false);
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ this.params = params;
+ }
+
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ */
+ public byte[] getEncoded()
+ {
+ return h.toBinary(params.q);
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUSigningPublicKeyParameters other = (NTRUSigningPublicKeyParameters)obj;
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java
new file mode 100644
index 000000000..73eaaf118
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java
@@ -0,0 +1,322 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.pqc.crypto.rainbow.util.GF2Field;
+import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil;
+import org.spongycastle.util.Arrays;
+
+
+/**
+ * This class represents a layer of the Rainbow Oil- and Vinegar Map. Each Layer
+ * consists of oi polynomials with their coefficients, generated at random.
+ *
+ * To sign a document, we solve a LES (linear equation system) for each layer in
+ * order to find the oil variables of that layer and to be able to use the
+ * variables to compute the signature. This functionality is implemented in the
+ * RainbowSignature-class, by the aid of the private key.
+ *
+ * Each layer is a part of the private key.
+ *
+ * More information about the layer can be found in the paper of Jintai Ding,
+ * Dieter Schmidt: Rainbow, a New Multivariable Polynomial Signature Scheme.
+ * ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class Layer
+{
+ private int vi; // number of vinegars in this layer
+ private int viNext; // number of vinegars in next layer
+ private int oi; // number of oils in this layer
+
+ /*
+ * k : index of polynomial
+ *
+ * i,j : indices of oil and vinegar variables
+ */
+ private short[/* k */][/* i */][/* j */] coeff_alpha;
+ private short[/* k */][/* i */][/* j */] coeff_beta;
+ private short[/* k */][/* i */] coeff_gamma;
+ private short[/* k */] coeff_eta;
+
+ /**
+ * Constructor
+ *
+ * @param vi number of vinegar variables of this layer
+ * @param viNext number of vinegar variables of next layer. It's the same as
+ * (num of oils) + (num of vinegars) of this layer.
+ * @param coeffAlpha alpha-coefficients in the polynomials of this layer
+ * @param coeffBeta beta-coefficients in the polynomials of this layer
+ * @param coeffGamma gamma-coefficients in the polynomials of this layer
+ * @param coeffEta eta-coefficients in the polynomials of this layer
+ */
+ public Layer(byte vi, byte viNext, short[][][] coeffAlpha,
+ short[][][] coeffBeta, short[][] coeffGamma, short[] coeffEta)
+ {
+ this.vi = vi & 0xff;
+ this.viNext = viNext & 0xff;
+ this.oi = this.viNext - this.vi;
+
+ // the secret coefficients of all polynomials in this layer
+ this.coeff_alpha = coeffAlpha;
+ this.coeff_beta = coeffBeta;
+ this.coeff_gamma = coeffGamma;
+ this.coeff_eta = coeffEta;
+ }
+
+ /**
+ * This function generates the coefficients of all polynomials in this layer
+ * at random using random generator.
+ *
+ * @param sr the random generator which is to be used
+ */
+ public Layer(int vi, int viNext, SecureRandom sr)
+ {
+ this.vi = vi;
+ this.viNext = viNext;
+ this.oi = viNext - vi;
+
+ // the coefficients of all polynomials in this layer
+ this.coeff_alpha = new short[this.oi][this.oi][this.vi];
+ this.coeff_beta = new short[this.oi][this.vi][this.vi];
+ this.coeff_gamma = new short[this.oi][this.viNext];
+ this.coeff_eta = new short[this.oi];
+
+ int numOfPoly = this.oi; // number of polynomials per layer
+
+ // Alpha coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.oi; i++)
+ {
+ for (int j = 0; j < this.vi; j++)
+ {
+ coeff_alpha[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ }
+ // Beta coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.vi; i++)
+ {
+ for (int j = 0; j < this.vi; j++)
+ {
+ coeff_beta[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ }
+ // Gamma coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.viNext; i++)
+ {
+ coeff_gamma[k][i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ // Eta
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ coeff_eta[k] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+
+ /**
+ * This method plugs in the vinegar variables into the polynomials of this
+ * layer and computes the coefficients of the Oil-variables as well as the
+ * free coefficient in each polynomial.
+ *
+ * It is needed for computing the Oil variables while signing.
+ *
+ * @param x vinegar variables of this layer that should be plugged into
+ * the polynomials.
+ * @return coeff the coefficients of Oil variables and the free coeff in the
+ * polynomials of this layer.
+ */
+ public short[][] plugInVinegars(short[] x)
+ {
+ // temporary variable needed for the multiplication
+ short tmpMult = 0;
+ // coeff: 1st index = which polynomial, 2nd index=which variable
+ short[][] coeff = new short[oi][oi + 1]; // gets returned
+ // free coefficient per polynomial
+ short[] sum = new short[oi];
+
+ /*
+ * evaluate the beta-part of the polynomials (it contains no oil
+ * variables)
+ */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < vi; i++)
+ {
+ for (int j = 0; j < vi; j++)
+ {
+ // tmp = beta * xi (plug in)
+ tmpMult = GF2Field.multElem(coeff_beta[k][i][j], x[i]);
+ // tmp = tmp * xj
+ tmpMult = GF2Field.multElem(tmpMult, x[j]);
+ // accumulate into the array for the free coefficients.
+ sum[k] = GF2Field.addElem(sum[k], tmpMult);
+ }
+ }
+ }
+
+ /* evaluate the alpha-part (it contains oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < oi; i++)
+ {
+ for (int j = 0; j < vi; j++)
+ {
+ // alpha * xj (plug in)
+ tmpMult = GF2Field.multElem(coeff_alpha[k][i][j], x[j]);
+ // accumulate
+ coeff[k][i] = GF2Field.addElem(coeff[k][i], tmpMult);
+ }
+ }
+ }
+ /* evaluate the gama-part of the polynomial (containing no oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < vi; i++)
+ {
+ // gamma * xi (plug in)
+ tmpMult = GF2Field.multElem(coeff_gamma[k][i], x[i]);
+ // accumulate in the array for the free coefficients (per
+ // polynomial).
+ sum[k] = GF2Field.addElem(sum[k], tmpMult);
+ }
+ }
+ /* evaluate the gama-part of the polynomial (but containing oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = vi; i < viNext; i++)
+ { // oils
+ // accumulate the coefficients of the oil variables (per
+ // polynomial).
+ coeff[k][i - vi] = GF2Field.addElem(coeff_gamma[k][i],
+ coeff[k][i - vi]);
+ }
+ }
+ /* evaluate the eta-part of the polynomial */
+ for (int k = 0; k < oi; k++)
+ {
+ // accumulate in the array for the free coefficients per polynomial.
+ sum[k] = GF2Field.addElem(sum[k], coeff_eta[k]);
+ }
+
+ /* put the free coefficients (sum) into the coeff-array as last column */
+ for (int k = 0; k < oi; k++)
+ {
+ coeff[k][oi] = sum[k];
+ }
+ return coeff;
+ }
+
+ /**
+ * Getter for the number of vinegar variables of this layer.
+ *
+ * @return the number of vinegar variables of this layer.
+ */
+ public int getVi()
+ {
+ return vi;
+ }
+
+ /**
+ * Getter for the number of vinegar variables of the next layer.
+ *
+ * @return the number of vinegar variables of the next layer.
+ */
+ public int getViNext()
+ {
+ return viNext;
+ }
+
+ /**
+ * Getter for the number of Oil variables of this layer.
+ *
+ * @return the number of oil variables of this layer.
+ */
+ public int getOi()
+ {
+ return oi;
+ }
+
+ /**
+ * Getter for the alpha-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of alpha-terms of this layer.
+ */
+ public short[][][] getCoeffAlpha()
+ {
+ return coeff_alpha;
+ }
+
+ /**
+ * Getter for the beta-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of beta-terms of this layer.
+ */
+
+ public short[][][] getCoeffBeta()
+ {
+ return coeff_beta;
+ }
+
+ /**
+ * Getter for the gamma-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of gamma-terms of this layer
+ */
+ public short[][] getCoeffGamma()
+ {
+ return coeff_gamma;
+ }
+
+ /**
+ * Getter for the eta-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients eta of this layer
+ */
+ public short[] getCoeffEta()
+ {
+ return coeff_eta;
+ }
+
+ /**
+ * This function compares this Layer with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof Layer))
+ {
+ return false;
+ }
+ Layer otherLayer = (Layer)other;
+
+ return vi == otherLayer.getVi()
+ && viNext == otherLayer.getViNext()
+ && oi == otherLayer.getOi()
+ && RainbowUtil.equals(coeff_alpha, otherLayer.getCoeffAlpha())
+ && RainbowUtil.equals(coeff_beta, otherLayer.getCoeffBeta())
+ && RainbowUtil.equals(coeff_gamma, otherLayer.getCoeffGamma())
+ && RainbowUtil.equals(coeff_eta, otherLayer.getCoeffEta());
+ }
+
+ public int hashCode()
+ {
+ int hash = vi;
+ hash = hash * 37 + viNext;
+ hash = hash * 37 + oi;
+ hash = hash * 37 + Arrays.hashCode(coeff_alpha);
+ hash = hash * 37 + Arrays.hashCode(coeff_beta);
+ hash = hash * 37 + Arrays.hashCode(coeff_gamma);
+ hash = hash * 37 + Arrays.hashCode(coeff_eta);
+
+ return hash;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java
new file mode 100644
index 000000000..2cbc5b622
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java
@@ -0,0 +1,26 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class RainbowKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private RainbowParameters params;
+
+ public RainbowKeyGenerationParameters(
+ SecureRandom random,
+ RainbowParameters params)
+ {
+ // TODO: key size?
+ super(random, params.getVi()[params.getVi().length - 1] - params.getVi()[0]);
+ this.params = params;
+ }
+
+ public RainbowParameters getParameters()
+ {
+ return params;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
new file mode 100644
index 000000000..ac9a1ae78
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
@@ -0,0 +1,414 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.crypto.rainbow.util.ComputeInField;
+import org.spongycastle.pqc.crypto.rainbow.util.GF2Field;
+
+/**
+ * This class implements AsymmetricCipherKeyPairGenerator. It is used
+ * as a generator for the private and public key of the Rainbow Signature
+ * Scheme.
+ *
+ * Detailed information about the key generation is to be found in the paper of
+ * Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial
+ * Signature Scheme. ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class RainbowKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private boolean initialized = false;
+ private SecureRandom sr;
+ private RainbowKeyGenerationParameters rainbowParams;
+
+ /* linear affine map L1: */
+ private short[][] A1; // matrix of the lin. affine map L1(n-v1 x n-v1 matrix)
+ private short[][] A1inv; // inverted A1
+ private short[] b1; // translation element of the lin.affine map L1
+
+ /* linear affine map L2: */
+ private short[][] A2; // matrix of the lin. affine map (n x n matrix)
+ private short[][] A2inv; // inverted A2
+ private short[] b2; // translation elemt of the lin.affine map L2
+
+ /* components of F: */
+ private int numOfLayers; // u (number of sets S)
+ private Layer layers[]; // layers of polynomials of F
+ private int[] vi; // set of vinegar vars per layer.
+
+ /* components of Public Key */
+ private short[][] pub_quadratic; // quadratic(mixed) coefficients
+ private short[][] pub_singular; // singular coefficients
+ private short[] pub_scalar; // scalars
+
+ // TODO
+
+ /**
+ * The standard constructor tries to generate the Rainbow algorithm identifier
+ * with the corresponding OID.
+ *
+ */
+ public RainbowKeyPairGenerator()
+ {
+ }
+
+
+ /**
+ * This function generates a Rainbow key pair.
+ *
+ * @return the generated key pair
+ */
+ public AsymmetricCipherKeyPair genKeyPair()
+ {
+ RainbowPrivateKeyParameters privKey;
+ RainbowPublicKeyParameters pubKey;
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ /* choose all coefficients at random */
+ keygen();
+
+ /* now marshall them to PrivateKey */
+ privKey = new RainbowPrivateKeyParameters(A1inv, b1, A2inv, b2, vi, layers);
+
+
+ /* marshall to PublicKey */
+ pubKey = new RainbowPublicKeyParameters(vi[vi.length - 1] - vi[0], pub_quadratic, pub_singular, pub_scalar);
+
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ // TODO
+ public void initialize(
+ KeyGenerationParameters param)
+ {
+ this.rainbowParams = (RainbowKeyGenerationParameters)param;
+
+ // set source of randomness
+ this.sr = new SecureRandom();
+
+ // unmarshalling:
+ this.vi = this.rainbowParams.getParameters().getVi();
+ this.numOfLayers = this.rainbowParams.getParameters().getNumOfLayers();
+
+ this.initialized = true;
+ }
+
+ private void initializeDefault()
+ {
+ RainbowKeyGenerationParameters rbKGParams = new RainbowKeyGenerationParameters(new SecureRandom(), new RainbowParameters());
+ initialize(rbKGParams);
+ }
+
+ /**
+ * This function calls the functions for the random generation of the coefficients
+ * and the matrices needed for the private key and the method for computing the public key.
+ */
+ private void keygen()
+ {
+ generateL1();
+ generateL2();
+ generateF();
+ computePublicKey();
+ }
+
+ /**
+ * This function generates the invertible affine linear map L1 = A1*x + b1
+ *
+ * The translation part b1, is stored in a separate array. The inverse of
+ * the matrix-part of L1 A1inv is also computed here.
+ *
+ * This linear map hides the output of the map F. It is on k^(n-v1).
+ */
+ private void generateL1()
+ {
+
+ // dimension = n-v1 = vi[last] - vi[first]
+ int dim = vi[vi.length - 1] - vi[0];
+ this.A1 = new short[dim][dim];
+ this.A1inv = null;
+ ComputeInField c = new ComputeInField();
+
+ /* generation of A1 at random */
+ while (A1inv == null)
+ {
+ for (int i = 0; i < dim; i++)
+ {
+ for (int j = 0; j < dim; j++)
+ {
+ A1[i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ A1inv = c.inverse(A1);
+ }
+
+ /* generation of the translation vector at random */
+ b1 = new short[dim];
+ for (int i = 0; i < dim; i++)
+ {
+ b1[i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+
+ /**
+ * This function generates the invertible affine linear map L2 = A2*x + b2
+ *
+ * The translation part b2, is stored in a separate array. The inverse of
+ * the matrix-part of L2 A2inv is also computed here.
+ *
+ * This linear map hides the output of the map F. It is on k^(n).
+ */
+ private void generateL2()
+ {
+
+ // dimension = n = vi[last]
+ int dim = vi[vi.length - 1];
+ this.A2 = new short[dim][dim];
+ this.A2inv = null;
+ ComputeInField c = new ComputeInField();
+
+ /* generation of A2 at random */
+ while (this.A2inv == null)
+ {
+ for (int i = 0; i < dim; i++)
+ {
+ for (int j = 0; j < dim; j++)
+ { // one col extra for b
+ A2[i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ this.A2inv = c.inverse(A2);
+ }
+ /* generation of the translation vector at random */
+ b2 = new short[dim];
+ for (int i = 0; i < dim; i++)
+ {
+ b2[i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+
+ }
+
+ /**
+ * This function generates the private map F, which consists of u-1 layers.
+ * Each layer consists of oi polynomials where oi = vi[i+1]-vi[i].
+ *
+ * The methods for the generation of the coefficients of these polynomials
+ * are called here.
+ */
+ private void generateF()
+ {
+
+ this.layers = new Layer[this.numOfLayers];
+ for (int i = 0; i < this.numOfLayers; i++)
+ {
+ layers[i] = new Layer(this.vi[i], this.vi[i + 1], sr);
+ }
+ }
+
+ /**
+ * This function computes the public key from the private key.
+ *
+ * The composition of F with L2 is computed, followed by applying L1 to the
+ * composition's result. The singular and scalar values constitute to the
+ * public key as is, the quadratic terms are compacted in
+ * compactPublicKey()
+ */
+ private void computePublicKey()
+ {
+
+ ComputeInField c = new ComputeInField();
+ int rows = this.vi[this.vi.length - 1] - this.vi[0];
+ int vars = this.vi[this.vi.length - 1];
+ // Fpub
+ short[][][] coeff_quadratic_3dim = new short[rows][vars][vars];
+ this.pub_singular = new short[rows][vars];
+ this.pub_scalar = new short[rows];
+
+ // Coefficients of layers of Private Key F
+ short[][][] coeff_alpha;
+ short[][][] coeff_beta;
+ short[][] coeff_gamma;
+ short[] coeff_eta;
+
+ // Needed for counters;
+ int oils = 0;
+ int vins = 0;
+ int crnt_row = 0; // current row (polynomial)
+
+ short vect_tmp[] = new short[vars]; // vector tmp;
+ short sclr_tmp = 0;
+
+ // Composition of F and L2: Insert L2 = A2*x+b2 in F
+ for (int l = 0; l < this.layers.length; l++)
+ {
+ // get coefficients of current layer
+ coeff_alpha = this.layers[l].getCoeffAlpha();
+ coeff_beta = this.layers[l].getCoeffBeta();
+ coeff_gamma = this.layers[l].getCoeffGamma();
+ coeff_eta = this.layers[l].getCoeffEta();
+ oils = coeff_alpha[0].length;// this.layers[l].getOi();
+ vins = coeff_beta[0].length;// this.layers[l].getVi();
+ // compute polynomials of layer
+ for (int p = 0; p < oils; p++)
+ {
+ // multiply alphas
+ for (int x1 = 0; x1 < oils; x1++)
+ {
+ for (int x2 = 0; x2 < vins; x2++)
+ {
+ // multiply polynomial1 with polynomial2
+ vect_tmp = c.multVect(coeff_alpha[p][x1][x2],
+ this.A2[x1 + vins]);
+ coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix(
+ coeff_quadratic_3dim[crnt_row + p], c
+ .multVects(vect_tmp, this.A2[x2]));
+ // mul poly1 with scalar2
+ vect_tmp = c.multVect(this.b2[x2], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with poly2
+ vect_tmp = c.multVect(coeff_alpha[p][x1][x2],
+ this.A2[x2]);
+ vect_tmp = c.multVect(b2[x1 + vins], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with scalar2
+ sclr_tmp = GF2Field.multElem(coeff_alpha[p][x1][x2],
+ this.b2[x1 + vins]);
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field
+ .multElem(sclr_tmp, this.b2[x2]));
+ }
+ }
+ // multiply betas
+ for (int x1 = 0; x1 < vins; x1++)
+ {
+ for (int x2 = 0; x2 < vins; x2++)
+ {
+ // multiply polynomial1 with polynomial2
+ vect_tmp = c.multVect(coeff_beta[p][x1][x2],
+ this.A2[x1]);
+ coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix(
+ coeff_quadratic_3dim[crnt_row + p], c
+ .multVects(vect_tmp, this.A2[x2]));
+ // mul poly1 with scalar2
+ vect_tmp = c.multVect(this.b2[x2], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with poly2
+ vect_tmp = c.multVect(coeff_beta[p][x1][x2],
+ this.A2[x2]);
+ vect_tmp = c.multVect(this.b2[x1], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with scalar2
+ sclr_tmp = GF2Field.multElem(coeff_beta[p][x1][x2],
+ this.b2[x1]);
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field
+ .multElem(sclr_tmp, this.b2[x2]));
+ }
+ }
+ // multiply gammas
+ for (int n = 0; n < vins + oils; n++)
+ {
+ // mul poly with scalar
+ vect_tmp = c.multVect(coeff_gamma[p][n], this.A2[n]);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar with scalar
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field.multElem(
+ coeff_gamma[p][n], this.b2[n]));
+ }
+ // add eta
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], coeff_eta[p]);
+ }
+ crnt_row = crnt_row + oils;
+ }
+
+ // Apply L1 = A1*x+b1 to composition of F and L2
+ {
+ // temporary coefficient arrays
+ short[][][] tmp_c_quad = new short[rows][vars][vars];
+ short[][] tmp_c_sing = new short[rows][vars];
+ short[] tmp_c_scal = new short[rows];
+ for (int r = 0; r < rows; r++)
+ {
+ for (int q = 0; q < A1.length; q++)
+ {
+ tmp_c_quad[r] = c.addSquareMatrix(tmp_c_quad[r], c
+ .multMatrix(A1[r][q], coeff_quadratic_3dim[q]));
+ tmp_c_sing[r] = c.addVect(tmp_c_sing[r], c.multVect(
+ A1[r][q], this.pub_singular[q]));
+ tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], GF2Field
+ .multElem(A1[r][q], this.pub_scalar[q]));
+ }
+ tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], b1[r]);
+ }
+ // set public key
+ coeff_quadratic_3dim = tmp_c_quad;
+ this.pub_singular = tmp_c_sing;
+ this.pub_scalar = tmp_c_scal;
+ }
+ compactPublicKey(coeff_quadratic_3dim);
+ }
+
+ /**
+ * The quadratic (or mixed) terms of the public key are compacted from a n x
+ * n matrix per polynomial to an upper diagonal matrix stored in one integer
+ * array of n (n + 1) / 2 elements per polynomial. The ordering of elements
+ * is lexicographic and the result is updating this.pub_quadratic,
+ * which stores the quadratic elements of the public key.
+ *
+ * @param coeff_quadratic_to_compact 3-dimensional array containing a n x n Matrix for each of the
+ * n - v1 polynomials
+ */
+ private void compactPublicKey(short[][][] coeff_quadratic_to_compact)
+ {
+ int polynomials = coeff_quadratic_to_compact.length;
+ int n = coeff_quadratic_to_compact[0].length;
+ int entries = n * (n + 1) / 2;// the small gauss
+ this.pub_quadratic = new short[polynomials][entries];
+ int offset = 0;
+
+ for (int p = 0; p < polynomials; p++)
+ {
+ offset = 0;
+ for (int x = 0; x < n; x++)
+ {
+ for (int y = x; y < n; y++)
+ {
+ if (y == x)
+ {
+ this.pub_quadratic[p][offset] = coeff_quadratic_to_compact[p][x][y];
+ }
+ else
+ {
+ this.pub_quadratic[p][offset] = GF2Field.addElem(
+ coeff_quadratic_to_compact[p][x][y],
+ coeff_quadratic_to_compact[p][y][x]);
+ }
+ offset++;
+ }
+ }
+ }
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java
new file mode 100644
index 000000000..075afda4b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+public class RainbowKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private int docLength;
+
+ public RainbowKeyParameters(
+ boolean isPrivate,
+ int docLength)
+ {
+ super(isPrivate);
+ this.docLength = docLength;
+ }
+
+ /**
+ * @return the docLength
+ */
+ public int getDocLength()
+ {
+ return this.docLength;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java
new file mode 100644
index 000000000..edc0d0238
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java
@@ -0,0 +1,111 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class RainbowParameters
+ implements CipherParameters
+{
+
+ /**
+ * DEFAULT PARAMS
+ */
+ /*
+ * Vi = vinegars per layer whereas n is vu (vu = 33 = n) such that
+ *
+ * v1 = 6; o1 = 12-6 = 6
+ *
+ * v2 = 12; o2 = 17-12 = 5
+ *
+ * v3 = 17; o3 = 22-17 = 5
+ *
+ * v4 = 22; o4 = 33-22 = 11
+ *
+ * v5 = 33; (o5 = 0)
+ */
+ private final int[] DEFAULT_VI = {6, 12, 17, 22, 33};
+
+ private int[] vi;// set of vinegar vars per layer.
+
+ /**
+ * Default Constructor The elements of the array containing the number of
+ * Vinegar variables in each layer are set to the default values here.
+ */
+ public RainbowParameters()
+ {
+ this.vi = this.DEFAULT_VI;
+ }
+
+ /**
+ * Constructor with parameters
+ *
+ * @param vi The elements of the array containing the number of Vinegar
+ * variables per layer are set to the values of the input array.
+ */
+ public RainbowParameters(int[] vi)
+ {
+ this.vi = vi;
+ try
+ {
+ checkParams();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void checkParams()
+ throws Exception
+ {
+ if (vi == null)
+ {
+ throw new Exception("no layers defined.");
+ }
+ if (vi.length > 1)
+ {
+ for (int i = 0; i < vi.length - 1; i++)
+ {
+ if (vi[i] >= vi[i + 1])
+ {
+ throw new Exception(
+ "v[i] has to be smaller than v[i+1]");
+ }
+ }
+ }
+ else
+ {
+ throw new Exception(
+ "Rainbow needs at least 1 layer, such that v1 < v2.");
+ }
+ }
+
+ /**
+ * Getter for the number of layers
+ *
+ * @return the number of layers
+ */
+ public int getNumOfLayers()
+ {
+ return this.vi.length - 1;
+ }
+
+ /**
+ * Getter for the number of all the polynomials in Rainbow
+ *
+ * @return the number of the polynomials
+ */
+ public int getDocLength()
+ {
+ return vi[vi.length - 1] - vi[0];
+ }
+
+ /**
+ * Getter for the array containing the number of Vinegar-variables per layer
+ *
+ * @return the numbers of vinegars per layer
+ */
+ public int[] getVi()
+ {
+ return this.vi;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java
new file mode 100644
index 000000000..5d501a536
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java
@@ -0,0 +1,117 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+public class RainbowPrivateKeyParameters
+ extends RainbowKeyParameters
+{
+ /**
+ * Constructor
+ *
+ * @param A1inv the inverse of A1(the matrix part of the affine linear map L1)
+ * (n-v1 x n-v1 matrix)
+ * @param b1 translation vector, part of the linear affine map L1
+ * @param A2inv the inverse of A2(the matrix part of the affine linear map L2)
+ * (n x n matrix)
+ * @param b2 translation vector, part of the linear affine map L2
+ * @param vi the number of Vinegar-variables per layer
+ * @param layers the polynomials with their coefficients of private map F
+ */
+ public RainbowPrivateKeyParameters(short[][] A1inv, short[] b1,
+ short[][] A2inv, short[] b2, int[] vi, Layer[] layers)
+ {
+ super(true, vi[vi.length - 1] - vi[0]);
+
+ this.A1inv = A1inv;
+ this.b1 = b1;
+ this.A2inv = A2inv;
+ this.b2 = b2;
+ this.vi = vi;
+ this.layers = layers;
+ }
+
+ /*
+ * invertible affine linear map L1
+ */
+ // the inverse of A1, (n-v1 x n-v1 matrix)
+ private short[][] A1inv;
+
+ // translation vector of L1
+ private short[] b1;
+
+ /*
+ * invertible affine linear map L2
+ */
+ // the inverse of A2, (n x n matrix)
+ private short[][] A2inv;
+
+ // translation vector of L2
+ private short[] b2;
+
+ /*
+ * components of F
+ */
+ // the number of Vinegar-variables per layer.
+ private int[] vi;
+
+ // contains the polynomials with their coefficients of private map F
+ private Layer[] layers;
+
+ /**
+ * Getter for the translation part of the private quadratic map L1.
+ *
+ * @return b1 the translation part of L1
+ */
+ public short[] getB1()
+ {
+ return this.b1;
+ }
+
+ /**
+ * Getter for the inverse matrix of A1.
+ *
+ * @return the A1inv inverse
+ */
+ public short[][] getInvA1()
+ {
+ return this.A1inv;
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L2.
+ *
+ * @return b2 the translation part of L2
+ */
+ public short[] getB2()
+ {
+ return this.b2;
+ }
+
+ /**
+ * Getter for the inverse matrix of A2
+ *
+ * @return the A2inv
+ */
+ public short[][] getInvA2()
+ {
+ return this.A2inv;
+ }
+
+ /**
+ * Returns the layers contained in the private key
+ *
+ * @return layers
+ */
+ public Layer[] getLayers()
+ {
+ return this.layers;
+ }
+
+ /**
+ * /** Returns the array of vi-s
+ *
+ * @return the vi
+ */
+ public int[] getVi()
+ {
+ return vi;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java
new file mode 100644
index 000000000..3a5314e17
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java
@@ -0,0 +1,53 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+public class RainbowPublicKeyParameters
+ extends RainbowKeyParameters
+{
+ private short[][] coeffquadratic;
+ private short[][] coeffsingular;
+ private short[] coeffscalar;
+
+ /**
+ * Constructor
+ *
+ * @param docLength
+ * @param coeffQuadratic
+ * @param coeffSingular
+ * @param coeffScalar
+ */
+ public RainbowPublicKeyParameters(int docLength,
+ short[][] coeffQuadratic, short[][] coeffSingular,
+ short[] coeffScalar)
+ {
+ super(false, docLength);
+
+ this.coeffquadratic = coeffQuadratic;
+ this.coeffsingular = coeffSingular;
+ this.coeffscalar = coeffScalar;
+
+ }
+
+ /**
+ * @return the coeffquadratic
+ */
+ public short[][] getCoeffQuadratic()
+ {
+ return coeffquadratic;
+ }
+
+ /**
+ * @return the coeffsingular
+ */
+ public short[][] getCoeffSingular()
+ {
+ return coeffsingular;
+ }
+
+ /**
+ * @return the coeffscalar
+ */
+ public short[] getCoeffScalar()
+ {
+ return coeffscalar;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java
new file mode 100644
index 000000000..12bd9810b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java
@@ -0,0 +1,301 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageSigner;
+import org.spongycastle.pqc.crypto.rainbow.util.ComputeInField;
+import org.spongycastle.pqc.crypto.rainbow.util.GF2Field;
+
+/**
+ * It implements the sign and verify functions for the Rainbow Signature Scheme.
+ * Here the message, which has to be signed, is updated. The use of
+ * different hash functions is possible.
+ *
+ * Detailed information about the signature and the verify-method is to be found
+ * in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable
+ * Polynomial Signature Scheme. ACNS 2005: 164-175
+ * (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class RainbowSigner
+ implements MessageSigner
+{
+ // Source of randomness
+ private SecureRandom random;
+
+ // The length of a document that can be signed with the privKey
+ int signableDocumentLength;
+
+ // Container for the oil and vinegar variables of all the layers
+ private short[] x;
+
+ private ComputeInField cf = new ComputeInField();
+
+ RainbowKeyParameters key;
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (RainbowPrivateKeyParameters)rParam.getParameters();
+
+ }
+ else
+ {
+
+ this.random = new SecureRandom();
+ this.key = (RainbowPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (RainbowPublicKeyParameters)param;
+ }
+
+ this.signableDocumentLength = this.key.getDocLength();
+ }
+
+
+ /**
+ * initial operations before solving the Linear equation system.
+ *
+ * @param layer the current layer for which a LES is to be solved.
+ * @param msg the message that should be signed.
+ * @return Y_ the modified document needed for solving LES, (Y_ =
+ * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1.
+ */
+ private short[] initSign(Layer[] layer, short[] msg)
+ {
+
+ /* preparation: Modifies the document with the inverse of L1 */
+ // tmp = Y - b1:
+ short[] tmpVec = new short[msg.length];
+
+ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB1(), msg);
+
+ // Y_ = A1^{-1} * (Y - b1) :
+ short[] Y_ = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA1(), tmpVec);
+
+ /* generates the vinegar vars of the first layer at random */
+ for (int i = 0; i < layer[0].getVi(); i++)
+ {
+ x[i] = (short)random.nextInt();
+ x[i] = (short)(x[i] & GF2Field.MASK);
+ }
+
+ return Y_;
+ }
+
+ /**
+ * This function signs the message that has been updated, making use of the
+ * private key.
+ *
+ * For computing the signature, L1 and L2 are needed, as well as LES should
+ * be solved for each layer in order to find the Oil-variables in the layer.
+ *
+ * The Vinegar-variables of the first layer are random generated.
+ *
+ * @param message the message
+ * @return the signature of the message.
+ */
+ public byte[] generateSignature(byte[] message)
+ {
+ Layer[] layer = ((RainbowPrivateKeyParameters)this.key).getLayers();
+ int numberOfLayers = layer.length;
+
+ x = new short[((RainbowPrivateKeyParameters)this.key).getInvA2().length]; // all variables
+
+ short[] Y_; // modified document
+ short[] y_i; // part of Y_ each polynomial
+ int counter; // index of the current part of the doc
+
+ short[] solVec; // the solution of LES pro layer
+ short[] tmpVec;
+
+ // the signature as an array of shorts:
+ short[] signature;
+ // the signature as a byte-array:
+ byte[] S = new byte[layer[numberOfLayers - 1].getViNext()];
+
+ short[] msgHashVals = makeMessageRepresentative(message);
+
+ // shows if an exception is caught
+ boolean ok;
+ do
+ {
+ ok = true;
+ counter = 0;
+ try
+ {
+ Y_ = initSign(layer, msgHashVals);
+
+ for (int i = 0; i < numberOfLayers; i++)
+ {
+
+ y_i = new short[layer[i].getOi()];
+ solVec = new short[layer[i].getOi()]; // solution of LES
+
+ /* copy oi elements of Y_ into y_i */
+ for (int k = 0; k < layer[i].getOi(); k++)
+ {
+ y_i[k] = Y_[counter];
+ counter++; // current index of Y_
+ }
+
+ /*
+ * plug in the vars of the previous layer in order to get
+ * the vars of the current layer
+ */
+ solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i);
+
+ if (solVec == null)
+ { // LES is not solveable
+ throw new Exception("LES is not solveable!");
+ }
+
+ /* copy the new vars into the x-array */
+ for (int j = 0; j < solVec.length; j++)
+ {
+ x[layer[i].getVi() + j] = solVec[j];
+ }
+ }
+
+ /* apply the inverse of L2: (signature = A2^{-1}*(b2+x)) */
+ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB2(), x);
+ signature = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA2(), tmpVec);
+
+ /* cast signature from short[] to byte[] */
+ for (int i = 0; i < S.length; i++)
+ {
+ S[i] = ((byte)signature[i]);
+ }
+ }
+ catch (Exception se)
+ {
+ // if one of the LESs was not solveable - sign again
+ ok = false;
+ }
+ }
+ while (!ok);
+ /* return the signature in bytes */
+ return S;
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param message the message
+ * @param signature the signature of the message
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verifySignature(byte[] message, byte[] signature)
+ {
+ short[] sigInt = new short[signature.length];
+ short tmp;
+
+ for (int i = 0; i < signature.length; i++)
+ {
+ tmp = (short)signature[i];
+ tmp &= (short)0xff;
+ sigInt[i] = tmp;
+ }
+
+ short[] msgHashVal = makeMessageRepresentative(message);
+
+ // verify
+ short[] verificationResult = verifySignatureIntern(sigInt);
+
+ // compare
+ boolean verified = true;
+ if (msgHashVal.length != verificationResult.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < msgHashVal.length; i++)
+ {
+ verified = verified && msgHashVal[i] == verificationResult[i];
+ }
+
+ return verified;
+ }
+
+ /**
+ * Signature verification using public key
+ *
+ * @param signature vector of dimension n
+ * @return document hash of length n - v1
+ */
+ private short[] verifySignatureIntern(short[] signature)
+ {
+
+ short[][] coeff_quadratic = ((RainbowPublicKeyParameters)this.key).getCoeffQuadratic();
+ short[][] coeff_singular = ((RainbowPublicKeyParameters)this.key).getCoeffSingular();
+ short[] coeff_scalar = ((RainbowPublicKeyParameters)this.key).getCoeffScalar();
+
+ short[] rslt = new short[coeff_quadratic.length];// n - v1
+ int n = coeff_singular[0].length;
+ int offset = 0; // array position
+ short tmp = 0; // for scalar
+
+ for (int p = 0; p < coeff_quadratic.length; p++)
+ { // no of polynomials
+ offset = 0;
+ for (int x = 0; x < n; x++)
+ {
+ // calculate quadratic terms
+ for (int y = x; y < n; y++)
+ {
+ tmp = GF2Field.multElem(coeff_quadratic[p][offset],
+ GF2Field.multElem(signature[x], signature[y]));
+ rslt[p] = GF2Field.addElem(rslt[p], tmp);
+ offset++;
+ }
+ // calculate singular terms
+ tmp = GF2Field.multElem(coeff_singular[p][x], signature[x]);
+ rslt[p] = GF2Field.addElem(rslt[p], tmp);
+ }
+ // add scalar
+ rslt[p] = GF2Field.addElem(rslt[p], coeff_scalar[p]);
+ }
+
+ return rslt;
+ }
+
+ /**
+ * This function creates the representative of the message which gets signed
+ * or verified.
+ *
+ * @param message the message
+ * @return message representative
+ */
+ private short[] makeMessageRepresentative(byte[] message)
+ {
+ // the message representative
+ short[] output = new short[this.signableDocumentLength];
+
+ int h = 0;
+ int i = 0;
+ do
+ {
+ if (i >= message.length)
+ {
+ break;
+ }
+ output[i] = (short)message[h];
+ output[i] &= (short)0xff;
+ h++;
+ i++;
+ }
+ while (i < output.length);
+
+ return output;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java
new file mode 100644
index 000000000..2094fb031
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java
@@ -0,0 +1,490 @@
+package org.spongycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class offers different operations on matrices in field GF2^8.
+ *
+ * Implemented are functions:
+ * - finding inverse of a matrix
+ * - solving linear equation systems using the Gauss-Elimination method
+ * - basic operations like matrix multiplication, addition and so on.
+ */
+
+public class ComputeInField
+{
+
+ private short[][] A; // used by solveEquation and inverse
+ short[] x;
+
+ /**
+ * Constructor with no parameters
+ */
+ public ComputeInField()
+ {
+ }
+
+
+ /**
+ * This function finds a solution of the equation Bx = b.
+ * Exception is thrown if the linear equation system has no solution
+ *
+ * @param B this matrix is the left part of the
+ * equation (B in the equation above)
+ * @param b the right part of the equation
+ * (b in the equation above)
+ * @return x the solution of the equation if it is solvable
+ * null otherwise
+ * @throws RuntimeException if LES is not solvable
+ */
+ public short[] solveEquation(short[][] B, short[] b)
+ {
+ try
+ {
+
+ if (B.length != b.length)
+ {
+ throw new RuntimeException(
+ "The equation system is not solvable");
+ }
+
+ /** initialize **/
+ // this matrix stores B and b from the equation B*x = b
+ // b is stored as the last column.
+ // B contains one column more than rows.
+ // In this column we store a free coefficient that should be later subtracted from b
+ A = new short[B.length][B.length + 1];
+ // stores the solution of the LES
+ x = new short[B.length];
+
+ /** copy B into the global matrix A **/
+ for (int i = 0; i < B.length; i++)
+ { // rows
+ for (int j = 0; j < B[0].length; j++)
+ { // cols
+ A[i][j] = B[i][j];
+ }
+ }
+
+ /** copy the vector b into the global A **/
+ //the free coefficient, stored in the last column of A( A[i][b.length]
+ // is to be subtracted from b
+ for (int i = 0; i < b.length; i++)
+ {
+ A[i][b.length] = GF2Field.addElem(b[i], A[i][b.length]);
+ }
+
+ /** call the methods for gauss elimination and backward substitution **/
+ computeZerosUnder(false); // obtain zeros under the diagonal
+ substitute();
+
+ return x;
+
+ }
+ catch (RuntimeException rte)
+ {
+ return null; // the LES is not solvable!
+ }
+ }
+
+ /**
+ * This function computes the inverse of a given matrix using the Gauss-
+ * Elimination method.
+ *
+ * An exception is thrown if the matrix has no inverse
+ *
+ * @param coef the matrix which inverse matrix is needed
+ * @return inverse matrix of the input matrix.
+ * If the matrix is singular, null is returned.
+ * @throws RuntimeException if the given matrix is not invertible
+ */
+ public short[][] inverse(short[][] coef)
+ {
+ try
+ {
+ /** Initialization: **/
+ short factor;
+ short[][] inverse;
+ A = new short[coef.length][2 * coef.length];
+ if (coef.length != coef[0].length)
+ {
+ throw new RuntimeException(
+ "The matrix is not invertible. Please choose another one!");
+ }
+
+ /** prepare: Copy coef and the identity matrix into the global A. **/
+ for (int i = 0; i < coef.length; i++)
+ {
+ for (int j = 0; j < coef.length; j++)
+ {
+ //copy the input matrix coef into A
+ A[i][j] = coef[i][j];
+ }
+ // copy the identity matrix into A.
+ for (int j = coef.length; j < 2 * coef.length; j++)
+ {
+ A[i][j] = 0;
+ }
+ A[i][i + A.length] = 1;
+ }
+
+ /** Elimination operations to get the identity matrix from the left side of A. **/
+ // modify A to get 0s under the diagonal.
+ computeZerosUnder(true);
+
+ // modify A to get only 1s on the diagonal: A[i][j] =A[i][j]/A[i][i].
+ for (int i = 0; i < A.length; i++)
+ {
+ factor = GF2Field.invElem(A[i][i]);
+ for (int j = i; j < 2 * A.length; j++)
+ {
+ A[i][j] = GF2Field.multElem(A[i][j], factor);
+ }
+ }
+
+ //modify A to get only 0s above the diagonal.
+ computeZerosAbove();
+
+ // copy the result (the second half of A) in the matrix inverse.
+ inverse = new short[A.length][A.length];
+ for (int i = 0; i < A.length; i++)
+ {
+ for (int j = A.length; j < 2 * A.length; j++)
+ {
+ inverse[i][j - A.length] = A[i][j];
+ }
+ }
+ return inverse;
+
+ }
+ catch (RuntimeException rte)
+ {
+ // The matrix is not invertible! A new one should be generated!
+ return null;
+ }
+ }
+
+ /**
+ * Elimination under the diagonal.
+ * This function changes a matrix so that it contains only zeros under the
+ * diagonal(Ai,i) using only Gauss-Elimination operations.
+ *
+ * It is used in solveEquaton as well as in the function for
+ * finding an inverse of a matrix: {@link}inverse. Both of them use the
+ * Gauss-Elimination Method.
+ *
+ * The result is stored in the global matrix A
+ *
+ * @param usedForInverse This parameter shows if the function is used by the
+ * solveEquation-function or by the inverse-function and according
+ * to this creates matrices of different sizes.
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void computeZerosUnder(boolean usedForInverse)
+ throws RuntimeException
+ {
+
+ //the number of columns in the global A where the tmp results are stored
+ int length;
+ short tmp = 0;
+
+ //the function is used in inverse() - A should have 2 times more columns than rows
+ if (usedForInverse)
+ {
+ length = 2 * A.length;
+ }
+ //the function is used in solveEquation - A has 1 column more than rows
+ else
+ {
+ length = A.length + 1;
+ }
+
+ //elimination operations to modify A so that that it contains only 0s under the diagonal
+ for (int k = 0; k < A.length - 1; k++)
+ { // the fixed row
+ for (int i = k + 1; i < A.length; i++)
+ { // rows
+ short factor1 = A[i][k];
+ short factor2 = GF2Field.invElem(A[k][k]);
+
+ //The element which multiplicative inverse is needed, is 0
+ //in this case is the input matrix not invertible
+ if (factor2 == 0)
+ {
+ throw new RuntimeException("Matrix not invertible! We have to choose another one!");
+ }
+
+ for (int j = k; j < length; j++)
+ {// columns
+ // tmp=A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(A[k][j], factor2);
+ // tmp = A[i,k] * A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(factor1, tmp);
+ // A[i,j]=A[i,j]-A[i,k]/A[k,k]*A[k,j];
+ A[i][j] = GF2Field.addElem(A[i][j], tmp);
+ }
+ }
+ }
+ }
+
+ /**
+ * Elimination above the diagonal.
+ * This function changes a matrix so that it contains only zeros above the
+ * diagonal(Ai,i) using only Gauss-Elimination operations.
+ *
+ * It is used in the inverse-function
+ * The result is stored in the global matrix A
+ *
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void computeZerosAbove()
+ throws RuntimeException
+ {
+ short tmp = 0;
+ for (int k = A.length - 1; k > 0; k--)
+ { // the fixed row
+ for (int i = k - 1; i >= 0; i--)
+ { // rows
+ short factor1 = A[i][k];
+ short factor2 = GF2Field.invElem(A[k][k]);
+ if (factor2 == 0)
+ {
+ throw new RuntimeException("The matrix is not invertible");
+ }
+ for (int j = k; j < 2 * A.length; j++)
+ { // columns
+ // tmp = A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(A[k][j], factor2);
+ // tmp = A[i,k] * A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(factor1, tmp);
+ // A[i,j] = A[i,j] - A[i,k] / A[k,k] * A[k,j];
+ A[i][j] = GF2Field.addElem(A[i][j], tmp);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This function uses backward substitution to find x
+ * of the linear equation system (LES) B*x = b,
+ * where A a triangle-matrix is (contains only zeros under the diagonal)
+ * and b is a vector
+ *
+ * If the multiplicative inverse of 0 is needed, an exception is thrown.
+ * In this case is the LES not solvable
+ *
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void substitute()
+ throws RuntimeException
+ {
+
+ // for the temporary results of the operations in field
+ short tmp, temp;
+
+ temp = GF2Field.invElem(A[A.length - 1][A.length - 1]);
+ if (temp == 0)
+ {
+ throw new RuntimeException("The equation system is not solvable");
+ }
+
+ /** backward substitution **/
+ x[A.length - 1] = GF2Field.multElem(A[A.length - 1][A.length], temp);
+ for (int i = A.length - 2; i >= 0; i--)
+ {
+ tmp = A[i][A.length];
+ for (int j = A.length - 1; j > i; j--)
+ {
+ temp = GF2Field.multElem(A[i][j], x[j]);
+ tmp = GF2Field.addElem(tmp, temp);
+ }
+
+ temp = GF2Field.invElem(A[i][i]);
+ if (temp == 0)
+ {
+ throw new RuntimeException("Not solvable equation system");
+ }
+ x[i] = GF2Field.multElem(tmp, temp);
+ }
+ }
+
+
+ /**
+ * This function multiplies two given matrices.
+ * If the given matrices cannot be multiplied due
+ * to different sizes, an exception is thrown.
+ *
+ * @param M1 -the 1st matrix
+ * @param M2 -the 2nd matrix
+ * @return A = M1*M2
+ * @throws RuntimeException in case the given matrices cannot be multiplied
+ * due to different dimensions.
+ */
+ public short[][] multiplyMatrix(short[][] M1, short[][] M2)
+ throws RuntimeException
+ {
+
+ if (M1[0].length != M2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short tmp = 0;
+ A = new short[M1.length][M2[0].length];
+ for (int i = 0; i < M1.length; i++)
+ {
+ for (int j = 0; j < M2.length; j++)
+ {
+ for (int k = 0; k < M2[0].length; k++)
+ {
+ tmp = GF2Field.multElem(M1[i][j], M2[j][k]);
+ A[i][k] = GF2Field.addElem(A[i][k], tmp);
+ }
+ }
+ }
+ return A;
+ }
+
+ /**
+ * This function multiplies a given matrix with a one-dimensional array.
+ *
+ * An exception is thrown, if the number of columns in the matrix and
+ * the number of rows in the one-dim. array differ.
+ *
+ * @param M1 the matrix to be multiplied
+ * @param m the one-dimensional array to be multiplied
+ * @return M1*m
+ * @throws RuntimeException in case of dimension inconsistency
+ */
+ public short[] multiplyMatrix(short[][] M1, short[] m)
+ throws RuntimeException
+ {
+ if (M1[0].length != m.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short tmp = 0;
+ short[] B = new short[M1.length];
+ for (int i = 0; i < M1.length; i++)
+ {
+ for (int j = 0; j < m.length; j++)
+ {
+ tmp = GF2Field.multElem(M1[i][j], m[j]);
+ B[i] = GF2Field.addElem(B[i], tmp);
+ }
+ }
+ return B;
+ }
+
+ /**
+ * Addition of two vectors
+ *
+ * @param vector1 first summand, always of dim n
+ * @param vector2 second summand, always of dim n
+ * @return addition of vector1 and vector2
+ * @throws RuntimeException in case the addition is impossible
+ * due to inconsistency in the dimensions
+ */
+ public short[] addVect(short[] vector1, short[] vector2)
+ {
+ if (vector1.length != vector2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short rslt[] = new short[vector1.length];
+ for (int n = 0; n < rslt.length; n++)
+ {
+ rslt[n] = GF2Field.addElem(vector1[n], vector2[n]);
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplication of column vector with row vector
+ *
+ * @param vector1 column vector, always n x 1
+ * @param vector2 row vector, always 1 x n
+ * @return resulting n x n matrix of multiplication
+ * @throws RuntimeException in case the multiplication is impossible due to
+ * inconsistency in the dimensions
+ */
+ public short[][] multVects(short[] vector1, short[] vector2)
+ {
+ if (vector1.length != vector2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short rslt[][] = new short[vector1.length][vector2.length];
+ for (int i = 0; i < vector1.length; i++)
+ {
+ for (int j = 0; j < vector2.length; j++)
+ {
+ rslt[i][j] = GF2Field.multElem(vector1[i], vector2[j]);
+ }
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplies vector with scalar
+ *
+ * @param scalar galois element to multiply vector with
+ * @param vector vector to be multiplied
+ * @return vector multiplied with scalar
+ */
+ public short[] multVect(short scalar, short[] vector)
+ {
+ short rslt[] = new short[vector.length];
+ for (int n = 0; n < rslt.length; n++)
+ {
+ rslt[n] = GF2Field.multElem(scalar, vector[n]);
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplies matrix with scalar
+ *
+ * @param scalar galois element to multiply matrix with
+ * @param matrix 2-dim n x n matrix to be multiplied
+ * @return matrix multiplied with scalar
+ */
+ public short[][] multMatrix(short scalar, short[][] matrix)
+ {
+ short[][] rslt = new short[matrix.length][matrix[0].length];
+ for (int i = 0; i < matrix.length; i++)
+ {
+ for (int j = 0; j < matrix[0].length; j++)
+ {
+ rslt[i][j] = GF2Field.multElem(scalar, matrix[i][j]);
+ }
+ }
+ return rslt;
+ }
+
+ /**
+ * Adds the n x n matrices matrix1 and matrix2
+ *
+ * @param matrix1 first summand
+ * @param matrix2 second summand
+ * @return addition of matrix1 and matrix2; both having the dimensions n x n
+ * @throws RuntimeException in case the addition is not possible because of
+ * different dimensions of the matrices
+ */
+ public short[][] addSquareMatrix(short[][] matrix1, short[][] matrix2)
+ {
+ if (matrix1.length != matrix2.length || matrix1[0].length != matrix2[0].length)
+ {
+ throw new RuntimeException("Addition is not possible!");
+ }
+
+ short[][] rslt = new short[matrix1.length][matrix1.length];//
+ for (int i = 0; i < matrix1.length; i++)
+ {
+ for (int j = 0; j < matrix2.length; j++)
+ {
+ rslt[i][j] = GF2Field.addElem(matrix1[i][j], matrix2[i][j]);
+ }
+ }
+ return rslt;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java
new file mode 100644
index 000000000..19b47a2fd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java
@@ -0,0 +1,139 @@
+package org.spongycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class provides the basic operations like addition, multiplication and
+ * finding the multiplicative inverse of an element in GF2^8.
+ *
+ * The operations are implemented using the irreducible polynomial
+ * 1+x^2+x^3+x^6+x^8 ( 1 0100 1101 = 0x14d )
+ *
+ * This class makes use of lookup tables(exps and logs) for implementing the
+ * operations in order to increase the efficiency of Rainbow.
+ */
+public class GF2Field
+{
+
+ public static final int MASK = 0xff;
+
+ /*
+ * this lookup table is needed for multiplication and computing the
+ * multiplicative inverse
+ */
+ static final short exps[] = {1, 2, 4, 8, 16, 32, 64, 128, 77, 154, 121, 242,
+ 169, 31, 62, 124, 248, 189, 55, 110, 220, 245, 167, 3, 6, 12, 24,
+ 48, 96, 192, 205, 215, 227, 139, 91, 182, 33, 66, 132, 69, 138, 89,
+ 178, 41, 82, 164, 5, 10, 20, 40, 80, 160, 13, 26, 52, 104, 208,
+ 237, 151, 99, 198, 193, 207, 211, 235, 155, 123, 246, 161, 15, 30,
+ 60, 120, 240, 173, 23, 46, 92, 184, 61, 122, 244, 165, 7, 14, 28,
+ 56, 112, 224, 141, 87, 174, 17, 34, 68, 136, 93, 186, 57, 114, 228,
+ 133, 71, 142, 81, 162, 9, 18, 36, 72, 144, 109, 218, 249, 191, 51,
+ 102, 204, 213, 231, 131, 75, 150, 97, 194, 201, 223, 243, 171, 27,
+ 54, 108, 216, 253, 183, 35, 70, 140, 85, 170, 25, 50, 100, 200,
+ 221, 247, 163, 11, 22, 44, 88, 176, 45, 90, 180, 37, 74, 148, 101,
+ 202, 217, 255, 179, 43, 86, 172, 21, 42, 84, 168, 29, 58, 116, 232,
+ 157, 119, 238, 145, 111, 222, 241, 175, 19, 38, 76, 152, 125, 250,
+ 185, 63, 126, 252, 181, 39, 78, 156, 117, 234, 153, 127, 254, 177,
+ 47, 94, 188, 53, 106, 212, 229, 135, 67, 134, 65, 130, 73, 146,
+ 105, 210, 233, 159, 115, 230, 129, 79, 158, 113, 226, 137, 95, 190,
+ 49, 98, 196, 197, 199, 195, 203, 219, 251, 187, 59, 118, 236, 149,
+ 103, 206, 209, 239, 147, 107, 214, 225, 143, 83, 166, 1};
+
+ /*
+ * this lookup table is needed for multiplication and computing the
+ * multiplicative inverse
+ */
+ static final short logs[] = {0, 0, 1, 23, 2, 46, 24, 83, 3, 106, 47, 147,
+ 25, 52, 84, 69, 4, 92, 107, 182, 48, 166, 148, 75, 26, 140, 53,
+ 129, 85, 170, 70, 13, 5, 36, 93, 135, 108, 155, 183, 193, 49, 43,
+ 167, 163, 149, 152, 76, 202, 27, 230, 141, 115, 54, 205, 130, 18,
+ 86, 98, 171, 240, 71, 79, 14, 189, 6, 212, 37, 210, 94, 39, 136,
+ 102, 109, 214, 156, 121, 184, 8, 194, 223, 50, 104, 44, 253, 168,
+ 138, 164, 90, 150, 41, 153, 34, 77, 96, 203, 228, 28, 123, 231, 59,
+ 142, 158, 116, 244, 55, 216, 206, 249, 131, 111, 19, 178, 87, 225,
+ 99, 220, 172, 196, 241, 175, 72, 10, 80, 66, 15, 186, 190, 199, 7,
+ 222, 213, 120, 38, 101, 211, 209, 95, 227, 40, 33, 137, 89, 103,
+ 252, 110, 177, 215, 248, 157, 243, 122, 58, 185, 198, 9, 65, 195,
+ 174, 224, 219, 51, 68, 105, 146, 45, 82, 254, 22, 169, 12, 139,
+ 128, 165, 74, 91, 181, 151, 201, 42, 162, 154, 192, 35, 134, 78,
+ 188, 97, 239, 204, 17, 229, 114, 29, 61, 124, 235, 232, 233, 60,
+ 234, 143, 125, 159, 236, 117, 30, 245, 62, 56, 246, 217, 63, 207,
+ 118, 250, 31, 132, 160, 112, 237, 20, 144, 179, 126, 88, 251, 226,
+ 32, 100, 208, 221, 119, 173, 218, 197, 64, 242, 57, 176, 247, 73,
+ 180, 11, 127, 81, 21, 67, 145, 16, 113, 187, 238, 191, 133, 200,
+ 161};
+
+ /**
+ * This function calculates the sum of two elements as an operation in GF2^8
+ *
+ * @param x the first element that is to be added
+ * @param y the second element that should be add
+ * @return the sum of the two elements x and y in GF2^8
+ */
+ public static short addElem(short x, short y)
+ {
+ return (short)(x ^ y);
+ }
+
+ /**
+ * This function computes the multiplicative inverse of a given element in
+ * GF2^8 The 0 has no multiplicative inverse and in this case 0 is returned.
+ *
+ * @param x the element which multiplicative inverse is to be computed
+ * @return the multiplicative inverse of the given element, in case it
+ * exists or 0, otherwise
+ */
+ public static short invElem(short x)
+ {
+ if (x == 0)
+ {
+ return 0;
+ }
+ return (exps[255 - logs[x]]);
+ }
+
+ /**
+ * This function multiplies two elements in GF2^8. If one of the two
+ * elements is 0, 0 is returned.
+ *
+ * @param x the first element to be multiplied.
+ * @param y the second element to be multiplied.
+ * @return the product of the two input elements in GF2^8.
+ */
+ public static short multElem(short x, short y)
+ {
+ if (x == 0 || y == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return (exps[(logs[x] + logs[y]) % 255]);
+ }
+ }
+
+ /**
+ * This function returns the values of exps-lookup table which correspond to
+ * the input
+ *
+ * @param x the index in the lookup table exps
+ * @return exps-value, corresponding to the input
+ */
+ public static short getExp(short x)
+ {
+ return exps[x];
+ }
+
+ /**
+ * This function returns the values of logs-lookup table which correspond to
+ * the input
+ *
+ * @param x the index in the lookup table logs
+ * @return logs-value, corresponding to the input
+ */
+ public static short getLog(short x)
+ {
+ return logs[x];
+ }
+
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java
new file mode 100644
index 000000000..1e4f40f48
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java
@@ -0,0 +1,230 @@
+package org.spongycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class is needed for the conversions while encoding and decoding, as well as for
+ * comparison between arrays of some dimensions
+ */
+public class RainbowUtil
+{
+
+ /**
+ * This function converts an one-dimensional array of bytes into a
+ * one-dimensional array of int
+ *
+ * @param in the array to be converted
+ * @return out
+ * the one-dimensional int-array that corresponds the input
+ */
+ public static int[] convertArraytoInt(byte[] in)
+ {
+ int[] out = new int[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = in[i] & GF2Field.MASK;
+ }
+ return out;
+ }
+
+ /**
+ * This function converts an one-dimensional array of bytes into a
+ * one-dimensional array of type short
+ *
+ * @param in the array to be converted
+ * @return out
+ * one-dimensional short-array that corresponds the input
+ */
+ public static short[] convertArray(byte[] in)
+ {
+ short[] out = new short[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (short)(in[i] & GF2Field.MASK);
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a matrix of bytes into a matrix of type short
+ *
+ * @param in the matrix to be converted
+ * @return out
+ * short-matrix that corresponds the input
+ */
+ public static short[][] convertArray(byte[][] in)
+ {
+ short[][] out = new short[in.length][in[0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ out[i][j] = (short)(in[i][j] & GF2Field.MASK);
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a 3-dimensional array of bytes into a 3-dimensional array of type short
+ *
+ * @param in the array to be converted
+ * @return out
+ * short-array that corresponds the input
+ */
+ public static short[][][] convertArray(byte[][][] in)
+ {
+ short[][][] out = new short[in.length][in[0].length][in[0][0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ for (int k = 0; k < in[0][0].length; k++)
+ {
+ out[i][j][k] = (short)(in[i][j][k] & GF2Field.MASK);
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts an array of type int into an array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[] convertIntArray(int[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (byte)in[i];
+ }
+ return out;
+ }
+
+
+ /**
+ * This function converts an array of type short into an array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[] convertArray(short[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (byte)in[i];
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a matrix of type short into a matrix of type byte
+ *
+ * @param in the matrix to be converted
+ * @return out
+ * the byte-matrix that corresponds the input
+ */
+ public static byte[][] convertArray(short[][] in)
+ {
+ byte[][] out = new byte[in.length][in[0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ out[i][j] = (byte)in[i][j];
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a 3-dimensional array of type short into a 3-dimensional array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[][][] convertArray(short[][][] in)
+ {
+ byte[][][] out = new byte[in.length][in[0].length][in[0][0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ for (int k = 0; k < in[0][0].length; k++)
+ {
+ out[i][j][k] = (byte)in[i][j][k];
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Compare two short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[] left, short[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[][] left, short[][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= equals(left[i], right[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[][][] left, short[][][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= equals(left[i], right[i]);
+ }
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java
new file mode 100644
index 000000000..4ed7f1c91
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java
@@ -0,0 +1,306 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This is a utility class containing data type conversions using big-endian
+ * byte order.
+ *
+ * @see LittleEndianConversions
+ */
+public final class BigEndianConversions
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private BigEndianConversions()
+ {
+ // empty
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param x the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(int x)
+ {
+ byte[] result = new byte[4];
+ result[0] = (byte)(x >>> 24);
+ result[1] = (byte)(x >>> 16);
+ result[2] = (byte)(x >>> 8);
+ result[3] = (byte)x;
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string according to IEEE 1363, Section
+ * 5.5.3. Length checking is performed.
+ *
+ * @param x the integer to convert
+ * @param oLen the desired length of the octet string
+ * @return an octet string of length oLen representing the
+ * integer x, or null if the integer is
+ * negative
+ * @throws ArithmeticException if x can't be encoded into oLen
+ * octets.
+ */
+ public static byte[] I2OSP(int x, int oLen)
+ throws ArithmeticException
+ {
+ if (x < 0)
+ {
+ return null;
+ }
+ int octL = IntegerFunctions.ceilLog256(x);
+ if (octL > oLen)
+ {
+ throw new ArithmeticException(
+ "Cannot encode given integer into specified number of octets.");
+ }
+ byte[] result = new byte[oLen];
+ for (int i = oLen - 1; i >= oLen - octL; i--)
+ {
+ result[i] = (byte)(x >>> (8 * (oLen - 1 - i)));
+ }
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ */
+ public static void I2OSP(int input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff] = (byte)input;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 8 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(long input)
+ {
+ byte[] output = new byte[8];
+ output[0] = (byte)(input >>> 56);
+ output[1] = (byte)(input >>> 48);
+ output[2] = (byte)(input >>> 40);
+ output[3] = (byte)(input >>> 32);
+ output[4] = (byte)(input >>> 24);
+ output[5] = (byte)(input >>> 16);
+ output[6] = (byte)(input >>> 8);
+ output[7] = (byte)input;
+ return output;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 8 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ */
+ public static void I2OSP(long input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)(input >>> 56);
+ output[outOff++] = (byte)(input >>> 48);
+ output[outOff++] = (byte)(input >>> 40);
+ output[outOff++] = (byte)(input >>> 32);
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff] = (byte)input;
+ }
+
+ /**
+ * Convert an integer to an octet string of the specified length according
+ * to IEEE 1363, Section 5.5.3. No length checking is performed (i.e., if
+ * the integer cannot be encoded into length octets, it is
+ * truncated).
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ * @param length the length of the encoding
+ */
+ public static void I2OSP(int input, byte[] output, int outOff, int length)
+ {
+ for (int i = length - 1; i >= 0; i--)
+ {
+ output[outOff + i] = (byte)(input >>> (8 * (length - 1 - i)));
+ }
+ }
+
+ /**
+ * Convert an octet string to an integer according to IEEE 1363, Section
+ * 5.5.3.
+ *
+ * @param input the byte array holding the octet string
+ * @return an integer representing the octet string input, or
+ * 0 if the represented integer is negative or too large
+ * or the byte array is empty
+ * @throws ArithmeticException if the length of the given octet string is larger than 4.
+ */
+ public static int OS2IP(byte[] input)
+ {
+ if (input.length > 4)
+ {
+ throw new ArithmeticException("invalid input length");
+ }
+ if (input.length == 0)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int j = 0; j < input.length; j++)
+ {
+ result |= (input[j] & 0xff) << (8 * (input.length - 1 - j));
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array of length 4 beginning at offset into an
+ * integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting integer
+ */
+ public static int OS2IP(byte[] input, int inOff)
+ {
+ int result = (input[inOff++] & 0xff) << 24;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= input[inOff] & 0xff;
+ return result;
+ }
+
+ /**
+ * Convert an octet string to an integer according to IEEE 1363, Section
+ * 5.5.3.
+ *
+ * @param input the byte array holding the octet string
+ * @param inOff the offset in the input byte array where the octet string
+ * starts
+ * @param inLen the length of the encoded integer
+ * @return an integer representing the octet string bytes, or
+ * 0 if the represented integer is negative or too large
+ * or the byte array is empty
+ */
+ public static int OS2IP(byte[] input, int inOff, int inLen)
+ {
+ if ((input.length == 0) || input.length < inOff + inLen - 1)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int j = 0; j < inLen; j++)
+ {
+ result |= (input[inOff + j] & 0xff) << (8 * (inLen - j - 1));
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array of length 8 beginning at inOff into a
+ * long integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting long integer
+ */
+ public static long OS2LIP(byte[] input, int inOff)
+ {
+ long result = ((long)input[inOff++] & 0xff) << 56;
+ result |= ((long)input[inOff++] & 0xff) << 48;
+ result |= ((long)input[inOff++] & 0xff) << 40;
+ result |= ((long)input[inOff++] & 0xff) << 32;
+ result |= ((long)input[inOff++] & 0xff) << 24;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= input[inOff] & 0xff;
+ return result;
+ }
+
+ /**
+ * Convert an int array into a byte array.
+ *
+ * @param input the int array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(final int[] input)
+ {
+ byte[] result = new byte[input.length << 2];
+ for (int i = 0; i < input.length; i++)
+ {
+ I2OSP(input[i], result, i << 2);
+ }
+ return result;
+ }
+
+ /**
+ * Convert an int array into a byte array of the specified length. No length
+ * checking is performed (i.e., if the last integer cannot be encoded into
+ * length % 4 octets, it is truncated).
+ *
+ * @param input the int array
+ * @param length the length of the converted array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(final int[] input, int length)
+ {
+ final int intLen = input.length;
+ byte[] result = new byte[length];
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ I2OSP(input[i], result, index);
+ }
+ I2OSP(input[intLen - 1], result, index, length - index);
+ return result;
+ }
+
+ /**
+ * Convert a byte array into an int array.
+ *
+ * @param input the byte array
+ * @return the converted array
+ */
+ public static int[] toIntArray(byte[] input)
+ {
+ final int intLen = (input.length + 3) / 4;
+ final int lastLen = input.length & 0x03;
+ int[] result = new int[intLen];
+
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ result[i] = OS2IP(input, index);
+ }
+ if (lastLen != 0)
+ {
+ result[intLen - 1] = OS2IP(input, index, lastLen);
+ }
+ else
+ {
+ result[intLen - 1] = OS2IP(input, index);
+ }
+
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java
new file mode 100644
index 000000000..19734977a
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java
@@ -0,0 +1,138 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+/**
+ * FIXME: is this really necessary?!
+ */
+public final class BigIntUtils
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private BigIntUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Checks if two BigInteger arrays contain the same entries
+ *
+ * @param a first BigInteger array
+ * @param b second BigInteger array
+ * @return true or false
+ */
+ public static boolean equals(BigInteger[] a, BigInteger[] b)
+ {
+ int flag = 0;
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < a.length; i++)
+ {
+ // avoid branches here!
+ // problem: compareTo on BigIntegers is not
+ // guaranteed constant-time!
+ flag |= a[i].compareTo(b[i]);
+ }
+ return flag == 0;
+ }
+
+ /**
+ * Fill the given BigInteger array with the given value.
+ *
+ * @param array the array
+ * @param value the value
+ */
+ public static void fill(BigInteger[] array, BigInteger value)
+ {
+ for (int i = array.length - 1; i >= 0; i--)
+ {
+ array[i] = value;
+ }
+ }
+
+ /**
+ * Generates a subarray of a given BigInteger array.
+ *
+ * @param input -
+ * the input BigInteger array
+ * @param start -
+ * the start index
+ * @param end -
+ * the end index
+ * @return a subarray of input, ranging from start to
+ * end
+ */
+ public static BigInteger[] subArray(BigInteger[] input, int start, int end)
+ {
+ BigInteger[] result = new BigInteger[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Converts a BigInteger array into an integer array
+ *
+ * @param input -
+ * the BigInteger array
+ * @return the integer array
+ */
+ public static int[] toIntArray(BigInteger[] input)
+ {
+ int[] result = new int[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = input[i].intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Converts a BigInteger array into an integer array, reducing all
+ * BigIntegers mod q.
+ *
+ * @param q -
+ * the modulus
+ * @param input -
+ * the BigInteger array
+ * @return the integer array
+ */
+ public static int[] toIntArrayModQ(int q, BigInteger[] input)
+ {
+ BigInteger bq = BigInteger.valueOf(q);
+ int[] result = new int[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = input[i].mod(bq).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Return the value of big as a byte array. Although BigInteger
+ * has such a method, it uses an extra bit to indicate the sign of the
+ * number. For elliptic curve cryptography, the numbers usually are
+ * positive. Thus, this helper method returns a byte array of minimal
+ * length, ignoring the sign of the number.
+ *
+ * @param value the BigInteger value to be converted to a byte
+ * array
+ * @return the value big as byte array
+ */
+ public static byte[] toMinimalByteArray(BigInteger value)
+ {
+ byte[] valBytes = value.toByteArray();
+ if ((valBytes.length == 1) || (value.bitLength() & 0x07) != 0)
+ {
+ return valBytes;
+ }
+ byte[] result = new byte[value.bitLength() >> 3];
+ System.arraycopy(valBytes, 1, result, 0, result.length);
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java
new file mode 100644
index 000000000..0e234ae9c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java
@@ -0,0 +1,414 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class is a utility class for manipulating byte arrays.
+ */
+public final class ByteUtils
+{
+
+ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * Default constructor (private)
+ */
+ private ByteUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Compare two byte arrays (perform null checks beforehand).
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[] left, byte[] right)
+ {
+ if (left == null)
+ {
+ return right == null;
+ }
+ if (right == null)
+ {
+ return false;
+ }
+
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][] left, byte[][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= ByteUtils.equals(left[i], right[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][][] left, byte[][][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ if (left[i].length != right[i].length)
+ {
+ return false;
+ }
+ for (int j = left[i].length - 1; j >= 0; j--)
+ {
+ result &= ByteUtils.equals(left[i][j], right[i][j]);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a one-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + array[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a two-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a three-dimensional byte
+ * array rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][][] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+
+ /**
+ * Return a clone of the given byte array (performs null check beforehand).
+ *
+ * @param array the array to clone
+ * @return the clone of the given array, or null if the array is
+ * null
+ */
+ public static byte[] clone(byte[] array)
+ {
+ if (array == null)
+ {
+ return null;
+ }
+ byte[] result = new byte[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert a string containing hexadecimal characters to a byte-array.
+ *
+ * @param s a hex string
+ * @return a byte array with the corresponding value
+ */
+ public static byte[] fromHexString(String s)
+ {
+ char[] rawChars = s.toUpperCase().toCharArray();
+
+ int hexChars = 0;
+ for (int i = 0; i < rawChars.length; i++)
+ {
+ if ((rawChars[i] >= '0' && rawChars[i] <= '9')
+ || (rawChars[i] >= 'A' && rawChars[i] <= 'F'))
+ {
+ hexChars++;
+ }
+ }
+
+ byte[] byteString = new byte[(hexChars + 1) >> 1];
+
+ int pos = hexChars & 1;
+
+ for (int i = 0; i < rawChars.length; i++)
+ {
+ if (rawChars[i] >= '0' && rawChars[i] <= '9')
+ {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - '0';
+ }
+ else if (rawChars[i] >= 'A' && rawChars[i] <= 'F')
+ {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - 'A' + 10;
+ }
+ else
+ {
+ continue;
+ }
+ pos++;
+ }
+
+ return byteString;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hexstring.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding hexstring
+ */
+ public static String toHexString(byte[] input)
+ {
+ String result = "";
+ for (int i = 0; i < input.length; i++)
+ {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hex string.
+ *
+ * @param input the byte array to be converted
+ * @param prefix the prefix to put at the beginning of the hex string
+ * @param seperator a separator string
+ * @return the corresponding hex string
+ */
+ public static String toHexString(byte[] input, String prefix,
+ String seperator)
+ {
+ String result = new String(prefix);
+ for (int i = 0; i < input.length; i++)
+ {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ if (i < input.length - 1)
+ {
+ result += seperator;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding bit string.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding bit string
+ */
+ public static String toBinaryString(byte[] input)
+ {
+ String result = "";
+ int i;
+ for (i = 0; i < input.length; i++)
+ {
+ int e = input[i];
+ for (int ii = 0; ii < 8; ii++)
+ {
+ int b = (e >>> ii) & 1;
+ result += b;
+ }
+ if (i != input.length - 1)
+ {
+ result += " ";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the bitwise XOR of two arrays of bytes. The arrays have to be of
+ * same length. No length checking is performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return x1 XOR x2
+ */
+ public static byte[] xor(byte[] x1, byte[] x2)
+ {
+ byte[] out = new byte[x1.length];
+
+ for (int i = x1.length - 1; i >= 0; i--)
+ {
+ out[i] = (byte)(x1[i] ^ x2[i]);
+ }
+ return out;
+ }
+
+ /**
+ * Concatenate two byte arrays. No null checks are performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return (x2||x1) (little-endian order, i.e. x1 is at lower memory
+ * addresses)
+ */
+ public static byte[] concatenate(byte[] x1, byte[] x2)
+ {
+ byte[] result = new byte[x1.length + x2.length];
+
+ System.arraycopy(x1, 0, result, 0, x1.length);
+ System.arraycopy(x2, 0, result, x1.length, x2.length);
+
+ return result;
+ }
+
+ /**
+ * Convert a 2-dimensional byte array into a 1-dimensional byte array by
+ * concatenating all entries.
+ *
+ * @param array a 2-dimensional byte array
+ * @return the concatenated input array
+ */
+ public static byte[] concatenate(byte[][] array)
+ {
+ int rowLength = array[0].length;
+ byte[] result = new byte[array.length * rowLength];
+ int index = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ System.arraycopy(array[i], 0, result, index, rowLength);
+ index += rowLength;
+ }
+ return result;
+ }
+
+ /**
+ * Split a byte array input into two arrays at index,
+ * i.e. the first array will have the lower index bytes, the
+ * second one the higher input.length - index bytes.
+ *
+ * @param input the byte array to be split
+ * @param index the index where the byte array is split
+ * @return the splitted input array as an array of two byte arrays
+ * @throws ArrayIndexOutOfBoundsException if index is out of bounds
+ */
+ public static byte[][] split(byte[] input, int index)
+ throws ArrayIndexOutOfBoundsException
+ {
+ if (index > input.length)
+ {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ byte[][] result = new byte[2][];
+ result[0] = new byte[index];
+ result[1] = new byte[input.length - index];
+ System.arraycopy(input, 0, result[0], 0, index);
+ System.arraycopy(input, index, result[1], 0, input.length - index);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @param end the end index
+ * @return a subarray of input, ranging from start
+ * (inclusively) to end (exclusively)
+ */
+ public static byte[] subArray(byte[] input, int start, int end)
+ {
+ byte[] result = new byte[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @return a subarray of input, ranging from start to
+ * the end of the array
+ */
+ public static byte[] subArray(byte[] input, int start)
+ {
+ return subArray(input, start, input.length);
+ }
+
+ /**
+ * Rewrite a byte array as a char array
+ *
+ * @param input -
+ * the byte array
+ * @return char array
+ */
+ public static char[] toCharArray(byte[] input)
+ {
+ char[] result = new char[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = (char)input[i];
+ }
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java
new file mode 100644
index 000000000..44f977169
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java
@@ -0,0 +1,98 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+public final class CharUtils
+{
+
+ /**
+ * Default constructor (private)
+ */
+ private CharUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Return a clone of the given char array. No null checks are performed.
+ *
+ * @param array the array to clone
+ * @return the clone of the given array
+ */
+ public static char[] clone(char[] array)
+ {
+ char[] result = new char[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert the given char array into a byte array.
+ *
+ * @param chars the char array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(char[] chars)
+ {
+ byte[] result = new byte[chars.length];
+ for (int i = chars.length - 1; i >= 0; i--)
+ {
+ result[i] = (byte)chars[i];
+ }
+ return result;
+ }
+
+ /**
+ * Convert the given char array into a
+ * byte array for use with PBE encryption.
+ *
+ * @param chars the char array
+ * @return the converted array
+ */
+ public static byte[] toByteArrayForPBE(char[] chars)
+ {
+
+ byte[] out = new byte[chars.length];
+
+ for (int i = 0; i < chars.length; i++)
+ {
+ out[i] = (byte)chars[i];
+ }
+
+ int length = out.length * 2;
+ byte[] ret = new byte[length + 2];
+
+ int j = 0;
+ for (int i = 0; i < out.length; i++)
+ {
+ j = i * 2;
+ ret[j] = 0;
+ ret[j + 1] = out[i];
+ }
+
+ ret[length] = 0;
+ ret[length + 1] = 0;
+
+ return ret;
+ }
+
+ /**
+ * Compare two char arrays. No null checks are performed.
+ *
+ * @param left the char byte array
+ * @param right the second char array
+ * @return the result of the comparison
+ */
+ public static boolean equals(char[] left, char[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java
new file mode 100644
index 000000000..7e2de19e5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java
@@ -0,0 +1,1323 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes some operations with matrices over finite field GF(2)
+ * and is used in ecc and MQ-PKC (also has some specific methods and
+ * implementation)
+ */
+public class GF2Matrix
+ extends Matrix
+{
+
+ /**
+ * For the matrix representation the array of type int[][] is used, thus one
+ * element of the array keeps 32 elements of the matrix (from one row and 32
+ * columns)
+ */
+ private int[][] matrix;
+
+ /**
+ * the length of each array representing a row of this matrix, computed as
+ * (numColumns + 31) / 32
+ */
+ private int length;
+
+ /**
+ * Create the matrix from encoded form.
+ *
+ * @param enc the encoded matrix
+ */
+ public GF2Matrix(byte[] enc)
+ {
+ if (enc.length < 9)
+ {
+ throw new ArithmeticException(
+ "given array is not an encoded matrix over GF(2)");
+ }
+
+ numRows = LittleEndianConversions.OS2IP(enc, 0);
+ numColumns = LittleEndianConversions.OS2IP(enc, 4);
+
+ int n = ((numColumns + 7) >>> 3) * numRows;
+
+ if ((numRows <= 0) || (n != (enc.length - 8)))
+ {
+ throw new ArithmeticException(
+ "given array is not an encoded matrix over GF(2)");
+ }
+
+ length = (numColumns + 31) >>> 5;
+ matrix = new int[numRows][length];
+
+ // number of "full" integer
+ int q = numColumns >> 5;
+ // number of bits in non-full integer
+ int r = numColumns & 0x1f;
+
+ int count = 8;
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < q; j++, count += 4)
+ {
+ matrix[i][j] = LittleEndianConversions.OS2IP(enc, count);
+ }
+ for (int j = 0; j < r; j += 8)
+ {
+ matrix[i][q] ^= (enc[count++] & 0xff) << j;
+ }
+ }
+ }
+
+ /**
+ * Create the matrix with the contents of the given array. The matrix is not
+ * copied. Unused coefficients are masked out.
+ *
+ * @param numColumns the number of columns
+ * @param matrix the element array
+ */
+ public GF2Matrix(int numColumns, int[][] matrix)
+ {
+ if (matrix[0].length != (numColumns + 31) >> 5)
+ {
+ throw new ArithmeticException(
+ "Int array does not match given number of columns.");
+ }
+ this.numColumns = numColumns;
+ numRows = matrix.length;
+ length = matrix[0].length;
+ int rest = numColumns & 0x1f;
+ int bitMask;
+ if (rest == 0)
+ {
+ bitMask = 0xffffffff;
+ }
+ else
+ {
+ bitMask = (1 << rest) - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ matrix[i][length - 1] &= bitMask;
+ }
+ this.matrix = matrix;
+ }
+
+ /**
+ * Create an nxn matrix of the given type.
+ *
+ * @param n the number of rows (and columns)
+ * @param typeOfMatrix the martix type (see {@link Matrix} for predefined
+ * constants)
+ */
+ public GF2Matrix(int n, char typeOfMatrix)
+ {
+ this(n, typeOfMatrix, new java.security.SecureRandom());
+ }
+
+ /**
+ * Create an nxn matrix of the given type.
+ *
+ * @param n the matrix size
+ * @param typeOfMatrix the matrix type
+ * @param sr the source of randomness
+ */
+ public GF2Matrix(int n, char typeOfMatrix, SecureRandom sr)
+ {
+ if (n <= 0)
+ {
+ throw new ArithmeticException("Size of matrix is non-positive.");
+ }
+
+ switch (typeOfMatrix)
+ {
+
+ case Matrix.MATRIX_TYPE_ZERO:
+ assignZeroMatrix(n, n);
+ break;
+
+ case Matrix.MATRIX_TYPE_UNIT:
+ assignUnitMatrix(n);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_LT:
+ assignRandomLowerTriangularMatrix(n, sr);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_UT:
+ assignRandomUpperTriangularMatrix(n, sr);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_REGULAR:
+ assignRandomRegularMatrix(n, sr);
+ break;
+
+ default:
+ throw new ArithmeticException("Unknown matrix type.");
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param a another {@link GF2Matrix}
+ */
+ public GF2Matrix(GF2Matrix a)
+ {
+ numColumns = a.getNumColumns();
+ numRows = a.getNumRows();
+ length = a.length;
+ matrix = new int[a.matrix.length][];
+ for (int i = 0; i < matrix.length; i++)
+ {
+ matrix[i] = IntUtils.clone(a.matrix[i]);
+ }
+
+ }
+
+ /**
+ * create the mxn zero matrix
+ */
+ private GF2Matrix(int m, int n)
+ {
+ if ((n <= 0) || (m <= 0))
+ {
+ throw new ArithmeticException("size of matrix is non-positive");
+ }
+
+ assignZeroMatrix(m, n);
+ }
+
+ /**
+ * Create the mxn zero matrix.
+ *
+ * @param m number of rows
+ * @param n number of columns
+ */
+ private void assignZeroMatrix(int m, int n)
+ {
+ numRows = m;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ }
+ }
+
+ /**
+ * Create the mxn unit matrix.
+ *
+ * @param n number of rows (and columns)
+ */
+ private void assignUnitMatrix(int n)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int rest = i & 0x1f;
+ matrix[i][i >>> 5] = 1 << rest;
+ }
+ }
+
+ /**
+ * Create a nxn random lower triangular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomLowerTriangularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int s = 31 - r;
+ r = 1 << r;
+ for (int j = 0; j < q; j++)
+ {
+ matrix[i][j] = sr.nextInt();
+ }
+ matrix[i][q] = (sr.nextInt() >>> s) | r;
+ for (int j = q + 1; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+
+ }
+
+ }
+
+ /**
+ * Create a nxn random upper triangular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomUpperTriangularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ int rest = n & 0x1f;
+ int help;
+ if (rest == 0)
+ {
+ help = 0xffffffff;
+ }
+ else
+ {
+ help = (1 << rest) - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int s = r;
+ r = 1 << r;
+ for (int j = 0; j < q; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ matrix[i][q] = (sr.nextInt() << s) | r;
+ for (int j = q + 1; j < length; j++)
+ {
+ matrix[i][j] = sr.nextInt();
+ }
+ matrix[i][length - 1] &= help;
+ }
+
+ }
+
+ /**
+ * Create an nxn random regular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomRegularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr);
+ GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr);
+ GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um);
+ Permutation perm = new Permutation(n, sr);
+ int[] p = perm.getVector();
+ for (int i = 0; i < n; i++)
+ {
+ System.arraycopy(rm.matrix[i], 0, matrix[p[i]], 0, length);
+ }
+ }
+
+ /**
+ * Create a nxn random regular matrix and its inverse.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ * @return the created random regular matrix and its inverse
+ */
+ public static GF2Matrix[] createRandomRegularMatrixAndItsInverse(int n,
+ SecureRandom sr)
+ {
+
+ GF2Matrix[] result = new GF2Matrix[2];
+
+ // ------------------------------------
+ // First part: create regular matrix
+ // ------------------------------------
+
+ // ------
+ int length = (n + 31) >> 5;
+ GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr);
+ GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr);
+ GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um);
+ Permutation p = new Permutation(n, sr);
+ int[] pVec = p.getVector();
+
+ int[][] matrix = new int[n][length];
+ for (int i = 0; i < n; i++)
+ {
+ System.arraycopy(rm.matrix[pVec[i]], 0, matrix[i], 0, length);
+ }
+
+ result[0] = new GF2Matrix(n, matrix);
+
+ // ------------------------------------
+ // Second part: create inverse matrix
+ // ------------------------------------
+
+ // inverse to lm
+ GF2Matrix invLm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT);
+ for (int i = 0; i < n; i++)
+ {
+ int rest = i & 0x1f;
+ int q = i >>> 5;
+ int r = 1 << rest;
+ for (int j = i + 1; j < n; j++)
+ {
+ int b = (lm.matrix[j][q]) & r;
+ if (b != 0)
+ {
+ for (int k = 0; k <= q; k++)
+ {
+ invLm.matrix[j][k] ^= invLm.matrix[i][k];
+ }
+ }
+ }
+ }
+ // inverse to um
+ GF2Matrix invUm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT);
+ for (int i = n - 1; i >= 0; i--)
+ {
+ int rest = i & 0x1f;
+ int q = i >>> 5;
+ int r = 1 << rest;
+ for (int j = i - 1; j >= 0; j--)
+ {
+ int b = (um.matrix[j][q]) & r;
+ if (b != 0)
+ {
+ for (int k = q; k < length; k++)
+ {
+ invUm.matrix[j][k] ^= invUm.matrix[i][k];
+ }
+ }
+ }
+ }
+
+ // inverse matrix
+ result[1] = (GF2Matrix)invUm.rightMultiply(invLm.rightMultiply(p));
+
+ return result;
+ }
+
+ /**
+ * @return the array keeping the matrix elements
+ */
+ public int[][] getIntArray()
+ {
+ return matrix;
+ }
+
+ /**
+ * @return the length of each array representing a row of this matrix
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Return the row of this matrix with the given index.
+ *
+ * @param index the index
+ * @return the row of this matrix with the given index
+ */
+ public int[] getRow(int index)
+ {
+ return matrix[index];
+ }
+
+ /**
+ * Returns encoded matrix, i.e., this matrix in byte array form
+ *
+ * @return the encoded matrix
+ */
+ public byte[] getEncoded()
+ {
+ int n = (numColumns + 7) >>> 3;
+ n *= numRows;
+ n += 8;
+ byte[] enc = new byte[n];
+
+ LittleEndianConversions.I2OSP(numRows, enc, 0);
+ LittleEndianConversions.I2OSP(numColumns, enc, 4);
+
+ // number of "full" integer
+ int q = numColumns >>> 5;
+ // number of bits in non-full integer
+ int r = numColumns & 0x1f;
+
+ int count = 8;
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < q; j++, count += 4)
+ {
+ LittleEndianConversions.I2OSP(matrix[i][j], enc, count);
+ }
+ for (int j = 0; j < r; j += 8)
+ {
+ enc[count++] = (byte)((matrix[i][q] >>> j) & 0xff);
+ }
+
+ }
+ return enc;
+ }
+
+
+ /**
+ * Returns the percentage of the number of "ones" in this matrix.
+ *
+ * @return the Hamming weight of this matrix (as a ratio).
+ */
+ public double getHammingWeight()
+ {
+ double counter = 0.0;
+ double elementCounter = 0.0;
+ int rest = numColumns & 0x1f;
+ int d;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+
+ for (int i = 0; i < numRows; i++)
+ {
+
+ for (int j = 0; j < d; j++)
+ {
+ int a = matrix[i][j];
+ for (int k = 0; k < 32; k++)
+ {
+ int b = (a >>> k) & 1;
+ counter = counter + b;
+ elementCounter = elementCounter + 1;
+ }
+ }
+ int a = matrix[i][length - 1];
+ for (int k = 0; k < rest; k++)
+ {
+ int b = (a >>> k) & 1;
+ counter = counter + b;
+ elementCounter = elementCounter + 1;
+ }
+ }
+
+ return counter / elementCounter;
+ }
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return true if this is the zero matrix
+ */
+ public boolean isZero()
+ {
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ if (matrix[i][j] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the quadratic submatrix of this matrix consisting of the leftmost
+ * numRows columns.
+ *
+ * @return the (numRows x numRows) submatrix
+ */
+ public GF2Matrix getLeftSubMatrix()
+ {
+ if (numColumns <= numRows)
+ {
+ throw new ArithmeticException("empty submatrix");
+ }
+ int length = (numRows + 31) >> 5;
+ int[][] result = new int[numRows][length];
+ int bitMask = (1 << (numRows & 0x1f)) - 1;
+ if (bitMask == 0)
+ {
+ bitMask = -1;
+ }
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ System.arraycopy(matrix[i], 0, result[i], 0, length);
+ result[i][length - 1] &= bitMask;
+ }
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * Compute the full form matrix (this | Id) from this matrix in
+ * left compact form, where Id is the k x k identity
+ * matrix and k is the number of rows of this matrix.
+ *
+ * @return (this | Id)
+ */
+ public GF2Matrix extendLeftCompactForm()
+ {
+ int newNumColumns = numColumns + numRows;
+ GF2Matrix result = new GF2Matrix(numRows, newNumColumns);
+
+ int ind = numRows - 1 + numColumns;
+ for (int i = numRows - 1; i >= 0; i--, ind--)
+ {
+ // copy this matrix to first columns
+ System.arraycopy(matrix[i], 0, result.matrix[i], 0, length);
+ // store the identity in last columns
+ result.matrix[i][ind >> 5] |= 1 << (ind & 0x1f);
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the submatrix of this matrix consisting of the rightmost
+ * numColumns-numRows columns.
+ *
+ * @return the (numRows x (numColumns-numRows)) submatrix
+ */
+ public GF2Matrix getRightSubMatrix()
+ {
+ if (numColumns <= numRows)
+ {
+ throw new ArithmeticException("empty submatrix");
+ }
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ GF2Matrix result = new GF2Matrix(numRows, numColumns - numRows);
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int ind = q;
+ // process all but last word
+ for (int j = 0; j < result.length - 1; j++)
+ {
+ // shift to correct position
+ result.matrix[i][j] = (matrix[i][ind++] >>> r)
+ | (matrix[i][ind] << (32 - r));
+ }
+ // process last word
+ result.matrix[i][result.length - 1] = matrix[i][ind++] >>> r;
+ if (ind < length)
+ {
+ result.matrix[i][result.length - 1] |= matrix[i][ind] << (32 - r);
+ }
+ }
+ else
+ {
+ // no shifting necessary
+ System.arraycopy(matrix[i], q, result.matrix[i], 0,
+ result.length);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the full form matrix (Id | this) from this matrix in
+ * right compact form, where Id is the k x k identity
+ * matrix and k is the number of rows of this matrix.
+ *
+ * @return (Id | this)
+ */
+ public GF2Matrix extendRightCompactForm()
+ {
+ GF2Matrix result = new GF2Matrix(numRows, numRows + numColumns);
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ // store the identity in first columns
+ result.matrix[i][i >> 5] |= 1 << (i & 0x1f);
+
+ // copy this matrix to last columns
+
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int ind = q;
+ // process all but last word
+ for (int j = 0; j < length - 1; j++)
+ {
+ // obtain matrix word
+ int mw = matrix[i][j];
+ // shift to correct position
+ result.matrix[i][ind++] |= mw << r;
+ result.matrix[i][ind] |= mw >>> (32 - r);
+ }
+ // process last word
+ int mw = matrix[i][length - 1];
+ result.matrix[i][ind++] |= mw << r;
+ if (ind < result.length)
+ {
+ result.matrix[i][ind] |= mw >>> (32 - r);
+ }
+ }
+ else
+ {
+ // no shifting necessary
+ System.arraycopy(matrix[i], 0, result.matrix[i], q, length);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the transpose of this matrix.
+ *
+ * @return (this)T
+ */
+ public Matrix computeTranspose()
+ {
+ int[][] result = new int[numColumns][(numRows + 31) >>> 5];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numColumns; j++)
+ {
+ int qs = j >>> 5;
+ int rs = j & 0x1f;
+ int b = (matrix[i][qs] >>> rs) & 1;
+ int qt = i >>> 5;
+ int rt = i & 0x1f;
+ if (b == 1)
+ {
+ result[j][qt] |= 1 << rt;
+ }
+ }
+ }
+
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ * @throws ArithmeticException if this matrix is not invertible.
+ */
+ public Matrix computeInverse()
+ {
+ if (numRows != numColumns)
+ {
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+
+ // clone this matrix
+ int[][] tmpMatrix = new int[numRows][length];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = IntUtils.clone(matrix[i]);
+ }
+
+ // initialize inverse matrix as unit matrix
+ int[][] invMatrix = new int[numRows][length];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ int q = i >> 5;
+ int r = i & 0x1f;
+ invMatrix[i][q] = 1 << r;
+ }
+
+ // simultaneously compute Gaussian reduction of tmpMatrix and unit
+ // matrix
+ for (int i = 0; i < numRows; i++)
+ {
+ // i = q * 32 + (i mod 32)
+ int q = i >> 5;
+ int bitMask = 1 << (i & 0x1f);
+ // if diagonal element is zero
+ if ((tmpMatrix[i][q] & bitMask) == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same column
+ for (int j = i + 1; j < numRows; j++)
+ {
+ if ((tmpMatrix[j][q] & bitMask) != 0)
+ {
+ // found it, swap rows ...
+ foundNonZero = true;
+ swapRows(tmpMatrix, i, j);
+ swapRows(invMatrix, i, j);
+ // ... and quit searching
+ j = numRows;
+ continue;
+ }
+ }
+ // if no non-zero element was found ...
+ if (!foundNonZero)
+ {
+ // ... the matrix is not invertible
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+ }
+
+ // normalize all but i-th row
+ for (int j = numRows - 1; j >= 0; j--)
+ {
+ if ((j != i) && ((tmpMatrix[j][q] & bitMask) != 0))
+ {
+ addToRow(tmpMatrix[i], tmpMatrix[j], q);
+ addToRow(invMatrix[i], invMatrix[j], 0);
+ }
+ }
+ }
+
+ return new GF2Matrix(numColumns, invMatrix);
+ }
+
+ /**
+ * Compute the product of a permutation matrix (which is generated from an
+ * n-permutation) and this matrix.
+ *
+ * @param p the permutation
+ * @return {@link GF2Matrix} P*this
+ */
+ public Matrix leftMultiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (pVec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[][] result = new int[numRows][];
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ result[i] = IntUtils.clone(matrix[pVec[i]]);
+ }
+
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * compute product a row vector and this matrix
+ *
+ * @param vec a vector over GF(2)
+ * @return Vector product a*matrix
+ */
+ public Vector leftMultiply(Vector vec)
+ {
+
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[length];
+
+ int q = numRows >> 5;
+ int r = 1 << (numRows & 0x1f);
+
+ // compute scalar products with full words of vector
+ int row = 0;
+ for (int i = 0; i < q; i++)
+ {
+ int bitMask = 1;
+ do
+ {
+ int b = v[i] & bitMask;
+ if (b != 0)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ }
+ row++;
+ bitMask <<= 1;
+ }
+ while (bitMask != 0);
+ }
+
+ // compute scalar products with last word of vector
+ int bitMask = 1;
+ while (bitMask != r)
+ {
+ int b = v[q] & bitMask;
+ if (b != 0)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ }
+ row++;
+ bitMask <<= 1;
+ }
+
+ return new GF2Vector(res, numColumns);
+ }
+
+ /**
+ * Compute the product of the matrix (this | Id) and a column
+ * vector, where Id is a (numRows x numRows) unit
+ * matrix.
+ *
+ * @param vec the vector over GF(2)
+ * @return (this | Id)*vector
+ */
+ public Vector leftMultiplyLeftCompactForm(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + numColumns + 31) >>> 5];
+
+ // process full words of vector
+ int words = numRows >>> 5;
+ int row = 0;
+ for (int i = 0; i < words; i++)
+ {
+ int bitMask = 1;
+ do
+ {
+ int b = v[i] & bitMask;
+ if (b != 0)
+ {
+ // compute scalar product part
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ // set last bit
+ int q = (numColumns + row) >>> 5;
+ int r = (numColumns + row) & 0x1f;
+ res[q] |= 1 << r;
+ }
+ row++;
+ bitMask <<= 1;
+ }
+ while (bitMask != 0);
+ }
+
+ // process last word of vector
+ int rem = 1 << (numRows & 0x1f);
+ int bitMask = 1;
+ while (bitMask != rem)
+ {
+ int b = v[words] & bitMask;
+ if (b != 0)
+ {
+ // compute scalar product part
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ // set last bit
+ int q = (numColumns + row) >>> 5;
+ int r = (numColumns + row) & 0x1f;
+ res[q] |= 1 << r;
+ }
+ row++;
+ bitMask <<= 1;
+ }
+
+ return new GF2Vector(res, numRows + numColumns);
+ }
+
+ /**
+ * Compute the product of this matrix and a matrix A over GF(2).
+ *
+ * @param mat a matrix A over GF(2)
+ * @return matrix product this*matrixA
+ */
+ public Matrix rightMultiply(Matrix mat)
+ {
+ if (!(mat instanceof GF2Matrix))
+ {
+ throw new ArithmeticException("matrix is not defined over GF(2)");
+ }
+
+ if (mat.numRows != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Matrix a = (GF2Matrix)mat;
+ GF2Matrix result = new GF2Matrix(numRows, mat.numColumns);
+
+ int d;
+ int rest = numColumns & 0x1f;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int count = 0;
+ for (int j = 0; j < d; j++)
+ {
+ int e = matrix[i][j];
+ for (int h = 0; h < 32; h++)
+ {
+ int b = e & (1 << h);
+ if (b != 0)
+ {
+ for (int g = 0; g < a.length; g++)
+ {
+ result.matrix[i][g] ^= a.matrix[count][g];
+ }
+ }
+ count++;
+ }
+ }
+ int e = matrix[i][length - 1];
+ for (int h = 0; h < rest; h++)
+ {
+ int b = e & (1 << h);
+ if (b != 0)
+ {
+ for (int g = 0; g < a.length; g++)
+ {
+ result.matrix[i][g] ^= a.matrix[count][g];
+ }
+ }
+ count++;
+ }
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this matrix and a permutation matrix which is
+ * generated from an n-permutation.
+ *
+ * @param p the permutation
+ * @return {@link GF2Matrix} this*P
+ */
+ public Matrix rightMultiply(Permutation p)
+ {
+
+ int[] pVec = p.getVector();
+ if (pVec.length != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Matrix result = new GF2Matrix(numRows, numColumns);
+
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int pq = pVec[i] >>> 5;
+ int pr = pVec[i] & 0x1f;
+ for (int j = numRows - 1; j >= 0; j--)
+ {
+ result.matrix[j][q] |= ((matrix[j][pq] >>> pr) & 1) << r;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this matrix and the given column vector.
+ *
+ * @param vec the vector over GF(2)
+ * @return this*vector
+ */
+ public Vector rightMultiply(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + 31) >>> 5];
+
+ for (int i = 0; i < numRows; i++)
+ {
+ // compute full word scalar products
+ int help = 0;
+ for (int j = 0; j < length; j++)
+ {
+ help ^= matrix[i][j] & v[j];
+ }
+ // compute single word scalar product
+ int bitValue = 0;
+ for (int j = 0; j < 32; j++)
+ {
+ bitValue ^= (help >>> j) & 1;
+ }
+ // set result bit
+ if (bitValue == 1)
+ {
+ res[i >>> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return new GF2Vector(res, numRows);
+ }
+
+ /**
+ * Compute the product of the matrix (Id | this) and a column
+ * vector, where Id is a (numRows x numRows) unit
+ * matrix.
+ *
+ * @param vec the vector over GF(2)
+ * @return (Id | this)*vector
+ */
+ public Vector rightMultiplyRightCompactForm(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numColumns + numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + 31) >>> 5];
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ // for all rows
+ for (int i = 0; i < numRows; i++)
+ {
+ // get vector bit
+ int help = (v[i >> 5] >>> (i & 0x1f)) & 1;
+
+ // compute full word scalar products
+ int vInd = q;
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int vw = 0;
+ // process all but last word
+ for (int j = 0; j < length - 1; j++)
+ {
+ // shift to correct position
+ vw = (v[vInd++] >>> r) | (v[vInd] << (32 - r));
+ help ^= matrix[i][j] & vw;
+ }
+ // process last word
+ vw = v[vInd++] >>> r;
+ if (vInd < v.length)
+ {
+ vw |= v[vInd] << (32 - r);
+ }
+ help ^= matrix[i][length - 1] & vw;
+ }
+ else
+ {
+ // no shifting necessary
+ for (int j = 0; j < length; j++)
+ {
+ help ^= matrix[i][j] & v[vInd++];
+ }
+ }
+
+ // compute single word scalar product
+ int bitValue = 0;
+ for (int j = 0; j < 32; j++)
+ {
+ bitValue ^= help & 1;
+ help >>>= 1;
+ }
+
+ // set result bit
+ if (bitValue == 1)
+ {
+ res[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return new GF2Vector(res, numRows);
+ }
+
+ /**
+ * Compare this matrix with another object.
+ *
+ * @param other another object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2Matrix))
+ {
+ return false;
+ }
+ GF2Matrix otherMatrix = (GF2Matrix)other;
+
+ if ((numRows != otherMatrix.numRows)
+ || (numColumns != otherMatrix.numColumns)
+ || (length != otherMatrix.length))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < numRows; i++)
+ {
+ if (!IntUtils.equals(matrix[i], otherMatrix.matrix[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the hash code of this matrix
+ */
+ public int hashCode()
+ {
+ int hash = (numRows * 31 + numColumns) * 31 + length;
+ for (int i = 0; i < numRows; i++)
+ {
+ hash = hash * 31 + matrix[i].hashCode();
+ }
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of the matrix
+ */
+ public String toString()
+ {
+ int rest = numColumns & 0x1f;
+ int d;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < numRows; i++)
+ {
+ buf.append(i + ": ");
+ for (int j = 0; j < d; j++)
+ {
+ int a = matrix[i][j];
+ for (int k = 0; k < 32; k++)
+ {
+ int b = (a >>> k) & 1;
+ if (b == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ buf.append(' ');
+ }
+ int a = matrix[i][length - 1];
+ for (int k = 0; k < rest; k++)
+ {
+ int b = (a >>> k) & 1;
+ if (b == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ buf.append('\n');
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Swap two rows of the given matrix.
+ *
+ * @param matrix the matrix
+ * @param first the index of the first row
+ * @param second the index of the second row
+ */
+ private static void swapRows(int[][] matrix, int first, int second)
+ {
+ int[] tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+ /**
+ * Partially add one row to another.
+ *
+ * @param fromRow the addend
+ * @param toRow the row to add to
+ * @param startIndex the array index to start from
+ */
+ private static void addToRow(int[] fromRow, int[] toRow, int startIndex)
+ {
+ for (int i = toRow.length - 1; i >= startIndex; i--)
+ {
+ toRow[i] = fromRow[i] ^ toRow[i];
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java
new file mode 100644
index 000000000..fdef21054
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java
@@ -0,0 +1,2039 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+
+/**
+ * This class stores very long strings of bits and does some basic arithmetics.
+ * It is used by GF2nField, GF2nPolynomialField and
+ * GFnPolynomialElement.
+ *
+ * @see GF2nPolynomialElement
+ * @see GF2nField
+ */
+public class GF2Polynomial
+{
+
+ // number of bits stored in this GF2Polynomial
+ private int len;
+
+ // number of int used in value
+ private int blocks;
+
+ // storage
+ private int[] value;
+
+ // Random source
+ private static Random rand = new Random();
+
+ // Lookup-Table for vectorMult: parity[a]= #1(a) mod 2 == 1
+ private static final boolean[] parity = {false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, true, false, false, true, false, true, true, false, false,
+ true, true, false, true, false, false, true, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, false, true, true, false, true, false, false,
+ true, true, false, false, true, false, true, true, false, true,
+ false, false, true, false, true, true, false, false, true, true,
+ false, true, false, false, true, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, true,
+ false, false, true, false, true, true, false, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, false,
+ true, true, false, true, false, false, true, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, true, false, false, true, false, true, true, false, false,
+ true, true, false, true, false, false, true, false, true, true,
+ false, true, false, false, true, true, false, false, true, false,
+ true, true, false, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, true,
+ false, false, true, false, true, true, false, false, true, true,
+ false, true, false, false, true, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false};
+
+ // Lookup-Table for Squaring: squaringTable[a]=a^2
+ private static final short[] squaringTable = {0x0000, 0x0001, 0x0004,
+ 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044,
+ 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104,
+ 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144,
+ 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404,
+ 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444,
+ 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, 0x0501, 0x0504,
+ 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544,
+ 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001, 0x1004,
+ 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044,
+ 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104,
+ 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144,
+ 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404,
+ 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, 0x1441, 0x1444,
+ 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504,
+ 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 0x1540, 0x1541, 0x1544,
+ 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004,
+ 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044,
+ 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104,
+ 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144,
+ 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401, 0x4404,
+ 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444,
+ 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, 0x4500, 0x4501, 0x4504,
+ 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544,
+ 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004,
+ 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044,
+ 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104,
+ 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, 0x5141, 0x5144,
+ 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404,
+ 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 0x5444,
+ 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504,
+ 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544,
+ 0x5545, 0x5550, 0x5551, 0x5554, 0x5555};
+
+ // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
+ private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
+ 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
+ 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
+ 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
+
+ // pre-computed Bitmask for fast masking, rightMask[a]=0xffffffff >>> (32-a)
+ private static final int[] reverseRightMask = {0x00000000, 0x00000001,
+ 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f,
+ 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+ 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+ 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff,
+ 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff,
+ 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+ 0xffffffff};
+
+ /**
+ * Creates a new GF2Polynomial of the given length and value zero.
+ *
+ * @param length the desired number of bits to store
+ */
+ public GF2Polynomial(int length)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given length and random value.
+ *
+ * @param length the desired number of bits to store
+ * @param rand SecureRandom to use for randomization
+ */
+ public GF2Polynomial(int length, Random rand)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ randomize(rand);
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given length and value
+ * selected by value:
+ *
+ *
+ *
+ * @param length the desired number of bits to store
+ * @param value the value described by a String
+ */
+ public GF2Polynomial(int length, String value)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ this.value = new int[blocks];
+ len = l;
+ if (value.equalsIgnoreCase("ZERO"))
+ {
+ assignZero();
+ }
+ else if (value.equalsIgnoreCase("ONE"))
+ {
+ assignOne();
+ }
+ else if (value.equalsIgnoreCase("RANDOM"))
+ {
+ randomize();
+ }
+ else if (value.equalsIgnoreCase("X"))
+ {
+ assignX();
+ }
+ else if (value.equalsIgnoreCase("ALL"))
+ {
+ assignAll();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Error: GF2Polynomial was called using " + value
+ + " as value!");
+ }
+
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given length using the given
+ * int[]. LSB is contained in bs[0].
+ *
+ * @param length the desired number of bits to store
+ * @param bs contains the desired value, LSB in bs[0]
+ */
+ public GF2Polynomial(int length, int[] bs)
+ {
+ int leng = length;
+ if (leng < 1)
+ {
+ leng = 1;
+ }
+ blocks = ((leng - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = leng;
+ int l = Math.min(blocks, bs.length);
+ System.arraycopy(bs, 0, value, 0, l);
+ zeroUnusedBits();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by converting the given byte[] os
+ * according to 1363 and using the given length.
+ *
+ * @param length the intended length of this polynomial
+ * @param os the octet string to assign to this polynomial
+ * @see "P1363 5.5.2 p22f, OS2BSP"
+ */
+ public GF2Polynomial(int length, byte[] os)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ int i, m;
+ int k = Math.min(((os.length - 1) >> 2) + 1, blocks);
+ for (i = 0; i < k - 1; i++)
+ {
+ m = os.length - (i << 2) - 1;
+ value[i] = (os[m]) & 0x000000ff;
+ value[i] |= (os[m - 1] << 8) & 0x0000ff00;
+ value[i] |= (os[m - 2] << 16) & 0x00ff0000;
+ value[i] |= (os[m - 3] << 24) & 0xff000000;
+ }
+ i = k - 1;
+ m = os.length - (i << 2) - 1;
+ value[i] = os[m] & 0x000000ff;
+ if (m > 0)
+ {
+ value[i] |= (os[m - 1] << 8) & 0x0000ff00;
+ }
+ if (m > 1)
+ {
+ value[i] |= (os[m - 2] << 16) & 0x00ff0000;
+ }
+ if (m > 2)
+ {
+ value[i] |= (os[m - 3] << 24) & 0xff000000;
+ }
+ zeroUnusedBits();
+ reduceN();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by converting the given FlexiBigInt bi
+ * according to 1363 and using the given length.
+ *
+ * @param length the intended length of this polynomial
+ * @param bi the FlexiBigInt to assign to this polynomial
+ * @see "P1363 5.5.1 p22, I2BSP"
+ */
+ public GF2Polynomial(int length, BigInteger bi)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ int i;
+ byte[] val = bi.toByteArray();
+ if (val[0] == 0)
+ {
+ byte[] dummy = new byte[val.length - 1];
+ System.arraycopy(val, 1, dummy, 0, dummy.length);
+ val = dummy;
+ }
+ int ov = val.length & 0x03;
+ int k = ((val.length - 1) >> 2) + 1;
+ for (i = 0; i < ov; i++)
+ {
+ value[k - 1] |= (val[i] & 0x000000ff) << ((ov - 1 - i) << 3);
+ }
+ int m = 0;
+ for (i = 0; i <= (val.length - 4) >> 2; i++)
+ {
+ m = val.length - 1 - (i << 2);
+ value[i] = (val[m]) & 0x000000ff;
+ value[i] |= ((val[m - 1]) << 8) & 0x0000ff00;
+ value[i] |= ((val[m - 2]) << 16) & 0x00ff0000;
+ value[i] |= ((val[m - 3]) << 24) & 0xff000000;
+ }
+ if ((len & 0x1f) != 0)
+ {
+ value[blocks - 1] &= reverseRightMask[len & 0x1f];
+ }
+ reduceN();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by cloneing the given GF2Polynomial b.
+ *
+ * @param b the GF2Polynomial to clone
+ */
+ public GF2Polynomial(GF2Polynomial b)
+ {
+ len = b.len;
+ blocks = b.blocks;
+ value = IntUtils.clone(b.value);
+ }
+
+ /**
+ * @return a copy of this GF2Polynomial
+ */
+ public Object clone()
+ {
+ return new GF2Polynomial(this);
+ }
+
+ /**
+ * Returns the length of this GF2Polynomial. The length can be greater than
+ * the degree. To get the degree call reduceN() before calling getLength().
+ *
+ * @return the length of this GF2Polynomial
+ */
+ public int getLength()
+ {
+ return len;
+ }
+
+ /**
+ * Returns the value of this GF2Polynomial in an int[].
+ *
+ * @return the value of this GF2Polynomial in a new int[], LSB in int[0]
+ */
+ public int[] toIntegerArray()
+ {
+ int[] result;
+ result = new int[blocks];
+ System.arraycopy(value, 0, result, 0, blocks);
+ return result;
+ }
+
+ /**
+ * Returns a string representing this GF2Polynomials value using hexadecimal
+ * or binary radix in MSB-first order.
+ *
+ * @param radix the radix to use (2 or 16, otherwise 2 is used)
+ * @return a String representing this GF2Polynomials value.
+ */
+ public String toString(int radix)
+ {
+ final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ final String[] BIN_CHARS = {"0000", "0001", "0010", "0011", "0100",
+ "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100",
+ "1101", "1110", "1111"};
+ String res;
+ int i;
+ res = new String();
+ if (radix == 16)
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ res += HEX_CHARS[(value[i] >>> 28) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 24) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 20) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 16) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 12) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 8) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 4) & 0x0f];
+ res += HEX_CHARS[(value[i]) & 0x0f];
+ res += " ";
+ }
+ }
+ else
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ res += BIN_CHARS[(value[i] >>> 28) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 24) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 20) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 16) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 12) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 8) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 4) & 0x0f];
+ res += BIN_CHARS[(value[i]) & 0x0f];
+ res += " ";
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Converts this polynomial to a byte[] (octet string) according to 1363.
+ *
+ * @return a byte[] representing the value of this polynomial
+ * @see "P1363 5.5.2 p22f, BS2OSP"
+ */
+ public byte[] toByteArray()
+ {
+ int k = ((len - 1) >> 3) + 1;
+ int ov = k & 0x03;
+ int m;
+ byte[] res = new byte[k];
+ int i;
+ for (i = 0; i < (k >> 2); i++)
+ {
+ m = k - (i << 2) - 1;
+ res[m] = (byte)((value[i] & 0x000000ff));
+ res[m - 1] = (byte)((value[i] & 0x0000ff00) >>> 8);
+ res[m - 2] = (byte)((value[i] & 0x00ff0000) >>> 16);
+ res[m - 3] = (byte)((value[i] & 0xff000000) >>> 24);
+ }
+ for (i = 0; i < ov; i++)
+ {
+ m = (ov - i - 1) << 3;
+ res[i] = (byte)((value[blocks - 1] & (0x000000ff << m)) >>> m);
+ }
+ return res;
+ }
+
+ /**
+ * Converts this polynomial to an integer according to 1363.
+ *
+ * @return a FlexiBigInt representing the value of this polynomial
+ * @see "P1363 5.5.1 p22, BS2IP"
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ if (len == 0 || isZero())
+ {
+ return new BigInteger(0, new byte[0]);
+ }
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Sets the LSB to 1 and all other to 0, assigning 'one' to this
+ * GF2Polynomial.
+ */
+ public void assignOne()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ value[0] = 0x01;
+ }
+
+ /**
+ * Sets Bit 1 to 1 and all other to 0, assigning 'x' to this GF2Polynomial.
+ */
+ public void assignX()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ value[0] = 0x02;
+ }
+
+ /**
+ * Sets all Bits to 1.
+ */
+ public void assignAll()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = 0xffffffff;
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Resets all bits to zero.
+ */
+ public void assignZero()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ }
+
+ /**
+ * Fills all len bits of this GF2Polynomial with random values.
+ */
+ public void randomize()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = rand.nextInt();
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Fills all len bits of this GF2Polynomial with random values using the
+ * specified source of randomness.
+ *
+ * @param rand the source of randomness
+ */
+ public void randomize(Random rand)
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = rand.nextInt();
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Returns true if two GF2Polynomials have the same size and value and thus
+ * are equal.
+ *
+ * @param other the other GF2Polynomial
+ * @return true if this GF2Polynomial equals b (this ==
+ * b)
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2Polynomial))
+ {
+ return false;
+ }
+
+ GF2Polynomial otherPol = (GF2Polynomial)other;
+
+ if (len != otherPol.len)
+ {
+ return false;
+ }
+ for (int i = 0; i < blocks; i++)
+ {
+ if (value[i] != otherPol.value[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ return len + value.hashCode();
+ }
+
+ /**
+ * Tests if all bits equal zero.
+ *
+ * @return true if this GF2Polynomial equals 'zero' (this == 0)
+ */
+ public boolean isZero()
+ {
+ int i;
+ if (len == 0)
+ {
+ return true;
+ }
+ for (i = 0; i < blocks; i++)
+ {
+ if (value[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if all bits are reset to 0 and LSB is set to 1.
+ *
+ * @return true if this GF2Polynomial equals 'one' (this == 1)
+ */
+ public boolean isOne()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ if (value[i] != 0)
+ {
+ return false;
+ }
+ }
+ if (value[0] != 0x01)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Adds b to this GF2Polynomial and assigns the result to this
+ * GF2Polynomial. b can be of different size.
+ *
+ * @param b GF2Polynomial to add to this GF2Polynomial
+ */
+ public void addToThis(GF2Polynomial b)
+ {
+ expandN(b.len);
+ xorThisBy(b);
+ }
+
+ /**
+ * Adds two GF2Polynomials, this and b, and returns the
+ * result. this and b can be of different size.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (this + b)
+ */
+ public GF2Polynomial add(GF2Polynomial b)
+ {
+ return xor(b);
+ }
+
+ /**
+ * Subtracts b from this GF2Polynomial and assigns the result to
+ * this GF2Polynomial. b can be of different size.
+ *
+ * @param b a GF2Polynomial
+ */
+ public void subtractFromThis(GF2Polynomial b)
+ {
+ expandN(b.len);
+ xorThisBy(b);
+ }
+
+ /**
+ * Subtracts two GF2Polynomials, this and b, and returns the
+ * result in a new GF2Polynomial. this and b can be of
+ * different size.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (this - b)
+ */
+ public GF2Polynomial subtract(GF2Polynomial b)
+ {
+ return xor(b);
+ }
+
+ /**
+ * Toggles the LSB of this GF2Polynomial, increasing its value by 'one'.
+ */
+ public void increaseThis()
+ {
+ xorBit(0);
+ }
+
+ /**
+ * Toggles the LSB of this GF2Polynomial, increasing the value by 'one' and
+ * returns the result in a new GF2Polynomial.
+ *
+ * @return this + 1
+ */
+ public GF2Polynomial increase()
+ {
+ GF2Polynomial result = new GF2Polynomial(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * Multiplies this GF2Polynomial with b and returns the result in a
+ * new GF2Polynomial. This method does not reduce the result in GF(2^N).
+ * This method uses classic multiplication (schoolbook).
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (this * b)
+ */
+ public GF2Polynomial multiplyClassic(GF2Polynomial b)
+ {
+ GF2Polynomial result = new GF2Polynomial(Math.max(len, b.len) << 1);
+ GF2Polynomial[] m = new GF2Polynomial[32];
+ int i, j;
+ m[0] = new GF2Polynomial(this);
+ for (i = 1; i <= 31; i++)
+ {
+ m[i] = m[i - 1].shiftLeft();
+ }
+ for (i = 0; i < b.blocks; i++)
+ {
+ for (j = 0; j <= 31; j++)
+ {
+ if ((b.value[i] & bitMask[j]) != 0)
+ {
+ result.xorThisBy(m[j]);
+ }
+ }
+ for (j = 0; j <= 31; j++)
+ {
+ m[j].shiftBlocksLeft();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies this GF2Polynomial with b and returns the result in a
+ * new GF2Polynomial. This method does not reduce the result in GF(2^N).
+ * This method uses Karatzuba multiplication.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (this * b)
+ */
+ public GF2Polynomial multiply(GF2Polynomial b)
+ {
+ int n = Math.max(len, b.len);
+ expandN(n);
+ b.expandN(n);
+ return karaMult(b);
+ }
+
+ /**
+ * Does the recursion for Karatzuba multiplication.
+ */
+ private GF2Polynomial karaMult(GF2Polynomial b)
+ {
+ GF2Polynomial result = new GF2Polynomial(len << 1);
+ if (len <= 32)
+ {
+ result.value = mult32(value[0], b.value[0]);
+ return result;
+ }
+ if (len <= 64)
+ {
+ result.value = mult64(value, b.value);
+ return result;
+ }
+ if (len <= 128)
+ {
+ result.value = mult128(value, b.value);
+ return result;
+ }
+ if (len <= 256)
+ {
+ result.value = mult256(value, b.value);
+ return result;
+ }
+ if (len <= 512)
+ {
+ result.value = mult512(value, b.value);
+ return result;
+ }
+
+ int n = IntegerFunctions.floorLog(len - 1);
+ n = bitMask[n];
+
+ GF2Polynomial a0 = lower(((n - 1) >> 5) + 1);
+ GF2Polynomial a1 = upper(((n - 1) >> 5) + 1);
+ GF2Polynomial b0 = b.lower(((n - 1) >> 5) + 1);
+ GF2Polynomial b1 = b.upper(((n - 1) >> 5) + 1);
+
+ GF2Polynomial c = a1.karaMult(b1); // c = a1*b1
+ GF2Polynomial e = a0.karaMult(b0); // e = a0*b0
+ a0.addToThis(a1); // a0 = a0 + a1
+ b0.addToThis(b1); // b0 = b0 + b1
+ GF2Polynomial d = a0.karaMult(b0); // d = (a0+a1)*(b0+b1)
+
+ result.shiftLeftAddThis(c, n << 1);
+ result.shiftLeftAddThis(c, n);
+ result.shiftLeftAddThis(d, n);
+ result.shiftLeftAddThis(e, n);
+ result.addToThis(e);
+ return result;
+ }
+
+ /**
+ * 16-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult512(int[] a, int[] b)
+ {
+ int[] result = new int[32];
+ int[] a0 = new int[8];
+ System.arraycopy(a, 0, a0, 0, Math.min(8, a.length));
+ int[] a1 = new int[8];
+ if (a.length > 8)
+ {
+ System.arraycopy(a, 8, a1, 0, Math.min(8, a.length - 8));
+ }
+ int[] b0 = new int[8];
+ System.arraycopy(b, 0, b0, 0, Math.min(8, b.length));
+ int[] b1 = new int[8];
+ if (b.length > 8)
+ {
+ System.arraycopy(b, 8, b1, 0, Math.min(8, b.length - 8));
+ }
+ int[] c = mult256(a1, b1);
+ result[31] ^= c[15];
+ result[30] ^= c[14];
+ result[29] ^= c[13];
+ result[28] ^= c[12];
+ result[27] ^= c[11];
+ result[26] ^= c[10];
+ result[25] ^= c[9];
+ result[24] ^= c[8];
+ result[23] ^= c[7] ^ c[15];
+ result[22] ^= c[6] ^ c[14];
+ result[21] ^= c[5] ^ c[13];
+ result[20] ^= c[4] ^ c[12];
+ result[19] ^= c[3] ^ c[11];
+ result[18] ^= c[2] ^ c[10];
+ result[17] ^= c[1] ^ c[9];
+ result[16] ^= c[0] ^ c[8];
+ result[15] ^= c[7];
+ result[14] ^= c[6];
+ result[13] ^= c[5];
+ result[12] ^= c[4];
+ result[11] ^= c[3];
+ result[10] ^= c[2];
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ a1[2] ^= a0[2];
+ a1[3] ^= a0[3];
+ a1[4] ^= a0[4];
+ a1[5] ^= a0[5];
+ a1[6] ^= a0[6];
+ a1[7] ^= a0[7];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ b1[2] ^= b0[2];
+ b1[3] ^= b0[3];
+ b1[4] ^= b0[4];
+ b1[5] ^= b0[5];
+ b1[6] ^= b0[6];
+ b1[7] ^= b0[7];
+ int[] d = mult256(a1, b1);
+ result[23] ^= d[15];
+ result[22] ^= d[14];
+ result[21] ^= d[13];
+ result[20] ^= d[12];
+ result[19] ^= d[11];
+ result[18] ^= d[10];
+ result[17] ^= d[9];
+ result[16] ^= d[8];
+ result[15] ^= d[7];
+ result[14] ^= d[6];
+ result[13] ^= d[5];
+ result[12] ^= d[4];
+ result[11] ^= d[3];
+ result[10] ^= d[2];
+ result[9] ^= d[1];
+ result[8] ^= d[0];
+ int[] e = mult256(a0, b0);
+ result[23] ^= e[15];
+ result[22] ^= e[14];
+ result[21] ^= e[13];
+ result[20] ^= e[12];
+ result[19] ^= e[11];
+ result[18] ^= e[10];
+ result[17] ^= e[9];
+ result[16] ^= e[8];
+ result[15] ^= e[7] ^ e[15];
+ result[14] ^= e[6] ^ e[14];
+ result[13] ^= e[5] ^ e[13];
+ result[12] ^= e[4] ^ e[12];
+ result[11] ^= e[3] ^ e[11];
+ result[10] ^= e[2] ^ e[10];
+ result[9] ^= e[1] ^ e[9];
+ result[8] ^= e[0] ^ e[8];
+ result[7] ^= e[7];
+ result[6] ^= e[6];
+ result[5] ^= e[5];
+ result[4] ^= e[4];
+ result[3] ^= e[3];
+ result[2] ^= e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 8-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult256(int[] a, int[] b)
+ {
+ int[] result = new int[16];
+ int[] a0 = new int[4];
+ System.arraycopy(a, 0, a0, 0, Math.min(4, a.length));
+ int[] a1 = new int[4];
+ if (a.length > 4)
+ {
+ System.arraycopy(a, 4, a1, 0, Math.min(4, a.length - 4));
+ }
+ int[] b0 = new int[4];
+ System.arraycopy(b, 0, b0, 0, Math.min(4, b.length));
+ int[] b1 = new int[4];
+ if (b.length > 4)
+ {
+ System.arraycopy(b, 4, b1, 0, Math.min(4, b.length - 4));
+ }
+ if (a1[3] == 0 && a1[2] == 0 && b1[3] == 0 && b1[2] == 0)
+ {
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ if (a1[0] != 0 || b1[0] != 0)
+ { // [3]=[2]=[1]=0, [0]!=0
+ int[] c = mult32(a1[0], b1[0]);
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ }
+ else
+ { // [3]=[2]=0 [1]!=0, [0]!=0
+ int[] c = mult64(a1, b1);
+ result[11] ^= c[3];
+ result[10] ^= c[2];
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ }
+ else
+ { // [3]!=0 [2]!=0 [1]!=0, [0]!=0
+ int[] c = mult128(a1, b1);
+ result[15] ^= c[7];
+ result[14] ^= c[6];
+ result[13] ^= c[5];
+ result[12] ^= c[4];
+ result[11] ^= c[3] ^ c[7];
+ result[10] ^= c[2] ^ c[6];
+ result[9] ^= c[1] ^ c[5];
+ result[8] ^= c[0] ^ c[4];
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ a1[2] ^= a0[2];
+ a1[3] ^= a0[3];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ b1[2] ^= b0[2];
+ b1[3] ^= b0[3];
+ int[] d = mult128(a1, b1);
+ result[11] ^= d[7];
+ result[10] ^= d[6];
+ result[9] ^= d[5];
+ result[8] ^= d[4];
+ result[7] ^= d[3];
+ result[6] ^= d[2];
+ result[5] ^= d[1];
+ result[4] ^= d[0];
+ int[] e = mult128(a0, b0);
+ result[11] ^= e[7];
+ result[10] ^= e[6];
+ result[9] ^= e[5];
+ result[8] ^= e[4];
+ result[7] ^= e[3] ^ e[7];
+ result[6] ^= e[2] ^ e[6];
+ result[5] ^= e[1] ^ e[5];
+ result[4] ^= e[0] ^ e[4];
+ result[3] ^= e[3];
+ result[2] ^= e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 4-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult128(int[] a, int[] b)
+ {
+ int[] result = new int[8];
+ int[] a0 = new int[2];
+ System.arraycopy(a, 0, a0, 0, Math.min(2, a.length));
+ int[] a1 = new int[2];
+ if (a.length > 2)
+ {
+ System.arraycopy(a, 2, a1, 0, Math.min(2, a.length - 2));
+ }
+ int[] b0 = new int[2];
+ System.arraycopy(b, 0, b0, 0, Math.min(2, b.length));
+ int[] b1 = new int[2];
+ if (b.length > 2)
+ {
+ System.arraycopy(b, 2, b1, 0, Math.min(2, b.length - 2));
+ }
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ if (a1[0] != 0 || b1[0] != 0)
+ {
+ int[] c = mult32(a1[0], b1[0]);
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ result[3] ^= c[1];
+ result[2] ^= c[0];
+ }
+ }
+ else
+ {
+ int[] c = mult64(a1, b1);
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1] ^ c[3];
+ result[4] ^= c[0] ^ c[2];
+ result[3] ^= c[1];
+ result[2] ^= c[0];
+ }
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ int[] d = mult32(a1[0], b1[0]);
+ result[3] ^= d[1];
+ result[2] ^= d[0];
+ }
+ else
+ {
+ int[] d = mult64(a1, b1);
+ result[5] ^= d[3];
+ result[4] ^= d[2];
+ result[3] ^= d[1];
+ result[2] ^= d[0];
+ }
+ if (a0[1] == 0 && b0[1] == 0)
+ {
+ int[] e = mult32(a0[0], b0[0]);
+ result[3] ^= e[1];
+ result[2] ^= e[0];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ }
+ else
+ {
+ int[] e = mult64(a0, b0);
+ result[5] ^= e[3];
+ result[4] ^= e[2];
+ result[3] ^= e[1] ^ e[3];
+ result[2] ^= e[0] ^ e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ }
+ return result;
+ }
+
+ /**
+ * 2-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult64(int[] a, int[] b)
+ {
+ int[] result = new int[4];
+ int a0 = a[0];
+ int a1 = 0;
+ if (a.length > 1)
+ {
+ a1 = a[1];
+ }
+ int b0 = b[0];
+ int b1 = 0;
+ if (b.length > 1)
+ {
+ b1 = b[1];
+ }
+ if (a1 != 0 || b1 != 0)
+ {
+ int[] c = mult32(a1, b1);
+ result[3] ^= c[1];
+ result[2] ^= c[0] ^ c[1];
+ result[1] ^= c[0];
+ }
+ int[] d = mult32(a0 ^ a1, b0 ^ b1);
+ result[2] ^= d[1];
+ result[1] ^= d[0];
+ int[] e = mult32(a0, b0);
+ result[2] ^= e[1];
+ result[1] ^= e[0] ^ e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 4-Byte Version of Karatzuba multiplication. Here the actual work is done.
+ */
+ private static int[] mult32(int a, int b)
+ {
+ int[] result = new int[2];
+ if (a == 0 || b == 0)
+ {
+ return result;
+ }
+ long b2 = b;
+ b2 &= 0x00000000ffffffffL;
+ int i;
+ long h = 0;
+ for (i = 1; i <= 32; i++)
+ {
+ if ((a & bitMask[i - 1]) != 0)
+ {
+ h ^= b2;
+ }
+ b2 <<= 1;
+ }
+ result[1] = (int)(h >>> 32);
+ result[0] = (int)(h & 0x00000000ffffffffL);
+ return result;
+ }
+
+ /**
+ * Returns a new GF2Polynomial containing the upper k bytes of this
+ * GF2Polynomial.
+ *
+ * @param k
+ * @return a new GF2Polynomial containing the upper k bytes of this
+ * GF2Polynomial
+ * @see GF2Polynomial#karaMult
+ */
+ private GF2Polynomial upper(int k)
+ {
+ int j = Math.min(k, blocks - k);
+ GF2Polynomial result = new GF2Polynomial(j << 5);
+ if (blocks >= k)
+ {
+ System.arraycopy(value, k, result.value, 0, j);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a new GF2Polynomial containing the lower k bytes of this
+ * GF2Polynomial.
+ *
+ * @param k
+ * @return a new GF2Polynomial containing the lower k bytes of this
+ * GF2Polynomial
+ * @see GF2Polynomial#karaMult
+ */
+ private GF2Polynomial lower(int k)
+ {
+ GF2Polynomial result = new GF2Polynomial(k << 5);
+ System.arraycopy(value, 0, result.value, 0, Math.min(k, blocks));
+ return result;
+ }
+
+ /**
+ * Returns the remainder of this divided by g in a new
+ * GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial (this % g)
+ * @throws PolynomialIsZeroException if g equals zero
+ */
+ public GF2Polynomial remainder(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ return a;
+ }
+ i = a.len - b.len;
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ i = a.len - b.len;
+ }
+ return a;
+ }
+
+ /**
+ * Returns the absolute quotient of this divided by g in a
+ * new GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial |_ this / g _|
+ * @throws PolynomialIsZeroException if g equals zero
+ */
+ public GF2Polynomial quotient(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial q = new GF2Polynomial(len);
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ return new GF2Polynomial(0);
+ }
+ i = a.len - b.len;
+ q.expandN(i + 1);
+
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ q.xorBit(i);
+ i = a.len - b.len;
+ }
+
+ return q;
+ }
+
+ /**
+ * Divides this by g and returns the quotient and remainder
+ * in a new GF2Polynomial[2], quotient in [0], remainder in [1].
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial[2] containing quotient and remainder
+ * @throws PolynomialIsZeroException if g equals zero
+ */
+ public GF2Polynomial[] divide(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial[] result = new GF2Polynomial[2];
+ GF2Polynomial q = new GF2Polynomial(len);
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ result[0] = new GF2Polynomial(0);
+ result[1] = a;
+ return result;
+ }
+ i = a.len - b.len;
+ q.expandN(i + 1);
+
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ q.xorBit(i);
+ i = a.len - b.len;
+ }
+
+ result[0] = q;
+ result[1] = a;
+ return result;
+ }
+
+ /**
+ * Returns the greatest common divisor of this and g in a
+ * new GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial gcd(this,g)
+ * @throws ArithmeticException if this and g both are equal to zero
+ * @throws PolynomialIsZeroException to be API-compliant (should never be thrown).
+ */
+ public GF2Polynomial gcd(GF2Polynomial g)
+ throws RuntimeException
+ {
+ if (isZero() && g.isZero())
+ {
+ throw new ArithmeticException("Both operands of gcd equal zero.");
+ }
+ if (isZero())
+ {
+ return new GF2Polynomial(g);
+ }
+ if (g.isZero())
+ {
+ return new GF2Polynomial(this);
+ }
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial c;
+
+ while (!b.isZero())
+ {
+ c = a.remainder(b);
+ a = b;
+ b = c;
+ }
+
+ return a;
+ }
+
+ /**
+ * Checks if this is irreducible, according to IEEE P1363, A.5.5,
+ * p103.
+ * Note: The algorithm from IEEE P1363, A5.5 can be used to check a
+ * polynomial with coefficients in GF(2^r) for irreducibility. As this class
+ * only represents polynomials with coefficients in GF(2), the algorithm is
+ * adapted to the case r=1.
+ *
+ * @return true if this is irreducible
+ * @see "P1363, A.5.5, p103"
+ */
+ public boolean isIrreducible()
+ {
+ if (isZero())
+ {
+ return false;
+ }
+ GF2Polynomial f = new GF2Polynomial(this);
+ int d, i;
+ GF2Polynomial u, g;
+ GF2Polynomial dummy;
+ f.reduceN();
+ d = f.len - 1;
+ u = new GF2Polynomial(f.len, "X");
+
+ for (i = 1; i <= (d >> 1); i++)
+ {
+ u.squareThisPreCalc();
+ u = u.remainder(f);
+ dummy = u.add(new GF2Polynomial(32, "X"));
+ if (!dummy.isZero())
+ {
+ g = f.gcd(dummy);
+ if (!g.isOne())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Reduces this GF2Polynomial using the trinomial x^m + x^tc +
+ * 1.
+ *
+ * @param m the degree of the used field
+ * @param tc degree of the middle x in the trinomial
+ */
+ void reduceTrinomial(int m, int tc)
+ {
+ int i;
+ int p0, p1;
+ int q0, q1;
+ long t;
+ p0 = m >>> 5; // block which contains 2^m
+ q0 = 32 - (m & 0x1f); // (32-index) of 2^m within block p0
+ p1 = (m - tc) >>> 5; // block which contains 2^tc
+ q1 = 32 - ((m - tc) & 0x1f); // (32-index) of 2^tc within block q1
+ int max = ((m << 1) - 2) >>> 5; // block which contains 2^(2m-2)
+ int min = p0; // block which contains 2^m
+ for (i = max; i > min; i--)
+ { // for i = maxBlock to minBlock
+ // reduce coefficients contained in t
+ // t = block[i]
+ t = value[i] & 0x00000000ffffffffL;
+ // block[i-p0-1] ^= t << q0
+ value[i - p0 - 1] ^= (int)(t << q0);
+ // block[i-p0] ^= t >>> (32-q0)
+ value[i - p0] ^= t >>> (32 - q0);
+ // block[i-p1-1] ^= << q1
+ value[i - p1 - 1] ^= (int)(t << q1);
+ // block[i-p1] ^= t >>> (32-q1)
+ value[i - p1] ^= t >>> (32 - q1);
+ value[i] = 0x00;
+ }
+ // reduce last coefficients in block containing 2^m
+ t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f)); // t
+ // contains the last coefficients > m
+ value[0] ^= t >>> (32 - q0);
+ if (min - p1 - 1 >= 0)
+ {
+ value[min - p1 - 1] ^= (int)(t << q1);
+ }
+ value[min - p1] ^= t >>> (32 - q1);
+
+ value[min] &= reverseRightMask[m & 0x1f];
+ blocks = ((m - 1) >>> 5) + 1;
+ len = m;
+ }
+
+ /**
+ * Reduces this GF2Polynomial using the pentanomial x^m + x^pc[2] +
+ * x^pc[1] + x^pc[0] + 1.
+ *
+ * @param m the degree of the used field
+ * @param pc degrees of the middle x's in the pentanomial
+ */
+ void reducePentanomial(int m, int[] pc)
+ {
+ int i;
+ int p0, p1, p2, p3;
+ int q0, q1, q2, q3;
+ long t;
+ p0 = m >>> 5;
+ q0 = 32 - (m & 0x1f);
+ p1 = (m - pc[0]) >>> 5;
+ q1 = 32 - ((m - pc[0]) & 0x1f);
+ p2 = (m - pc[1]) >>> 5;
+ q2 = 32 - ((m - pc[1]) & 0x1f);
+ p3 = (m - pc[2]) >>> 5;
+ q3 = 32 - ((m - pc[2]) & 0x1f);
+ int max = ((m << 1) - 2) >>> 5;
+ int min = p0;
+ for (i = max; i > min; i--)
+ {
+ t = value[i] & 0x00000000ffffffffL;
+ value[i - p0 - 1] ^= (int)(t << q0);
+ value[i - p0] ^= t >>> (32 - q0);
+ value[i - p1 - 1] ^= (int)(t << q1);
+ value[i - p1] ^= t >>> (32 - q1);
+ value[i - p2 - 1] ^= (int)(t << q2);
+ value[i - p2] ^= t >>> (32 - q2);
+ value[i - p3 - 1] ^= (int)(t << q3);
+ value[i - p3] ^= t >>> (32 - q3);
+ value[i] = 0;
+ }
+ t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f));
+ value[0] ^= t >>> (32 - q0);
+ if (min - p1 - 1 >= 0)
+ {
+ value[min - p1 - 1] ^= (int)(t << q1);
+ }
+ value[min - p1] ^= t >>> (32 - q1);
+ if (min - p2 - 1 >= 0)
+ {
+ value[min - p2 - 1] ^= (int)(t << q2);
+ }
+ value[min - p2] ^= t >>> (32 - q2);
+ if (min - p3 - 1 >= 0)
+ {
+ value[min - p3 - 1] ^= (int)(t << q3);
+ }
+ value[min - p3] ^= t >>> (32 - q3);
+ value[min] &= reverseRightMask[m & 0x1f];
+
+ blocks = ((m - 1) >>> 5) + 1;
+ len = m;
+ }
+
+ /**
+ * Reduces len by finding the most significant bit set to one and reducing
+ * len and blocks.
+ */
+ public void reduceN()
+ {
+ int i, j, h;
+ i = blocks - 1;
+ while ((value[i] == 0) && (i > 0))
+ {
+ i--;
+ }
+ h = value[i];
+ j = 0;
+ while (h != 0)
+ {
+ h >>>= 1;
+ j++;
+ }
+ len = (i << 5) + j;
+ blocks = i + 1;
+ }
+
+ /**
+ * Expands len and int[] value to i. This is useful before adding
+ * two GF2Polynomials of different size.
+ *
+ * @param i the intended length
+ */
+ public void expandN(int i)
+ {
+ int k;
+ int[] bs;
+ if (len >= i)
+ {
+ return;
+ }
+ len = i;
+ k = ((i - 1) >>> 5) + 1;
+ if (blocks >= k)
+ {
+ return;
+ }
+ if (value.length >= k)
+ {
+ int j;
+ for (j = blocks; j < k; j++)
+ {
+ value[j] = 0;
+ }
+ blocks = k;
+ return;
+ }
+ bs = new int[k];
+ System.arraycopy(value, 0, bs, 0, blocks);
+ blocks = k;
+ value = null;
+ value = bs;
+ }
+
+ /**
+ * Squares this GF2Polynomial and expands it accordingly. This method does
+ * not reduce the result in GF(2^N). There exists a faster method for
+ * squaring in GF(2^N).
+ *
+ * @see GF2nPolynomialElement#square
+ */
+ public void squareThisBitwise()
+ {
+ int i, h, j, k;
+ if (isZero())
+ {
+ return;
+ }
+ int[] result = new int[blocks << 1];
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ h = value[i];
+ j = 0x00000001;
+ for (k = 0; k < 16; k++)
+ {
+ if ((h & 0x01) != 0)
+ {
+ result[i << 1] |= j;
+ }
+ if ((h & 0x00010000) != 0)
+ {
+ result[(i << 1) + 1] |= j;
+ }
+ j <<= 2;
+ h >>>= 1;
+ }
+ }
+ value = null;
+ value = result;
+ blocks = result.length;
+ len = (len << 1) - 1;
+ }
+
+ /**
+ * Squares this GF2Polynomial by using precomputed values of squaringTable.
+ * This method does not reduce the result in GF(2^N).
+ */
+ public void squareThisPreCalc()
+ {
+ int i;
+ if (isZero())
+ {
+ return;
+ }
+ if (value.length >= (blocks << 1))
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ value[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16]
+ | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16);
+ value[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff]
+ | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16);
+ }
+ blocks <<= 1;
+ len = (len << 1) - 1;
+ }
+ else
+ {
+ int[] result = new int[blocks << 1];
+ for (i = 0; i < blocks; i++)
+ {
+ result[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff]
+ | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16);
+ result[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16]
+ | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16);
+ }
+ value = null;
+ value = result;
+ blocks <<= 1;
+ len = (len << 1) - 1;
+ }
+ }
+
+ /**
+ * Does a vector-multiplication modulo 2 and returns the result as boolean.
+ *
+ * @param b GF2Polynomial
+ * @return this x b as boolean (1->true, 0->false)
+ * @throws PolynomialsHaveDifferentLengthException if this and b have a different length and
+ * thus cannot be vector-multiplied
+ */
+ public boolean vectorMult(GF2Polynomial b)
+ throws RuntimeException
+ {
+ int i;
+ int h;
+ boolean result = false;
+ if (len != b.len)
+ {
+ throw new RuntimeException();
+ }
+ for (i = 0; i < blocks; i++)
+ {
+ h = value[i] & b.value[i];
+ result ^= parity[h & 0x000000ff];
+ result ^= parity[(h >>> 8) & 0x000000ff];
+ result ^= parity[(h >>> 16) & 0x000000ff];
+ result ^= parity[(h >>> 24) & 0x000000ff];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the bitwise exclusive-or of this and b in a new
+ * GF2Polynomial. this and b can be of different size.
+ *
+ * @param b GF2Polynomial
+ * @return a new GF2Polynomial (this ^ b)
+ */
+ public GF2Polynomial xor(GF2Polynomial b)
+ {
+ int i;
+ GF2Polynomial result;
+ int k = Math.min(blocks, b.blocks);
+ if (len >= b.len)
+ {
+ result = new GF2Polynomial(this);
+ for (i = 0; i < k; i++)
+ {
+ result.value[i] ^= b.value[i];
+ }
+ }
+ else
+ {
+ result = new GF2Polynomial(b);
+ for (i = 0; i < k; i++)
+ {
+ result.value[i] ^= value[i];
+ }
+ }
+ // If we xor'ed some bits too many by proceeding blockwise,
+ // restore them to zero:
+ result.zeroUnusedBits();
+ return result;
+ }
+
+ /**
+ * Computes the bitwise exclusive-or of this GF2Polynomial and b and
+ * stores the result in this GF2Polynomial. b can be of different
+ * size.
+ *
+ * @param b GF2Polynomial
+ */
+ public void xorThisBy(GF2Polynomial b)
+ {
+ int i;
+ for (i = 0; i < Math.min(blocks, b.blocks); i++)
+ {
+ value[i] ^= b.value[i];
+ }
+ // If we xor'ed some bits too many by proceeding blockwise,
+ // restore them to zero:
+ zeroUnusedBits();
+ }
+
+ /**
+ * If {@link #len} is not a multiple of the block size (32), some extra bits
+ * of the last block might have been modified during a blockwise operation.
+ * This method compensates for that by restoring these "extra" bits to zero.
+ */
+ private void zeroUnusedBits()
+ {
+ if ((len & 0x1f) != 0)
+ {
+ value[blocks - 1] &= reverseRightMask[len & 0x1f];
+ }
+ }
+
+ /**
+ * Sets the bit at position i.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (i < 0) || (i > (len - 1))
+ */
+ public void setBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] |= bitMask[i & 0x1f];
+ return;
+ }
+
+ /**
+ * Returns the bit at position i.
+ *
+ * @param i int
+ * @return the bit at position i if i is a valid position, 0
+ * otherwise.
+ */
+ public int getBit(int i)
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ return 0;
+ }
+ return ((value[i >>> 5] & bitMask[i & 0x1f]) != 0) ? 1 : 0;
+ }
+
+ /**
+ * Resets the bit at position i.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (i < 0) || (i > (len - 1))
+ */
+ public void resetBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] &= ~bitMask[i & 0x1f];
+ }
+
+ /**
+ * Xors the bit at position i.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (i < 0) || (i > (len - 1))
+ */
+ public void xorBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] ^= bitMask[i & 0x1f];
+ }
+
+ /**
+ * Tests the bit at position i.
+ *
+ * @param i the position of the bit to be tested
+ * @return true if the bit at position i is set (a(i) ==
+ * 1). False if (i < 0) || (i > (len - 1))
+ */
+ public boolean testBit(int i)
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ return false;
+ }
+ return (value[i >>> 5] & bitMask[i & 0x1f]) != 0;
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-left by 1 in a new GF2Polynomial.
+ *
+ * @return a new GF2Polynomial (this << 1)
+ */
+ public GF2Polynomial shiftLeft()
+ {
+ GF2Polynomial result = new GF2Polynomial(len + 1, value);
+ int i;
+ for (i = result.blocks - 1; i >= 1; i--)
+ {
+ result.value[i] <<= 1;
+ result.value[i] |= result.value[i - 1] >>> 31;
+ }
+ result.value[0] <<= 1;
+ return result;
+ }
+
+ /**
+ * Shifts-left this by one and enlarges the size of value if necesary.
+ */
+ public void shiftLeftThis()
+ {
+ /** @todo This is untested. */
+ int i;
+ if ((len & 0x1f) == 0)
+ { // check if blocks increases
+ len += 1;
+ blocks += 1;
+ if (blocks > value.length)
+ { // enlarge value
+ int[] bs = new int[blocks];
+ System.arraycopy(value, 0, bs, 0, value.length);
+ value = null;
+ value = bs;
+ }
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] |= value[i - 1] >>> 31;
+ value[i - 1] <<= 1;
+ }
+ }
+ else
+ {
+ len += 1;
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] <<= 1;
+ value[i] |= value[i - 1] >>> 31;
+ }
+ value[0] <<= 1;
+ }
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-left by k in a new
+ * GF2Polynomial.
+ *
+ * @param k int
+ * @return a new GF2Polynomial (this << k)
+ */
+ public GF2Polynomial shiftLeft(int k)
+ {
+ // Variant 2, requiring a modified shiftBlocksLeft(k)
+ // In case of modification, consider a rename to doShiftBlocksLeft()
+ // with an explicit note that this method assumes that the polynomial
+ // has already been resized. Or consider doing things inline.
+ // Construct the resulting polynomial of appropriate length:
+ GF2Polynomial result = new GF2Polynomial(len + k, value);
+ // Shift left as many multiples of the block size as possible:
+ if (k >= 32)
+ {
+ result.doShiftBlocksLeft(k >>> 5);
+ }
+ // Shift left by the remaining (<32) amount:
+ final int remaining = k & 0x1f;
+ if (remaining != 0)
+ {
+ for (int i = result.blocks - 1; i >= 1; i--)
+ {
+ result.value[i] <<= remaining;
+ result.value[i] |= result.value[i - 1] >>> (32 - remaining);
+ }
+ result.value[0] <<= remaining;
+ }
+ return result;
+ }
+
+ /**
+ * Shifts left b and adds the result to Its a fast version of
+ * this = add(b.shl(k));
+ *
+ * @param b GF2Polynomial to shift and add to this
+ * @param k the amount to shift
+ * @see GF2nPolynomialElement#invertEEA
+ */
+ public void shiftLeftAddThis(GF2Polynomial b, int k)
+ {
+ if (k == 0)
+ {
+ addToThis(b);
+ return;
+ }
+ int i;
+ expandN(b.len + k);
+ int d = k >>> 5;
+ for (i = b.blocks - 1; i >= 0; i--)
+ {
+ if ((i + d + 1 < blocks) && ((k & 0x1f) != 0))
+ {
+ value[i + d + 1] ^= b.value[i] >>> (32 - (k & 0x1f));
+ }
+ value[i + d] ^= b.value[i] << (k & 0x1f);
+ }
+ }
+
+ /**
+ * Shifts-left this GF2Polynomial's value blockwise 1 block resulting in a
+ * shift-left by 32.
+ *
+ * @see GF2Polynomial#multiply
+ */
+ void shiftBlocksLeft()
+ {
+ blocks += 1;
+ len += 32;
+ if (blocks <= value.length)
+ {
+ int i;
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] = value[i - 1];
+ }
+ value[0] = 0x00;
+ }
+ else
+ {
+ int[] result = new int[blocks];
+ System.arraycopy(value, 0, result, 1, blocks - 1);
+ value = null;
+ value = result;
+ }
+ }
+
+ /**
+ * Shifts left this GF2Polynomial's value blockwise b blocks
+ * resulting in a shift-left by b*32. This method assumes that {@link #len}
+ * and {@link #blocks} have already been updated to reflect the final state.
+ *
+ * @param b shift amount (in blocks)
+ */
+ private void doShiftBlocksLeft(int b)
+ {
+ if (blocks <= value.length)
+ {
+ int i;
+ for (i = blocks - 1; i >= b; i--)
+ {
+ value[i] = value[i - b];
+ }
+ for (i = 0; i < b; i++)
+ {
+ value[i] = 0x00;
+ }
+ }
+ else
+ {
+ int[] result = new int[blocks];
+ System.arraycopy(value, 0, result, b, blocks - b);
+ value = null;
+ value = result;
+ }
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-right by 1 in a new GF2Polynomial.
+ *
+ * @return a new GF2Polynomial (this << 1)
+ */
+ public GF2Polynomial shiftRight()
+ {
+ GF2Polynomial result = new GF2Polynomial(len - 1);
+ int i;
+ System.arraycopy(value, 0, result.value, 0, result.blocks);
+ for (i = 0; i <= result.blocks - 2; i++)
+ {
+ result.value[i] >>>= 1;
+ result.value[i] |= result.value[i + 1] << 31;
+ }
+ result.value[result.blocks - 1] >>>= 1;
+ if (result.blocks < blocks)
+ {
+ result.value[result.blocks - 1] |= value[result.blocks] << 31;
+ }
+ return result;
+ }
+
+ /**
+ * Shifts-right this GF2Polynomial by 1.
+ */
+ public void shiftRightThis()
+ {
+ int i;
+ len -= 1;
+ blocks = ((len - 1) >>> 5) + 1;
+ for (i = 0; i <= blocks - 2; i++)
+ {
+ value[i] >>>= 1;
+ value[i] |= value[i + 1] << 31;
+ }
+ value[blocks - 1] >>>= 1;
+ if ((len & 0x1f) == 0)
+ {
+ value[blocks - 1] |= value[blocks] << 31;
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java
new file mode 100644
index 000000000..560ad2166
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java
@@ -0,0 +1,539 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class implements the abstract class Vector for the case of
+ * vectors over the finite field GF(2).
+ * For the vector representation the array of type int[] is used, thus one
+ * element of the array holds 32 elements of the vector.
+ *
+ * @see Vector
+ */
+public class GF2Vector
+ extends Vector
+{
+
+ /**
+ * holds the elements of this vector
+ */
+ private int[] v;
+
+ /**
+ * Construct the zero vector of the given length.
+ *
+ * @param length the length of the vector
+ */
+ public GF2Vector(int length)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("Negative length.");
+ }
+ this.length = length;
+ v = new int[(length + 31) >> 5];
+ }
+
+ /**
+ * Construct a random GF2Vector of the given length.
+ *
+ * @param length the length of the vector
+ * @param sr the source of randomness
+ */
+ public GF2Vector(int length, SecureRandom sr)
+ {
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+ v = new int[size];
+
+ // generate random elements
+ for (int i = size - 1; i >= 0; i--)
+ {
+ v[i] = sr.nextInt();
+ }
+
+ // erase unused bits
+ int r = length & 0x1f;
+ if (r != 0)
+ {
+ // erase unused bits
+ v[size - 1] &= (1 << r) - 1;
+ }
+ }
+
+ /**
+ * Construct a random GF2Vector of the given length with the specified
+ * number of non-zero coefficients.
+ *
+ * @param length the length of the vector
+ * @param t the number of non-zero coefficients
+ * @param sr the source of randomness
+ */
+ public GF2Vector(int length, int t, SecureRandom sr)
+ {
+ if (t > length)
+ {
+ throw new ArithmeticException(
+ "The hamming weight is greater than the length of vector.");
+ }
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+ v = new int[size];
+
+ int[] help = new int[length];
+ for (int i = 0; i < length; i++)
+ {
+ help[i] = i;
+ }
+
+ int m = length;
+ for (int i = 0; i < t; i++)
+ {
+ int j = RandUtils.nextInt(sr, m);
+ setBit(help[j]);
+ m--;
+ help[j] = help[m];
+ }
+ }
+
+ /**
+ * Construct a GF2Vector of the given length and with elements from the
+ * given array. The array is copied and unused bits are masked out.
+ *
+ * @param length the length of the vector
+ * @param v the element array
+ */
+ public GF2Vector(int length, int[] v)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("negative length");
+ }
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+
+ if (v.length != size)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ this.v = IntUtils.clone(v);
+
+ int r = length & 0x1f;
+ if (r != 0)
+ {
+ // erase unused bits
+ this.v[size - 1] &= (1 << r) - 1;
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2Vector}
+ */
+ public GF2Vector(GF2Vector other)
+ {
+ this.length = other.length;
+ this.v = IntUtils.clone(other.v);
+ }
+
+ /**
+ * Construct a new {@link GF2Vector} of the given length and with the given
+ * element array. The array is not changed and only a reference to the array
+ * is stored. No length checking is performed either.
+ *
+ * @param v the element array
+ * @param length the length of the vector
+ */
+ protected GF2Vector(int[] v, int length)
+ {
+ this.v = v;
+ this.length = length;
+ }
+
+ /**
+ * Construct a new GF2Vector with the given length out of the encoded
+ * vector.
+ *
+ * @param length the length of the vector
+ * @param encVec the encoded vector
+ * @return the decoded vector
+ */
+ public static GF2Vector OS2VP(int length, byte[] encVec)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("negative length");
+ }
+
+ int byteLen = (length + 7) >> 3;
+
+ if (encVec.length > byteLen)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ return new GF2Vector(length, LittleEndianConversions.toIntArray(encVec));
+ }
+
+ /**
+ * Encode this vector as byte array.
+ *
+ * @return the encoded vector
+ */
+ public byte[] getEncoded()
+ {
+ int byteLen = (length + 7) >> 3;
+ return LittleEndianConversions.toByteArray(v, byteLen);
+ }
+
+ /**
+ * @return the int array representation of this vector
+ */
+ public int[] getVecArray()
+ {
+ return v;
+ }
+
+ /**
+ * Return the Hamming weight of this vector, i.e., compute the number of
+ * units of this vector.
+ *
+ * @return the Hamming weight of this vector
+ */
+ public int getHammingWeight()
+ {
+ int weight = 0;
+ for (int i = 0; i < v.length; i++)
+ {
+ int e = v[i];
+ for (int j = 0; j < 32; j++)
+ {
+ int b = e & 1;
+ if (b != 0)
+ {
+ weight++;
+ }
+ e >>>= 1;
+ }
+ }
+ return weight;
+ }
+
+ /**
+ * @return whether this is the zero vector (i.e., all elements are zero)
+ */
+ public boolean isZero()
+ {
+ for (int i = v.length - 1; i >= 0; i--)
+ {
+ if (v[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the value of the bit of this vector at the specified index.
+ *
+ * @param index the index
+ * @return the value of the bit (0 or 1)
+ */
+ public int getBit(int index)
+ {
+ if (index >= length)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ int q = index >> 5;
+ int r = index & 0x1f;
+ return (v[q] & (1 << r)) >>> r;
+ }
+
+ /**
+ * Set the coefficient at the given index to 1. If the index is out of
+ * bounds, do nothing.
+ *
+ * @param index the index of the coefficient to set
+ */
+ public void setBit(int index)
+ {
+ if (index >= length)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ v[index >> 5] |= 1 << (index & 0x1f);
+ }
+
+ /**
+ * Adds another GF2Vector to this vector.
+ *
+ * @param other another GF2Vector
+ * @return this + other
+ * @throws ArithmeticException if the other vector is not a GF2Vector or has another
+ * length.
+ */
+ public Vector add(Vector other)
+ {
+ if (!(other instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ GF2Vector otherVec = (GF2Vector)other;
+ if (length != otherVec.length)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] vec = IntUtils.clone(((GF2Vector)other).v);
+
+ for (int i = vec.length - 1; i >= 0; i--)
+ {
+ vec[i] ^= v[i];
+ }
+
+ return new GF2Vector(length, vec);
+ }
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return this*p = p*this
+ */
+ public Vector multiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (length != pVec.length)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Vector result = new GF2Vector(length);
+
+ for (int i = 0; i < pVec.length; i++)
+ {
+ int e = v[pVec[i] >> 5] & (1 << (pVec[i] & 0x1f));
+ if (e != 0)
+ {
+ result.v[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the elements of this vector with the
+ * indices given by the set setJ.
+ *
+ * @param setJ the set of indices of elements to extract
+ * @return the new {@link GF2Vector}
+ * [this_setJ[0], this_setJ[1], ..., this_setJ[#setJ-1]]
+ */
+ public GF2Vector extractVector(int[] setJ)
+ {
+ int k = setJ.length;
+ if (setJ[k - 1] > length)
+ {
+ throw new ArithmeticException("invalid index set");
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ for (int i = 0; i < k; i++)
+ {
+ int e = v[setJ[i] >> 5] & (1 << (setJ[i] & 0x1f));
+ if (e != 0)
+ {
+ result.v[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the first k elements of this
+ * vector.
+ *
+ * @param k the number of elements to extract
+ * @return a new {@link GF2Vector} consisting of the first k
+ * elements of this vector
+ */
+ public GF2Vector extractLeftVector(int k)
+ {
+ if (k > length)
+ {
+ throw new ArithmeticException("invalid length");
+ }
+
+ if (k == length)
+ {
+ return new GF2Vector(this);
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ int q = k >> 5;
+ int r = k & 0x1f;
+
+ System.arraycopy(v, 0, result.v, 0, q);
+ if (r != 0)
+ {
+ result.v[q] = v[q] & ((1 << r) - 1);
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the last k elements of this
+ * vector.
+ *
+ * @param k the number of elements to extract
+ * @return a new {@link GF2Vector} consisting of the last k
+ * elements of this vector
+ */
+ public GF2Vector extractRightVector(int k)
+ {
+ if (k > length)
+ {
+ throw new ArithmeticException("invalid length");
+ }
+
+ if (k == length)
+ {
+ return new GF2Vector(this);
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ int q = (length - k) >> 5;
+ int r = (length - k) & 0x1f;
+ int length = (k + 31) >> 5;
+
+ int ind = q;
+ // if words have to be shifted
+ if (r != 0)
+ {
+ // process all but last word
+ for (int i = 0; i < length - 1; i++)
+ {
+ result.v[i] = (v[ind++] >>> r) | (v[ind] << (32 - r));
+ }
+ // process last word
+ result.v[length - 1] = v[ind++] >>> r;
+ if (ind < v.length)
+ {
+ result.v[length - 1] |= v[ind] << (32 - r);
+ }
+ }
+ else
+ {
+ // no shift necessary
+ System.arraycopy(v, q, result.v, 0, length);
+ }
+
+ return result;
+ }
+
+ /**
+ * Rewrite this vector as a vector over GF(2m) with
+ * t elements.
+ *
+ * @param field the finite field GF(2m)
+ * @return the converted vector over GF(2m)
+ */
+ public GF2mVector toExtensionFieldVector(GF2mField field)
+ {
+ int m = field.getDegree();
+ if ((length % m) != 0)
+ {
+ throw new ArithmeticException("conversion is impossible");
+ }
+
+ int t = length / m;
+ int[] result = new int[t];
+ int count = 0;
+ for (int i = t - 1; i >= 0; i--)
+ {
+ for (int j = field.getDegree() - 1; j >= 0; j--)
+ {
+ int q = count >>> 5;
+ int r = count & 0x1f;
+
+ int e = (v[q] >>> r) & 1;
+ if (e == 1)
+ {
+ result[i] ^= 1 << j;
+ }
+ count++;
+ }
+ }
+ return new GF2mVector(field, result);
+ }
+
+ /**
+ * Check if the given object is equal to this vector.
+ *
+ * @param other vector
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2Vector))
+ {
+ return false;
+ }
+ GF2Vector otherVec = (GF2Vector)other;
+
+ return (length == otherVec.length) && IntUtils.equals(v, otherVec.v);
+ }
+
+ /**
+ * @return the hash code of this vector
+ */
+ public int hashCode()
+ {
+ int hash = length;
+ hash = hash * 31 + v.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < length; i++)
+ {
+ if ((i != 0) && ((i & 0x1f) == 0))
+ {
+ buf.append(' ');
+ }
+ int q = i >> 5;
+ int r = i & 0x1f;
+ int bit = v[q] & (1 << r);
+ if (bit == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java
new file mode 100644
index 000000000..5e7fa734c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java
@@ -0,0 +1,366 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes operations with elements from the finite field F =
+ * GF(2^m). ( GF(2^m)= GF(2)[A] where A is a root of irreducible polynomial with
+ * degree m, each field element B has a polynomial basis representation, i.e. it
+ * is represented by a different binary polynomial of degree less than m, B =
+ * poly(A) ) All operations are defined only for field with 1< m <32. For the
+ * representation of field elements the map f: F->Z, poly(A)->poly(2) is used,
+ * where integers have the binary representation. For example: A^7+A^3+A+1 ->
+ * (00...0010001011)=139 Also for elements type Integer is used.
+ *
+ * @see PolynomialRingGF2
+ */
+public class GF2mField
+{
+
+ /*
+ * degree - degree of the field polynomial - the field polynomial ring -
+ * polynomial ring over the finite field GF(2)
+ */
+
+ private int degree = 0;
+
+ private int polynomial;
+
+ /**
+ * create a finite field GF(2^m)
+ *
+ * @param degree the degree of the field
+ */
+ public GF2mField(int degree)
+ {
+ if (degree >= 32)
+ {
+ throw new IllegalArgumentException(
+ " Error: the degree of field is too large ");
+ }
+ if (degree < 1)
+ {
+ throw new IllegalArgumentException(
+ " Error: the degree of field is non-positive ");
+ }
+ this.degree = degree;
+ polynomial = PolynomialRingGF2.getIrreduciblePolynomial(degree);
+ }
+
+ /**
+ * create a finite field GF(2^m) with the fixed field polynomial
+ *
+ * @param degree the degree of the field
+ * @param poly the field polynomial
+ */
+ public GF2mField(int degree, int poly)
+ {
+ if (degree != PolynomialRingGF2.degree(poly))
+ {
+ throw new IllegalArgumentException(
+ " Error: the degree is not correct");
+ }
+ if (!PolynomialRingGF2.isIrreducible(poly))
+ {
+ throw new IllegalArgumentException(
+ " Error: given polynomial is reducible");
+ }
+ this.degree = degree;
+ polynomial = poly;
+
+ }
+
+ public GF2mField(byte[] enc)
+ {
+ if (enc.length != 4)
+ {
+ throw new IllegalArgumentException(
+ "byte array is not an encoded finite field");
+ }
+ polynomial = LittleEndianConversions.OS2IP(enc);
+ if (!PolynomialRingGF2.isIrreducible(polynomial))
+ {
+ throw new IllegalArgumentException(
+ "byte array is not an encoded finite field");
+ }
+
+ degree = PolynomialRingGF2.degree(polynomial);
+ }
+
+ public GF2mField(GF2mField field)
+ {
+ degree = field.degree;
+ polynomial = field.polynomial;
+ }
+
+ /**
+ * return degree of the field
+ *
+ * @return degree of the field
+ */
+ public int getDegree()
+ {
+ return degree;
+ }
+
+ /**
+ * return the field polynomial
+ *
+ * @return the field polynomial
+ */
+ public int getPolynomial()
+ {
+ return polynomial;
+ }
+
+ /**
+ * return the encoded form of this field
+ *
+ * @return the field in byte array form
+ */
+ public byte[] getEncoded()
+ {
+ return LittleEndianConversions.I2OSP(polynomial);
+ }
+
+ /**
+ * Return sum of two elements
+ *
+ * @param a
+ * @param b
+ * @return a+b
+ */
+ public int add(int a, int b)
+ {
+ return a ^ b;
+ }
+
+ /**
+ * Return product of two elements
+ *
+ * @param a
+ * @param b
+ * @return a*b
+ */
+ public int mult(int a, int b)
+ {
+ return PolynomialRingGF2.modMultiply(a, b, polynomial);
+ }
+
+ /**
+ * compute exponentiation a^k
+ *
+ * @param a a field element a
+ * @param k k degree
+ * @return a^k
+ */
+ public int exp(int a, int k)
+ {
+ if (a == 0)
+ {
+ return 0;
+ }
+ if (a == 1)
+ {
+ return 1;
+ }
+ int result = 1;
+ if (k < 0)
+ {
+ a = inverse(a);
+ k = -k;
+ }
+ while (k != 0)
+ {
+ if ((k & 1) == 1)
+ {
+ result = mult(result, a);
+ }
+ a = mult(a, a);
+ k >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * compute the multiplicative inverse of a
+ *
+ * @param a a field element a
+ * @return a-1
+ */
+ public int inverse(int a)
+ {
+ int d = (1 << degree) - 2;
+
+ return exp(a, d);
+ }
+
+ /**
+ * compute the square root of an integer
+ *
+ * @param a a field element a
+ * @return a1/2
+ */
+ public int sqRoot(int a)
+ {
+ for (int i = 1; i < degree; i++)
+ {
+ a = mult(a, a);
+ }
+ return a;
+ }
+
+ /**
+ * create a random field element using PRNG sr
+ *
+ * @param sr SecureRandom
+ * @return a random element
+ */
+ public int getRandomElement(SecureRandom sr)
+ {
+ int result = RandUtils.nextInt(sr, 1 << degree);
+ return result;
+ }
+
+ /**
+ * create a random non-zero field element
+ *
+ * @return a random element
+ */
+ public int getRandomNonZeroElement()
+ {
+ return getRandomNonZeroElement(new SecureRandom());
+ }
+
+ /**
+ * create a random non-zero field element using PRNG sr
+ *
+ * @param sr SecureRandom
+ * @return a random non-zero element
+ */
+ public int getRandomNonZeroElement(SecureRandom sr)
+ {
+ int controltime = 1 << 20;
+ int count = 0;
+ int result = RandUtils.nextInt(sr, 1 << degree);
+ while ((result == 0) && (count < controltime))
+ {
+ result = RandUtils.nextInt(sr, 1 << degree);
+ count++;
+ }
+ if (count == controltime)
+ {
+ result = 1;
+ }
+ return result;
+ }
+
+ /**
+ * @return true if e is encoded element of this field and false otherwise
+ */
+ public boolean isElementOfThisField(int e)
+ {
+ // e is encoded element of this field iff 0<= e < |2^m|
+ if (degree == 31)
+ {
+ return e >= 0;
+ }
+ return e >= 0 && e < (1 << degree);
+ }
+
+ /*
+ * help method for visual control
+ */
+ public String elementToStr(int a)
+ {
+ String s = "";
+ for (int i = 0; i < degree; i++)
+ {
+ if (((byte)a & 0x01) == 0)
+ {
+ s = "0" + s;
+ }
+ else
+ {
+ s = "1" + s;
+ }
+ a >>>= 1;
+ }
+ return s;
+ }
+
+ /**
+ * checks if given object is equal to this field.
+ *
+ * The method returns false whenever the given object is not GF2m.
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+ if ((other == null) || !(other instanceof GF2mField))
+ {
+ return false;
+ }
+
+ GF2mField otherField = (GF2mField)other;
+
+ if ((degree == otherField.degree)
+ && (polynomial == otherField.polynomial))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return polynomial;
+ }
+
+ /**
+ * Returns a human readable form of this field.
+ *
+ *
+ * @return a human readable form of this field.
+ */
+ public String toString()
+ {
+ String str = "Finite Field GF(2^" + degree + ") = " + "GF(2)[X]/<"
+ + polyToString(polynomial) + "> ";
+ return str;
+ }
+
+ private static String polyToString(int p)
+ {
+ String str = "";
+ if (p == 0)
+ {
+ str = "0";
+ }
+ else
+ {
+ byte b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ str = "1";
+ }
+ p >>>= 1;
+ int i = 1;
+ while (p != 0)
+ {
+ b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ str = str + "+x^" + i;
+ }
+ p >>>= 1;
+ i++;
+ }
+ }
+ return str;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java
new file mode 100644
index 000000000..fb2b1e4b8
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java
@@ -0,0 +1,377 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class describes some operations with matrices over finite field GF(2m)
+ * with small m (1< m <32).
+ *
+ * @see Matrix
+ */
+public class GF2mMatrix
+ extends Matrix
+{
+
+ /**
+ * finite field GF(2^m)
+ */
+ protected GF2mField field;
+
+ /**
+ * For the matrix representation the array of type int[][] is used, thus
+ * every element of the array keeps one element of the matrix (element from
+ * finite field GF(2^m))
+ */
+ protected int[][] matrix;
+
+ /**
+ * Constructor.
+ *
+ * @param field a finite field GF(2^m)
+ * @param enc byte[] matrix in byte array form
+ */
+ public GF2mMatrix(GF2mField field, byte[] enc)
+ {
+
+ this.field = field;
+
+ // decode matrix
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if (enc.length < 5)
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+
+ this.numRows = ((enc[3] & 0xff) << 24) ^ ((enc[2] & 0xff) << 16)
+ ^ ((enc[1] & 0xff) << 8) ^ (enc[0] & 0xff);
+
+ int n = count * this.numRows;
+
+ if ((this.numRows <= 0) || (((enc.length - 4) % n) != 0))
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+
+ this.numColumns = (enc.length - 4) / n;
+
+ matrix = new int[this.numRows][this.numColumns];
+ count = 4;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ for (int jj = 0; jj < d; jj += 8)
+ {
+ matrix[i][j] ^= (enc[count++] & 0x000000ff) << jj;
+ }
+ if (!this.field.isElementOfThisField(matrix[i][j]))
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2mMatrix}
+ */
+ public GF2mMatrix(GF2mMatrix other)
+ {
+ numRows = other.numRows;
+ numColumns = other.numColumns;
+ field = other.field;
+ matrix = new int[numRows][];
+ for (int i = 0; i < numRows; i++)
+ {
+ matrix[i] = IntUtils.clone(other.matrix[i]);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param field a finite field GF(2^m)
+ * @param matrix the matrix as int array. Only the reference is copied.
+ */
+ protected GF2mMatrix(GF2mField field, int[][] matrix)
+ {
+ this.field = field;
+ this.matrix = matrix;
+ numRows = matrix.length;
+ numColumns = matrix[0].length;
+ }
+
+ /**
+ * @return a byte array encoding of this matrix
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] bf = new byte[this.numRows * this.numColumns * count + 4];
+ bf[0] = (byte)(this.numRows & 0xff);
+ bf[1] = (byte)((this.numRows >>> 8) & 0xff);
+ bf[2] = (byte)((this.numRows >>> 16) & 0xff);
+ bf[3] = (byte)((this.numRows >>> 24) & 0xff);
+
+ count = 4;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ for (int jj = 0; jj < d; jj += 8)
+ {
+ bf[count++] = (byte)(matrix[i][j] >>> jj);
+ }
+ }
+ }
+
+ return bf;
+ }
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return true if this is the zero matrix
+ */
+ public boolean isZero()
+ {
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numColumns; j++)
+ {
+ if (matrix[i][j] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ */
+ public Matrix computeInverse()
+ {
+ if (numRows != numColumns)
+ {
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+
+ // clone this matrix
+ int[][] tmpMatrix = new int[numRows][numRows];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = IntUtils.clone(matrix[i]);
+ }
+
+ // initialize inverse matrix as unit matrix
+ int[][] invMatrix = new int[numRows][numRows];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ invMatrix[i][i] = 1;
+ }
+
+ // simultaneously compute Gaussian reduction of tmpMatrix and unit
+ // matrix
+ for (int i = 0; i < numRows; i++)
+ {
+ // if diagonal element is zero
+ if (tmpMatrix[i][i] == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same column
+ for (int j = i + 1; j < numRows; j++)
+ {
+ if (tmpMatrix[j][i] != 0)
+ {
+ // found it, swap rows ...
+ foundNonZero = true;
+ swapColumns(tmpMatrix, i, j);
+ swapColumns(invMatrix, i, j);
+ // ... and quit searching
+ j = numRows;
+ continue;
+ }
+ }
+ // if no non-zero element was found
+ if (!foundNonZero)
+ {
+ // the matrix is not invertible
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+ }
+
+ // normalize i-th row
+ int coef = tmpMatrix[i][i];
+ int invCoef = field.inverse(coef);
+ multRowWithElementThis(tmpMatrix[i], invCoef);
+ multRowWithElementThis(invMatrix[i], invCoef);
+
+ // normalize all other rows
+ for (int j = 0; j < numRows; j++)
+ {
+ if (j != i)
+ {
+ coef = tmpMatrix[j][i];
+ if (coef != 0)
+ {
+ int[] tmpRow = multRowWithElement(tmpMatrix[i], coef);
+ int[] tmpInvRow = multRowWithElement(invMatrix[i], coef);
+ addToRow(tmpRow, tmpMatrix[j]);
+ addToRow(tmpInvRow, invMatrix[j]);
+ }
+ }
+ }
+ }
+
+ return new GF2mMatrix(field, invMatrix);
+ }
+
+ private static void swapColumns(int[][] matrix, int first, int second)
+ {
+ int[] tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+ private void multRowWithElementThis(int[] row, int element)
+ {
+ for (int i = row.length - 1; i >= 0; i--)
+ {
+ row[i] = field.mult(row[i], element);
+ }
+ }
+
+ private int[] multRowWithElement(int[] row, int element)
+ {
+ int[] result = new int[row.length];
+ for (int i = row.length - 1; i >= 0; i--)
+ {
+ result[i] = field.mult(row[i], element);
+ }
+ return result;
+ }
+
+ /**
+ * Add one row to another.
+ *
+ * @param fromRow the addend
+ * @param toRow the row to add to
+ */
+ private void addToRow(int[] fromRow, int[] toRow)
+ {
+ for (int i = toRow.length - 1; i >= 0; i--)
+ {
+ toRow[i] = field.add(fromRow[i], toRow[i]);
+ }
+ }
+
+ public Matrix rightMultiply(Matrix a)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Matrix rightMultiply(Permutation perm)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Vector leftMultiply(Vector vector)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Vector rightMultiply(Vector vector)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Checks if given object is equal to this matrix. The method returns false
+ * whenever the given object is not a matrix over GF(2^m).
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (other == null || !(other instanceof GF2mMatrix))
+ {
+ return false;
+ }
+
+ GF2mMatrix otherMatrix = (GF2mMatrix)other;
+
+ if ((!this.field.equals(otherMatrix.field))
+ || (otherMatrix.numRows != this.numColumns)
+ || (otherMatrix.numColumns != this.numColumns))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ if (this.matrix[i][j] != otherMatrix.matrix[i][j])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int hash = (this.field.hashCode() * 31 + numRows) * 31 + numColumns;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ hash = hash * 31 + matrix[i][j];
+ }
+ }
+ return hash;
+ }
+
+ public String toString()
+ {
+ String str = this.numRows + " x " + this.numColumns + " Matrix over "
+ + this.field.toString() + ": \n";
+
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ str = str + this.field.elementToStr(matrix[i][j]) + " : ";
+ }
+ str = str + "\n";
+ }
+
+ return str;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java
new file mode 100644
index 000000000..b576ef632
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java
@@ -0,0 +1,256 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This class implements vectors over the finite field
+ * GF(2m) for small m (i.e.,
+ * 1<m<32). It extends the abstract class {@link Vector}.
+ */
+public class GF2mVector
+ extends Vector
+{
+
+ /**
+ * the finite field this vector is defined over
+ */
+ private GF2mField field;
+
+ /**
+ * the element array
+ */
+ private int[] vector;
+
+ /**
+ * creates the vector over GF(2^m) of given length and with elements from
+ * array v (beginning at the first bit)
+ *
+ * @param field finite field
+ * @param v array with elements of vector
+ */
+ public GF2mVector(GF2mField field, byte[] v)
+ {
+ this.field = new GF2mField(field);
+
+ // decode vector
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if ((v.length % count) != 0)
+ {
+ throw new IllegalArgumentException(
+ "Byte array is not an encoded vector over the given finite field.");
+ }
+
+ length = v.length / count;
+ vector = new int[length];
+ count = 0;
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ vector[i] |= (v[count++] & 0xff) << j;
+ }
+ if (!field.isElementOfThisField(vector[i]))
+ {
+ throw new IllegalArgumentException(
+ "Byte array is not an encoded vector over the given finite field.");
+ }
+ }
+ }
+
+ /**
+ * Create a new vector over GF(2m) of the given
+ * length and element array.
+ *
+ * @param field the finite field GF(2m)
+ * @param vector the element array
+ */
+ public GF2mVector(GF2mField field, int[] vector)
+ {
+ this.field = field;
+ length = vector.length;
+ for (int i = vector.length - 1; i >= 0; i--)
+ {
+ if (!field.isElementOfThisField(vector[i]))
+ {
+ throw new ArithmeticException(
+ "Element array is not specified over the given finite field.");
+ }
+ }
+ this.vector = IntUtils.clone(vector);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2mVector}
+ */
+ public GF2mVector(GF2mVector other)
+ {
+ field = new GF2mField(other.field);
+ length = other.length;
+ vector = IntUtils.clone(other.vector);
+ }
+
+ /**
+ * @return the finite field this vector is defined over
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return int[] form of this vector
+ */
+ public int[] getIntArrayForm()
+ {
+ return IntUtils.clone(vector);
+ }
+
+ /**
+ * @return a byte array encoding of this vector
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] res = new byte[vector.length * count];
+ count = 0;
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ res[count++] = (byte)(vector[i] >>> j);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @return whether this is the zero vector (i.e., all elements are zero)
+ */
+ public boolean isZero()
+ {
+ for (int i = vector.length - 1; i >= 0; i--)
+ {
+ if (vector[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Add another vector to this vector. Method is not yet implemented.
+ *
+ * @param addend the other vector
+ * @return this + addend
+ * @throws ArithmeticException if the other vector is not defined over the same field as
+ * this vector.
+ *
+ * TODO: implement this method
+ */
+ public Vector add(Vector addend)
+ {
+ throw new RuntimeException("not implemented");
+ }
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return this*p = p*this
+ */
+ public Vector multiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (length != pVec.length)
+ {
+ throw new ArithmeticException(
+ "permutation size and vector size mismatch");
+ }
+
+ int[] result = new int[length];
+ for (int i = 0; i < pVec.length; i++)
+ {
+ result[i] = vector[pVec[i]];
+ }
+
+ return new GF2mVector(field, result);
+ }
+
+ /**
+ * Compare this vector with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2mVector))
+ {
+ return false;
+ }
+ GF2mVector otherVec = (GF2mVector)other;
+
+ if (!field.equals(otherVec.field))
+ {
+ return false;
+ }
+
+ return IntUtils.equals(vector, otherVec.vector);
+ }
+
+ /**
+ * @return the hash code of this vector
+ */
+ public int hashCode()
+ {
+ int hash = this.field.hashCode();
+ hash = hash * 31 + vector.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < field.getDegree(); j++)
+ {
+ int r = j & 0x1f;
+ int bitMask = 1 << r;
+ int coeff = vector[i] & bitMask;
+ if (coeff != 0)
+ {
+ buf.append('1');
+ }
+ else
+ {
+ buf.append('0');
+ }
+ }
+ buf.append(' ');
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java
new file mode 100644
index 000000000..a95b25f83
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java
@@ -0,0 +1,186 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This abstract class implements an element of the finite field GF(2)n
+ * in either optimal normal basis representation (ONB)
+ * or in polynomial representation. It is extended by the classes GF2nONBElement and GF2nPolynomialElement .
+ *
+ * @see GF2nPolynomialElement
+ * @see GF2nONBElement
+ * @see GF2nONBField
+ */
+public abstract class GF2nElement
+ implements GFElement
+{
+
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * holds a pointer to this element's corresponding field.
+ */
+ protected GF2nField mField;
+
+ /**
+ * holds the extension degree n of this element's corresponding
+ * field.
+ */
+ protected int mDegree;
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * @return a copy of this GF2nElement
+ */
+ public abstract Object clone();
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Assign the value 0 to this element.
+ */
+ abstract void assignZero();
+
+ /**
+ * Assigns the value 1 to this element.
+ */
+ abstract void assignOne();
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns whether the rightmost bit of the bit representation is set. This
+ * is needed for data conversion according to 1363.
+ *
+ * @return true if the rightmost bit of this element is set
+ */
+ public abstract boolean testRightmostBit();
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set
+ *
+ * @param index the index of the bit to test
+ * @return true if the indexed bit is set
+ */
+ abstract boolean testBit(int index);
+
+ /**
+ * Returns the field of this element.
+ *
+ * @return the field of this element
+ */
+ public final GF2nField getField()
+ {
+ return mField;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns this element + 1.
+ *
+ * @return this + 1
+ */
+ public abstract GF2nElement increase();
+
+ /**
+ * Increases this element by one.
+ */
+ public abstract void increaseThis();
+
+ /**
+ * Compute the difference of this element and minuend.
+ *
+ * @param minuend the minuend
+ * @return this - minuend (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public final GFElement subtract(GFElement minuend)
+ throws RuntimeException
+ {
+ return add(minuend);
+ }
+
+ /**
+ * Compute the difference of this element and minuend,
+ * overwriting this element.
+ *
+ * @param minuend the minuend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public final void subtractFromThis(GFElement minuend)
+ {
+ addToThis(minuend);
+ }
+
+ /**
+ * Returns this element to the power of 2.
+ *
+ * @return this2
+ */
+ public abstract GF2nElement square();
+
+ /**
+ * Squares this element.
+ */
+ public abstract void squareThis();
+
+ /**
+ * Compute the square root of this element and return the result in a new
+ * {@link GF2nElement}.
+ *
+ * @return this1/2 (newly created)
+ */
+ public abstract GF2nElement squareRoot();
+
+ /**
+ * Compute the square root of this element.
+ */
+ public abstract void squareRootThis();
+
+ /**
+ * Performs a basis transformation of this element to the given GF2nField
+ * basis.
+ *
+ * @param basis the GF2nField representation to transform this element to
+ * @return this element in the representation of basis
+ * @throws DifferentFieldsException if this cannot be converted according to
+ * basis.
+ */
+ public final GF2nElement convert(GF2nField basis)
+ throws RuntimeException
+ {
+ return mField.convert(this, basis);
+ }
+
+ /**
+ * Returns the trace of this element.
+ *
+ * @return the trace of this element
+ */
+ public abstract int trace();
+
+ /**
+ * Solves a quadratic equation.
+ * Let z2 + z = this. Then this method returns z.
+ *
+ * @return z with z2 + z = this
+ * @throws NoSolutionException if z2 + z = this does not have a
+ * solution
+ */
+ public abstract GF2nElement solveQuadraticEquation()
+ throws RuntimeException;
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java
new file mode 100644
index 000000000..d1aa13633
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java
@@ -0,0 +1,292 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.util.Vector;
+
+
+/**
+ * This abstract class defines the finite field GF(2n). It
+ * holds the extension degree n, the characteristic, the irreducible
+ * fieldpolynomial and conversion matrices. GF2nField is implemented by the
+ * classes GF2nPolynomialField and GF2nONBField.
+ *
+ * @see GF2nONBField
+ * @see GF2nPolynomialField
+ */
+public abstract class GF2nField
+{
+
+ /**
+ * the degree of this field
+ */
+ protected int mDegree;
+
+ /**
+ * the irreducible fieldPolynomial stored in normal order (also for ONB)
+ */
+ protected GF2Polynomial fieldPolynomial;
+
+ /**
+ * holds a list of GF2nFields to which elements have been converted and thus
+ * a COB-Matrix exists
+ */
+ protected Vector fields;
+
+ /**
+ * the COB matrices
+ */
+ protected Vector matrices;
+
+ /**
+ * Returns the degree n of this field.
+ *
+ * @return the degree n of this field
+ */
+ public final int getDegree()
+ {
+ return mDegree;
+ }
+
+ /**
+ * Returns the fieldpolynomial as a new Bitstring.
+ *
+ * @return a copy of the fieldpolynomial as a new Bitstring
+ */
+ public final GF2Polynomial getFieldPolynomial()
+ {
+ if (fieldPolynomial == null)
+ {
+ computeFieldPolynomial();
+ }
+ return new GF2Polynomial(fieldPolynomial);
+ }
+
+ /**
+ * Decides whether the given object other is the same as this
+ * field.
+ *
+ * @param other another object
+ * @return (this == other)
+ */
+ public final boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nField))
+ {
+ return false;
+ }
+
+ GF2nField otherField = (GF2nField)other;
+
+ if (otherField.mDegree != mDegree)
+ {
+ return false;
+ }
+ if (!fieldPolynomial.equals(otherField.fieldPolynomial))
+ {
+ return false;
+ }
+ if ((this instanceof GF2nPolynomialField)
+ && !(otherField instanceof GF2nPolynomialField))
+ {
+ return false;
+ }
+ if ((this instanceof GF2nONBField)
+ && !(otherField instanceof GF2nONBField))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this field
+ */
+ public int hashCode()
+ {
+ return mDegree + fieldPolynomial.hashCode();
+ }
+
+ /**
+ * Computes a random root from the given irreducible fieldpolynomial
+ * according to IEEE 1363 algorithm A.5.6. This cal take very long for big
+ * degrees.
+ *
+ * @param B0FieldPolynomial the fieldpolynomial if the other basis as a Bitstring
+ * @return a random root of BOFieldPolynomial in representation according to
+ * this field
+ * @see "P1363 A.5.6, p103f"
+ */
+ protected abstract GF2nElement getRandomRoot(GF2Polynomial B0FieldPolynomial);
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected abstract void computeCOBMatrix(GF2nField B1);
+
+ /**
+ * Computes the fieldpolynomial. This can take a long time for big degrees.
+ */
+ protected abstract void computeFieldPolynomial();
+
+ /**
+ * Inverts the given matrix represented as bitstrings.
+ *
+ * @param matrix the matrix to invert as a Bitstring[]
+ * @return matrix^(-1)
+ */
+ protected final GF2Polynomial[] invertMatrix(GF2Polynomial[] matrix)
+ {
+ GF2Polynomial[] a = new GF2Polynomial[matrix.length];
+ GF2Polynomial[] inv = new GF2Polynomial[matrix.length];
+ GF2Polynomial dummy;
+ int i, j;
+ // initialize a as a copy of matrix and inv as E(inheitsmatrix)
+ for (i = 0; i < mDegree; i++)
+ {
+ try
+ {
+ a[i] = new GF2Polynomial(matrix[i]);
+ inv[i] = new GF2Polynomial(mDegree);
+ inv[i].setBit(mDegree - 1 - i);
+ }
+ catch (RuntimeException BDNEExc)
+ {
+ BDNEExc.printStackTrace();
+ }
+ }
+ // construct triangle matrix so that for each a[i] the first i bits are
+ // zero
+ for (i = 0; i < mDegree - 1; i++)
+ {
+ // find column where bit i is set
+ j = i;
+ while ((j < mDegree) && !a[j].testBit(mDegree - 1 - i))
+ {
+ j++;
+ }
+ if (j >= mDegree)
+ {
+ throw new RuntimeException(
+ "GF2nField.invertMatrix: Matrix cannot be inverted!");
+ }
+ if (i != j)
+ { // swap a[i]/a[j] and inv[i]/inv[j]
+ dummy = a[i];
+ a[i] = a[j];
+ a[j] = dummy;
+ dummy = inv[i];
+ inv[i] = inv[j];
+ inv[j] = dummy;
+ }
+ for (j = i + 1; j < mDegree; j++)
+ { // add column i to all columns>i
+ // having their i-th bit set
+ if (a[j].testBit(mDegree - 1 - i))
+ {
+ a[j].addToThis(a[i]);
+ inv[j].addToThis(inv[i]);
+ }
+ }
+ }
+ // construct Einheitsmatrix from a
+ for (i = mDegree - 1; i > 0; i--)
+ {
+ for (j = i - 1; j >= 0; j--)
+ { // eliminate the i-th bit in all
+ // columns < i
+ if (a[j].testBit(mDegree - 1 - i))
+ {
+ a[j].addToThis(a[i]);
+ inv[j].addToThis(inv[i]);
+ }
+ }
+ }
+ return inv;
+ }
+
+ /**
+ * Converts the given element in representation according to this field to a
+ * new element in representation according to B1 using the change-of-basis
+ * matrix calculated by computeCOBMatrix.
+ *
+ * @param elem the GF2nElement to convert
+ * @param basis the basis to convert elem to
+ * @return elem converted to a new element representation
+ * according to basis
+ * @throws DifferentFieldsException if elem cannot be converted according to
+ * basis.
+ * @see GF2nField#computeCOBMatrix
+ * @see GF2nField#getRandomRoot
+ * @see GF2nPolynomial
+ * @see "P1363 A.7 p109ff"
+ */
+ public final GF2nElement convert(GF2nElement elem, GF2nField basis)
+ throws RuntimeException
+ {
+ if (basis == this)
+ {
+ return (GF2nElement)elem.clone();
+ }
+ if (fieldPolynomial.equals(basis.fieldPolynomial))
+ {
+ return (GF2nElement)elem.clone();
+ }
+ if (mDegree != basis.mDegree)
+ {
+ throw new RuntimeException("GF2nField.convert: B1 has a"
+ + " different degree and thus cannot be coverted to!");
+ }
+
+ int i;
+ GF2Polynomial[] COBMatrix;
+ i = fields.indexOf(basis);
+ if (i == -1)
+ {
+ computeCOBMatrix(basis);
+ i = fields.indexOf(basis);
+ }
+ COBMatrix = (GF2Polynomial[])matrices.elementAt(i);
+
+ GF2nElement elemCopy = (GF2nElement)elem.clone();
+ if (elemCopy instanceof GF2nONBElement)
+ {
+ // remember: ONB treats its bits in reverse order
+ ((GF2nONBElement)elemCopy).reverseOrder();
+ }
+ GF2Polynomial bs = new GF2Polynomial(mDegree, elemCopy.toFlexiBigInt());
+ bs.expandN(mDegree);
+ GF2Polynomial result = new GF2Polynomial(mDegree);
+ for (i = 0; i < mDegree; i++)
+ {
+ if (bs.vectorMult(COBMatrix[i]))
+ {
+ result.setBit(mDegree - 1 - i);
+ }
+ }
+ if (basis instanceof GF2nPolynomialField)
+ {
+ return new GF2nPolynomialElement((GF2nPolynomialField)basis,
+ result);
+ }
+ else if (basis instanceof GF2nONBField)
+ {
+ GF2nONBElement res = new GF2nONBElement((GF2nONBField)basis,
+ result.toFlexiBigInt());
+ // TODO Remember: ONB treats its Bits in reverse order !!!
+ res.reverseOrder();
+ return res;
+ }
+ else
+ {
+ throw new RuntimeException(
+ "GF2nField.convert: B1 must be an instance of "
+ + "GF2nPolynomialField or GF2nONBField!");
+ }
+
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java
new file mode 100644
index 000000000..08d5beed7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java
@@ -0,0 +1,1154 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * This class implements an element of the finite field GF(2n ).
+ * It is represented in an optimal normal basis representation and holds the
+ * pointer mField to its corresponding field.
+ *
+ * @see GF2nField
+ * @see GF2nElement
+ */
+public class GF2nONBElement
+ extends GF2nElement
+{
+
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+
+ private static final long[] mBitmask = new long[]{0x0000000000000001L,
+ 0x0000000000000002L, 0x0000000000000004L, 0x0000000000000008L,
+ 0x0000000000000010L, 0x0000000000000020L, 0x0000000000000040L,
+ 0x0000000000000080L, 0x0000000000000100L, 0x0000000000000200L,
+ 0x0000000000000400L, 0x0000000000000800L, 0x0000000000001000L,
+ 0x0000000000002000L, 0x0000000000004000L, 0x0000000000008000L,
+ 0x0000000000010000L, 0x0000000000020000L, 0x0000000000040000L,
+ 0x0000000000080000L, 0x0000000000100000L, 0x0000000000200000L,
+ 0x0000000000400000L, 0x0000000000800000L, 0x0000000001000000L,
+ 0x0000000002000000L, 0x0000000004000000L, 0x0000000008000000L,
+ 0x0000000010000000L, 0x0000000020000000L, 0x0000000040000000L,
+ 0x0000000080000000L, 0x0000000100000000L, 0x0000000200000000L,
+ 0x0000000400000000L, 0x0000000800000000L, 0x0000001000000000L,
+ 0x0000002000000000L, 0x0000004000000000L, 0x0000008000000000L,
+ 0x0000010000000000L, 0x0000020000000000L, 0x0000040000000000L,
+ 0x0000080000000000L, 0x0000100000000000L, 0x0000200000000000L,
+ 0x0000400000000000L, 0x0000800000000000L, 0x0001000000000000L,
+ 0x0002000000000000L, 0x0004000000000000L, 0x0008000000000000L,
+ 0x0010000000000000L, 0x0020000000000000L, 0x0040000000000000L,
+ 0x0080000000000000L, 0x0100000000000000L, 0x0200000000000000L,
+ 0x0400000000000000L, 0x0800000000000000L, 0x1000000000000000L,
+ 0x2000000000000000L, 0x4000000000000000L, 0x8000000000000000L};
+
+ private static final long[] mMaxmask = new long[]{0x0000000000000001L,
+ 0x0000000000000003L, 0x0000000000000007L, 0x000000000000000FL,
+ 0x000000000000001FL, 0x000000000000003FL, 0x000000000000007FL,
+ 0x00000000000000FFL, 0x00000000000001FFL, 0x00000000000003FFL,
+ 0x00000000000007FFL, 0x0000000000000FFFL, 0x0000000000001FFFL,
+ 0x0000000000003FFFL, 0x0000000000007FFFL, 0x000000000000FFFFL,
+ 0x000000000001FFFFL, 0x000000000003FFFFL, 0x000000000007FFFFL,
+ 0x00000000000FFFFFL, 0x00000000001FFFFFL, 0x00000000003FFFFFL,
+ 0x00000000007FFFFFL, 0x0000000000FFFFFFL, 0x0000000001FFFFFFL,
+ 0x0000000003FFFFFFL, 0x0000000007FFFFFFL, 0x000000000FFFFFFFL,
+ 0x000000001FFFFFFFL, 0x000000003FFFFFFFL, 0x000000007FFFFFFFL,
+ 0x00000000FFFFFFFFL, 0x00000001FFFFFFFFL, 0x00000003FFFFFFFFL,
+ 0x00000007FFFFFFFFL, 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL,
+ 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL, 0x000000FFFFFFFFFFL,
+ 0x000001FFFFFFFFFFL, 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL,
+ 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL, 0x00003FFFFFFFFFFFL,
+ 0x00007FFFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL,
+ 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL, 0x000FFFFFFFFFFFFFL,
+ 0x001FFFFFFFFFFFFFL, 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL,
+ 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL, 0x03FFFFFFFFFFFFFFL,
+ 0x07FFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL,
+ 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL};
+
+ // mIBy64[j * 16 + i] = (j * 16 + i)/64
+ // i =
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ //
+ private static final int[] mIBY64 = new int[]{
+ // j =
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 8
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 9
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 10
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 11
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 12
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 13
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 14
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 15
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 16
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 17
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 18
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 19
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 20
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 21
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 22
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 // 23
+ };
+
+ private static final int MAXLONG = 64;
+
+ /**
+ * holds the lenght of the polynomial with 64 bit sized fields.
+ */
+ private int mLength;
+
+ /**
+ * holds the value of mDeg % MAXLONG.
+ */
+ private int mBit;
+
+ /**
+ * holds this element in ONB representation.
+ */
+ private long[] mPol;
+
+ // /////////////////////////////////////////////////////////////////////
+ // constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Construct a random element over the field gf2n, using the
+ * specified source of randomness.
+ *
+ * @param gf2n the field
+ * @param rand the source of randomness
+ */
+ public GF2nONBElement(GF2nONBField gf2n, Random rand)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ if (mLength > 1)
+ {
+ for (int j = 0; j < mLength - 1; j++)
+ {
+ mPol[j] = rand.nextLong();
+ }
+ long last = rand.nextLong();
+ mPol[mLength - 1] = last >>> (MAXLONG - mBit);
+ }
+ else
+ {
+ mPol[0] = rand.nextLong();
+ mPol[0] = mPol[0] >>> (MAXLONG - mBit);
+ }
+ }
+
+ /**
+ * Construct a new GF2nONBElement from its encoding.
+ *
+ * @param gf2n the field
+ * @param e the encoded element
+ */
+ public GF2nONBElement(GF2nONBField gf2n, byte[] e)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ assign(e);
+ }
+
+ /**
+ * Construct the element of the field gf2n with the specified
+ * value val.
+ *
+ * @param gf2n the field
+ * @param val the value represented by a BigInteger
+ */
+ public GF2nONBElement(GF2nONBField gf2n, BigInteger val)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ assign(val);
+ }
+
+ /**
+ * Construct the element of the field gf2n with the specified
+ * value val.
+ *
+ * @param gf2n the field
+ * @param val the value in ONB representation
+ */
+ private GF2nONBElement(GF2nONBField gf2n, long[] val)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = val;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Copy constructor.
+ *
+ * @param gf2n the field
+ */
+ public GF2nONBElement(GF2nONBElement gf2n)
+ {
+
+ mField = gf2n.mField;
+ mDegree = mField.getDegree();
+ mLength = ((GF2nONBField)mField).getONBLength();
+ mBit = ((GF2nONBField)mField).getONBBit();
+ mPol = new long[mLength];
+ assign(gf2n.getElement());
+ }
+
+ /**
+ * Create a new GF2nONBElement by cloning this GF2nPolynomialElement.
+ *
+ * @return a copy of this element
+ */
+ public Object clone()
+ {
+ return new GF2nONBElement(this);
+ }
+
+ /**
+ * Create the zero element.
+ *
+ * @param gf2n the finite field
+ * @return the zero element in the given finite field
+ */
+ public static GF2nONBElement ZERO(GF2nONBField gf2n)
+ {
+ long[] polynomial = new long[gf2n.getONBLength()];
+ return new GF2nONBElement(gf2n, polynomial);
+ }
+
+ /**
+ * Create the one element.
+ *
+ * @param gf2n the finite field
+ * @return the one element in the given finite field
+ */
+ public static GF2nONBElement ONE(GF2nONBField gf2n)
+ {
+ int mLength = gf2n.getONBLength();
+ long[] polynomial = new long[mLength];
+
+ // fill mDegree coefficients with one's
+ for (int i = 0; i < mLength - 1; i++)
+ {
+ polynomial[i] = 0xffffffffffffffffL;
+ }
+ polynomial[mLength - 1] = mMaxmask[gf2n.getONBBit() - 1];
+
+ return new GF2nONBElement(gf2n, polynomial);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * assigns to this element the zero element
+ */
+ void assignZero()
+ {
+ mPol = new long[mLength];
+ }
+
+ /**
+ * assigns to this element the one element
+ */
+ void assignOne()
+ {
+ // fill mDegree coefficients with one's
+ for (int i = 0; i < mLength - 1; i++)
+ {
+ mPol[i] = 0xffffffffffffffffL;
+ }
+ mPol[mLength - 1] = mMaxmask[mBit - 1];
+ }
+
+ /**
+ * assigns to this element the value val.
+ *
+ * @param val the value represented by a BigInteger
+ */
+ private void assign(BigInteger val)
+ {
+ assign(val.toByteArray());
+ }
+
+ /**
+ * assigns to this element the value val.
+ *
+ * @param val the value in ONB representation
+ */
+ private void assign(long[] val)
+ {
+ System.arraycopy(val, 0, mPol, 0, mLength);
+ }
+
+ /**
+ * assigns to this element the value val. First: inverting the
+ * order of val into reversed[]. That means: reversed[0] = val[length - 1],
+ * ..., reversed[reversed.length - 1] = val[0]. Second: mPol[0] = sum{i = 0,
+ * ... 7} (val[i]<<(i*8)) .... mPol[1] = sum{i = 8, ... 15} (val[i]<<(i*8))
+ *
+ * @param val the value in ONB representation
+ */
+ private void assign(byte[] val)
+ {
+ int j;
+ mPol = new long[mLength];
+ for (j = 0; j < val.length; j++)
+ {
+ mPol[j >>> 3] |= (val[val.length - 1 - j] & 0x00000000000000ffL) << ((j & 0x07) << 3);
+ }
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return true if this is the zero element
+ */
+ public boolean isZero()
+ {
+
+ boolean result = true;
+
+ for (int i = 0; i < mLength && result; i++)
+ {
+ result = result && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0);
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks whether this element is one.
+ *
+ * @return true if this is the one element
+ */
+ public boolean isOne()
+ {
+
+ boolean result = true;
+
+ for (int i = 0; i < mLength - 1 && result; i++)
+ {
+ result = result
+ && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0xFFFFFFFFFFFFFFFFL);
+ }
+
+ if (result)
+ {
+ result = result
+ && ((mPol[mLength - 1] & mMaxmask[mBit - 1]) == mMaxmask[mBit - 1]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare this element with another object.
+ *
+ * @param other the other object
+ * @return true if the two objects are equal, false
+ * otherwise
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nONBElement))
+ {
+ return false;
+ }
+
+ GF2nONBElement otherElem = (GF2nONBElement)other;
+
+ for (int i = 0; i < mLength; i++)
+ {
+ if (mPol[i] != otherElem.mPol[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the hash code of this element
+ */
+ public int hashCode()
+ {
+ return mPol.hashCode();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns whether the highest bit of the bit representation is set
+ *
+ * @return true, if the highest bit of mPol is set, false, otherwise
+ */
+ public boolean testRightmostBit()
+ {
+ // due to the reverse bit order (compared to 1363) this method returns
+ // the value of the leftmost bit
+ return (mPol[mLength - 1] & mBitmask[mBit - 1]) != 0L;
+ }
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set. Warning:
+ * GF2nONBElement currently stores its bits in reverse order (compared to
+ * 1363) !!!
+ *
+ * @param index the index of the bit to test
+ * @return true if the indexed bit of mPol is set, false
+ * otherwise.
+ */
+ boolean testBit(int index)
+ {
+ if (index < 0 || index > mDegree)
+ {
+ return false;
+ }
+ long test = mPol[index >>> 6] & mBitmask[index & 0x3f];
+ return test != 0x0L;
+ }
+
+ /**
+ * @return this element in its ONB representation
+ */
+ private long[] getElement()
+ {
+
+ long[] result = new long[mPol.length];
+ System.arraycopy(mPol, 0, result, 0, mPol.length);
+
+ return result;
+ }
+
+ /**
+ * Returns the ONB representation of this element. The Bit-Order is
+ * exchanged (according to 1363)!
+ *
+ * @return this element in its representation and reverse bit-order
+ */
+ private long[] getElementReverseOrder()
+ {
+ long[] result = new long[mPol.length];
+ for (int i = 0; i < mDegree; i++)
+ {
+ if (testBit(mDegree - i - 1))
+ {
+ result[i >>> 6] |= mBitmask[i & 0x3f];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Reverses the bit-order in this element(according to 1363). This is a
+ * hack!
+ */
+ void reverseOrder()
+ {
+ mPol = getElementReverseOrder();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the sum of this element and addend.
+ *
+ * @param addend the addend
+ * @return this + other (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement add(GFElement addend)
+ throws RuntimeException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.addToThis(addend);
+ return result;
+ }
+
+ /**
+ * Compute this + addend (overwrite this).
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void addToThis(GFElement addend)
+ throws RuntimeException
+ {
+ if (!(addend instanceof GF2nONBElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nONBElement)addend).mField))
+ {
+ throw new RuntimeException();
+ }
+
+ for (int i = 0; i < mLength; i++)
+ {
+ mPol[i] ^= ((GF2nONBElement)addend).mPol[i];
+ }
+ }
+
+ /**
+ * returns this element + 1.
+ *
+ * @return this + 1
+ */
+ public GF2nElement increase()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * increases this element.
+ */
+ public void increaseThis()
+ {
+ addToThis(ONE((GF2nONBField)mField));
+ }
+
+ /**
+ * Compute the product of this element and factor.
+ *
+ * @param factor the factor
+ * @return this * factor (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement multiply(GFElement factor)
+ throws RuntimeException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.multiplyThisBy(factor);
+ return result;
+ }
+
+ /**
+ * Compute this * factor (overwrite this).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void multiplyThisBy(GFElement factor)
+ throws RuntimeException
+ {
+
+ if (!(factor instanceof GF2nONBElement))
+ {
+ throw new RuntimeException("The elements have different"
+ + " representation: not yet" + " implemented");
+ }
+ if (!mField.equals(((GF2nONBElement)factor).mField))
+ {
+ throw new RuntimeException();
+ }
+
+ if (equals(factor))
+ {
+ squareThis();
+ }
+ else
+ {
+
+ long[] a = mPol;
+ long[] b = ((GF2nONBElement)factor).mPol;
+ long[] c = new long[mLength];
+
+ int[][] m = ((GF2nONBField)mField).mMult;
+
+ int degf, degb, s, fielda, fieldb, bita, bitb;
+ degf = mLength - 1;
+ degb = mBit - 1;
+ s = 0;
+
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ long TWOTODEGB = mBitmask[degb];
+
+ boolean old, now;
+
+ // the product c of a and b (a*b = c) is calculated in mDegree
+ // cicles
+ // in every cicle one coefficient of c is calculated and stored
+ // k indicates the coefficient
+ //
+ for (int k = 0; k < mDegree; k++)
+ {
+
+ s = 0;
+
+ for (int i = 0; i < mDegree; i++)
+ {
+
+ // fielda = i / MAXLONG
+ //
+ fielda = mIBY64[i];
+
+ // bita = i % MAXLONG
+ //
+ bita = i & (MAXLONG - 1);
+
+ // fieldb = m[i][0] / MAXLONG
+ //
+ fieldb = mIBY64[m[i][0]];
+
+ // bitb = m[i][0] % MAXLONG
+ //
+ bitb = m[i][0] & (MAXLONG - 1);
+
+ if ((a[fielda] & mBitmask[bita]) != 0)
+ {
+
+ if ((b[fieldb] & mBitmask[bitb]) != 0)
+ {
+ s ^= 1;
+ }
+
+ if (m[i][1] != -1)
+ {
+
+ // fieldb = m[i][1] / MAXLONG
+ //
+ fieldb = mIBY64[m[i][1]];
+
+ // bitb = m[i][1] % MAXLONG
+ //
+ bitb = m[i][1] & (MAXLONG - 1);
+
+ if ((b[fieldb] & mBitmask[bitb]) != 0)
+ {
+ s ^= 1;
+ }
+
+ }
+ }
+ }
+ fielda = mIBY64[k];
+ bita = k & (MAXLONG - 1);
+
+ if (s != 0)
+ {
+ c[fielda] ^= mBitmask[bita];
+ }
+
+ // Circular shift of x and y one bit to the right,
+ // respectively.
+
+ if (mLength > 1)
+ {
+
+ // Shift x.
+ //
+ old = (a[degf] & 1) == 1;
+
+ for (int i = degf - 1; i >= 0; i--)
+ {
+ now = (a[i] & 1) != 0;
+
+ a[i] = a[i] >>> 1;
+
+ if (old)
+ {
+ a[i] ^= TWOTOMAXLONGM1;
+ }
+
+ old = now;
+ }
+ a[degf] = a[degf] >>> 1;
+
+ if (old)
+ {
+ a[degf] ^= TWOTODEGB;
+ }
+
+ // Shift y.
+ //
+ old = (b[degf] & 1) == 1;
+
+ for (int i = degf - 1; i >= 0; i--)
+ {
+ now = (b[i] & 1) != 0;
+
+ b[i] = b[i] >>> 1;
+
+ if (old)
+ {
+ b[i] ^= TWOTOMAXLONGM1;
+ }
+
+ old = now;
+ }
+
+ b[degf] = b[degf] >>> 1;
+
+ if (old)
+ {
+ b[degf] ^= TWOTODEGB;
+ }
+ }
+ else
+ {
+ old = (a[0] & 1) == 1;
+ a[0] = a[0] >>> 1;
+
+ if (old)
+ {
+ a[0] ^= TWOTODEGB;
+ }
+
+ old = (b[0] & 1) == 1;
+ b[0] = b[0] >>> 1;
+
+ if (old)
+ {
+ b[0] ^= TWOTODEGB;
+ }
+ }
+ }
+ assign(c);
+ }
+ }
+
+ /**
+ * returns this element to the power of 2.
+ *
+ * @return this2
+ */
+ public GF2nElement square()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.squareThis();
+ return result;
+ }
+
+ /**
+ * squares this element.
+ */
+ public void squareThis()
+ {
+
+ long[] pol = getElement();
+
+ int f = mLength - 1;
+ int b = mBit - 1;
+
+ // Shift the coefficients one bit to the left.
+ //
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ boolean old, now;
+
+ old = (pol[f] & mBitmask[b]) != 0;
+
+ for (int i = 0; i < f; i++)
+ {
+
+ now = (pol[i] & TWOTOMAXLONGM1) != 0;
+
+ pol[i] = pol[i] << 1;
+
+ if (old)
+ {
+ pol[i] ^= 1;
+ }
+
+ old = now;
+ }
+ now = (pol[f] & mBitmask[b]) != 0;
+
+ pol[f] = pol[f] << 1;
+
+ if (old)
+ {
+ pol[f] ^= 1;
+ }
+
+ // Set the bit with index mDegree to zero.
+ //
+ if (now)
+ {
+ pol[f] ^= mBitmask[b + 1];
+ }
+
+ assign(pol);
+ }
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return this-1 (newly created)
+ * @throws ArithmeticException if this is the zero element.
+ */
+ public GFElement invert()
+ throws ArithmeticException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.invertThis();
+ return result;
+ }
+
+ /**
+ * Multiplicatively invert of this element (overwrite this).
+ *
+ * @throws ArithmeticException if this is the zero element.
+ */
+ public void invertThis()
+ throws ArithmeticException
+ {
+
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ int r = 31; // mDegree kann nur 31 Bits lang sein!!!
+
+ // Bitlaenge von mDegree:
+ for (boolean found = false; !found && r >= 0; r--)
+ {
+
+ if (((mDegree - 1) & mBitmask[r]) != 0)
+ {
+ found = true;
+ }
+ }
+ r++;
+
+ GF2nElement m = ZERO((GF2nONBField)mField);
+ GF2nElement n = new GF2nONBElement(this);
+
+ int k = 1;
+
+ for (int i = r - 1; i >= 0; i--)
+ {
+ m = (GF2nElement)n.clone();
+ for (int j = 1; j <= k; j++)
+ {
+ m.squareThis();
+ }
+
+ n.multiplyThisBy(m);
+
+ k <<= 1;
+ if (((mDegree - 1) & mBitmask[i]) != 0)
+ {
+ n.squareThis();
+
+ n.multiplyThisBy(this);
+
+ k++;
+ }
+ }
+ n.squareThis();
+ }
+
+ /**
+ * returns the root ofthis element.
+ *
+ * @return this1/2
+ */
+ public GF2nElement squareRoot()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.squareRootThis();
+ return result;
+ }
+
+ /**
+ * square roots this element.
+ */
+ public void squareRootThis()
+ {
+
+ long[] pol = getElement();
+
+ int f = mLength - 1;
+ int b = mBit - 1;
+
+ // Shift the coefficients one bit to the right.
+ //
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ boolean old, now;
+
+ old = (pol[0] & 1) != 0;
+
+ for (int i = f; i >= 0; i--)
+ {
+ now = (pol[i] & 1) != 0;
+ pol[i] = pol[i] >>> 1;
+
+ if (old)
+ {
+ if (i == f)
+ {
+ pol[i] ^= mBitmask[b];
+ }
+ else
+ {
+ pol[i] ^= TWOTOMAXLONGM1;
+ }
+ }
+ old = now;
+ }
+ assign(pol);
+ }
+
+ /**
+ * Returns the trace of this element.
+ *
+ * @return the trace of this element
+ */
+ public int trace()
+ {
+
+ // trace = sum of coefficients
+ //
+
+ int result = 0;
+
+ int max = mLength - 1;
+
+ for (int i = 0; i < max; i++)
+ {
+
+ for (int j = 0; j < MAXLONG; j++)
+ {
+
+ if ((mPol[i] & mBitmask[j]) != 0)
+ {
+ result ^= 1;
+ }
+ }
+ }
+
+ int b = mBit;
+
+ for (int j = 0; j < b; j++)
+ {
+
+ if ((mPol[max] & mBitmask[j]) != 0)
+ {
+ result ^= 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Solves a quadratic equation.
+ * Let z2 + z = this. Then this method returns z.
+ *
+ * @return z with z2 + z = this
+ * @throws NoSolutionException if z2 + z = this does not have a
+ * solution
+ */
+ public GF2nElement solveQuadraticEquation()
+ throws RuntimeException
+ {
+
+ if (trace() == 1)
+ {
+ throw new RuntimeException();
+ }
+
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ long ZERO = 0L;
+ long ONE = 1L;
+
+ long[] p = new long[mLength];
+ long z = 0L;
+ int j = 1;
+ for (int i = 0; i < mLength - 1; i++)
+ {
+
+ for (j = 1; j < MAXLONG; j++)
+ {
+
+ //
+ if (!((((mBitmask[j] & mPol[i]) != ZERO) && ((z & mBitmask[j - 1]) != ZERO)) || (((mPol[i] & mBitmask[j]) == ZERO) && ((z & mBitmask[j - 1]) == ZERO))))
+ {
+ z ^= mBitmask[j];
+ }
+ }
+ p[i] = z;
+
+ if (((TWOTOMAXLONGM1 & z) != ZERO && (ONE & mPol[i + 1]) == ONE)
+ || ((TWOTOMAXLONGM1 & z) == ZERO && (ONE & mPol[i + 1]) == ZERO))
+ {
+ z = ZERO;
+ }
+ else
+ {
+ z = ONE;
+ }
+ }
+
+ int b = mDegree & (MAXLONG - 1);
+
+ long LASTLONG = mPol[mLength - 1];
+
+ for (j = 1; j < b; j++)
+ {
+ if (!((((mBitmask[j] & LASTLONG) != ZERO) && ((mBitmask[j - 1] & z) != ZERO)) || (((mBitmask[j] & LASTLONG) == ZERO) && ((mBitmask[j - 1] & z) == ZERO))))
+ {
+ z ^= mBitmask[j];
+ }
+ }
+ p[mLength - 1] = z;
+ return new GF2nONBElement((GF2nONBField)mField, p);
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a String representation of this element.
+ *
+ * @return String representation of this element with the specified radix
+ */
+ public String toString()
+ {
+ return toString(16);
+ }
+
+ /**
+ * Returns a String representation of this element. radix
+ * specifies the radix of the String representation.
+ * NOTE: ONLY radix = 2 or radix = 16 IS IMPLEMENTED>
+ *
+ * @param radix specifies the radix of the String representation
+ * @return String representation of this element with the specified radix
+ */
+ public String toString(int radix)
+ {
+ String s = "";
+
+ long[] a = getElement();
+ int b = mBit;
+
+ if (radix == 2)
+ {
+
+ for (int j = b - 1; j >= 0; j--)
+ {
+ if ((a[a.length - 1] & ((long)1 << j)) == 0)
+ {
+ s += "0";
+ }
+ else
+ {
+ s += "1";
+ }
+ }
+
+ for (int i = a.length - 2; i >= 0; i--)
+ {
+ for (int j = MAXLONG - 1; j >= 0; j--)
+ {
+ if ((a[i] & mBitmask[j]) == 0)
+ {
+ s += "0";
+ }
+ else
+ {
+ s += "1";
+ }
+ }
+ }
+ }
+ else if (radix == 16)
+ {
+ final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ for (int i = a.length - 1; i >= 0; i--)
+ {
+ s += HEX_CHARS[(int)(a[i] >>> 60) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 56) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 52) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 48) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 44) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 40) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 36) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 32) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 28) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 24) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 20) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 16) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 12) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 8) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 4) & 0x0f];
+ s += HEX_CHARS[(int)(a[i]) & 0x0f];
+ s += " ";
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Returns this element as FlexiBigInt. The conversion is P1363-conform.
+ *
+ * @return this element as BigInteger
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ /** @todo this method does not reverse the bit-order as it should!!! */
+
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Returns this element as byte array. The conversion is P1363-conform.
+ *
+ * @return this element as byte array
+ */
+ public byte[] toByteArray()
+ {
+ /** @todo this method does not reverse the bit-order as it should!!! */
+
+ int k = ((mDegree - 1) >> 3) + 1;
+ byte[] result = new byte[k];
+ int i;
+ for (i = 0; i < k; i++)
+ {
+ result[k - i - 1] = (byte)((mPol[i >>> 3] & (0x00000000000000ffL << ((i & 0x07) << 3))) >>> ((i & 0x07) << 3));
+ }
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java
new file mode 100644
index 000000000..60e5be414
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java
@@ -0,0 +1,546 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.util.Random;
+import java.util.Vector;
+
+
+/**
+ * This class implements the abstract class GF2nField for ONB
+ * representation. It computes the fieldpolynomial, multiplication matrix and
+ * one of its roots mONBRoot, (see for example Certicoms Whitepapers).
+ * GF2nField is used by GF2nONBElement which implements the elements of this
+ * field.
+ *
+ * @see GF2nField
+ * @see GF2nONBElement
+ */
+public class GF2nONBField
+ extends GF2nField
+{
+
+ // ///////////////////////////////////////////////////////////////////
+ // Hashtable for irreducible normal polynomials //
+ // ///////////////////////////////////////////////////////////////////
+
+ // i*5 + 0 i*5 + 1 i*5 + 2 i*5 + 3 i*5 + 4
+ /*
+ * private static int[][] mNB = {{0, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0},
+ * {1, 0, 0}, // i = 0 {2, 0, 0}, {1, 0, 0}, {1, 0, 0}, {4, 3, 1}, {1, 0,
+ * 0}, // i = 1 {3, 0, 0}, {2, 0, 0}, {3, 0, 0}, {4, 3, 1}, {5, 0, 0}, // i =
+ * 2 {1, 0, 0}, {5, 3, 1}, {3, 0, 0}, {3, 0, 0}, {5, 2, 1}, // i = 3 {3, 0,
+ * 0}, {2, 0, 0}, {1, 0, 0}, {5, 0, 0}, {4, 3, 1}, // i = 4 {3, 0, 0}, {4,
+ * 3, 1}, {5, 2, 1}, {1, 0, 0}, {2, 0, 0}, // i = 5 {1, 0, 0}, {3, 0, 0},
+ * {7, 3, 2}, {10, 0, 0}, {7, 0, 0}, // i = 6 {2, 0, 0}, {9, 0, 0}, {6, 4,
+ * 1}, {6, 5, 1}, {4, 0, 0}, // i = 7 {5, 4, 3}, {3, 0, 0}, {7, 0, 0}, {6,
+ * 4, 3}, {5, 0, 0}, // i = 8 {4, 3, 1}, {1, 0, 0}, {5, 0, 0}, {5, 3, 2},
+ * {9, 0, 0}, // i = 9 {4, 3, 2}, {6, 3, 1}, {3, 0, 0}, {6, 2, 1}, {9, 0,
+ * 0}, // i = 10 {7, 0, 0}, {7, 4, 2}, {4, 0, 0}, {19, 0, 0}, {7, 4, 2}, //
+ * i = 11 {1, 0, 0}, {5, 2, 1}, {29, 0, 0}, {1, 0, 0}, {4, 3, 1}, // i = 12
+ * {18, 0, 0}, {3, 0, 0}, {5, 2, 1}, {9, 0, 0}, {6, 5, 2}, // i = 13 {5, 3,
+ * 1}, {6, 0, 0}, {10, 9, 3}, {25, 0, 0}, {35, 0, 0}, // i = 14 {6, 3, 1},
+ * {21, 0, 0}, {6, 5, 2}, {6, 5, 3}, {9, 0, 0}, // i = 15 {9, 4, 2}, {4, 0,
+ * 0}, {8, 3, 1}, {7, 4, 2}, {5, 0, 0}, // i = 16 {8, 2, 1}, {21, 0, 0},
+ * {13, 0, 0}, {7, 6, 2}, {38, 0, 0}, // i = 17 {27, 0, 0}, {8, 5, 1}, {21,
+ * 0, 0}, {2, 0, 0}, {21, 0, 0}, // i = 18 {11, 0, 0}, {10, 9, 6}, {6, 0,
+ * 0}, {11, 0, 0}, {6, 3, 1}, // i = 19 {15, 0, 0}, {7, 6, 1}, {29, 0, 0},
+ * {9, 0, 0}, {4, 3, 1}, // i = 20 {4, 0, 0}, {15, 0, 0}, {9, 7, 4}, {17, 0,
+ * 0}, {5, 4, 2}, // i = 21 {33, 0, 0}, {10, 0, 0}, {5, 4, 3}, {9, 0, 0},
+ * {5, 3, 2}, // i = 22 {8, 7, 5}, {4, 2, 1}, {5, 2, 1}, {33, 0, 0}, {8, 0,
+ * 0}, // i = 23 {4, 3, 1}, {18, 0, 0}, {6, 2, 1}, {2, 0, 0}, {19, 0, 0}, //
+ * i = 24 {7, 6, 5}, {21, 0, 0}, {1, 0, 0}, {7, 2, 1}, {5, 0, 0}, // i = 25
+ * {3, 0, 0}, {8, 3, 2}, {17, 0, 0}, {9, 8, 2}, {57, 0, 0}, // i = 26 {11,
+ * 0, 0}, {5, 3, 2}, {21, 0, 0}, {8, 7, 1}, {8, 5, 3}, // i = 27 {15, 0, 0},
+ * {10, 4, 1}, {21, 0, 0}, {5, 3, 2}, {7, 4, 2}, // i = 28 {52, 0, 0}, {71,
+ * 0, 0}, {14, 0, 0}, {27, 0, 0}, {10, 9, 7}, // i = 29 {53, 0, 0}, {3, 0,
+ * 0}, {6, 3, 2}, {1, 0, 0}, {15, 0, 0}, // i = 30 {62, 0, 0}, {9, 0, 0},
+ * {6, 5, 2}, {8, 6, 5}, {31, 0, 0}, // i = 31 {5, 3, 2}, {18, 0, 0 }, {27,
+ * 0, 0}, {7, 6, 3}, {10, 8, 7}, // i = 32 {9, 8, 3}, {37, 0, 0}, {6, 0, 0},
+ * {15, 3, 2}, {34, 0, 0}, // i = 33 {11, 0, 0}, {6, 5, 2}, {1, 0, 0}, {8,
+ * 5, 2}, {13, 0, 0}, // i = 34 {6, 0, 0}, {11, 3, 2}, {8, 0, 0}, {31, 0,
+ * 0}, {4, 2, 1}, // i = 35 {3, 0, 0}, {7, 6, 1}, {81, 0, 0}, {56, 0, 0},
+ * {9, 8, 7}, // i = 36 {24, 0, 0}, {11, 0, 0}, {7, 6, 5}, {6, 5, 2}, {6, 5,
+ * 2}, // i = 37 {8, 7, 6}, {9, 0, 0}, {7, 2, 1}, {15, 0, 0}, {87, 0, 0}, //
+ * i = 38 {8, 3, 2}, {3, 0, 0}, {9, 4, 2}, {9, 0, 0}, {34, 0, 0}, // i = 39
+ * {5, 3, 2}, {14, 0, 0}, {55, 0, 0}, {8, 7, 1}, {27, 0, 0}, // i = 40 {9,
+ * 5, 2}, {10, 9, 5}, {43, 0, 0}, {8, 6, 2}, {6, 0, 0}, // i = 41 {7, 0, 0},
+ * {11, 10, 8}, {105, 0, 0}, {6, 5, 2}, {73, 0, 0}}; // i = 42
+ */
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+ private static final int MAXLONG = 64;
+
+ /**
+ * holds the length of the array-representation of degree mDegree.
+ */
+ private int mLength;
+
+ /**
+ * holds the number of relevant bits in mONBPol[mLength-1].
+ */
+ private int mBit;
+
+ /**
+ * holds the type of mONB
+ */
+ private int mType;
+
+ /**
+ * holds the multiplication matrix
+ */
+ int[][] mMult;
+
+ // /////////////////////////////////////////////////////////////////////
+ // constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * constructs an instance of the finite field with 2deg
+ * elements and characteristic 2.
+ *
+ * @param deg -
+ * the extention degree of this field
+ * @throws NoSuchBasisException if an ONB-implementation other than type 1 or type 2 is
+ * requested.
+ */
+ public GF2nONBField(int deg)
+ throws RuntimeException
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+
+ mDegree = deg;
+ mLength = mDegree / MAXLONG;
+ mBit = mDegree & (MAXLONG - 1);
+ if (mBit == 0)
+ {
+ mBit = MAXLONG;
+ }
+ else
+ {
+ mLength++;
+ }
+
+ computeType();
+
+ // only ONB-implementations for type 1 and type 2
+ //
+ if (mType < 3)
+ {
+ mMult = new int[mDegree][2];
+ for (int i = 0; i < mDegree; i++)
+ {
+ mMult[i][0] = -1;
+ mMult[i][1] = -1;
+ }
+ computeMultMatrix();
+ }
+ else
+ {
+ throw new RuntimeException("\nThe type of this field is "
+ + mType);
+ }
+ computeFieldPolynomial();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ int getONBLength()
+ {
+ return mLength;
+ }
+
+ int getONBBit()
+ {
+ return mBit;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Computes a random root of the given polynomial.
+ *
+ * @param polynomial a polynomial
+ * @return a random root of the polynomial
+ * @see "P1363 A.5.6, p103f"
+ */
+ protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
+ {
+ // We are in B1!!!
+ GF2nPolynomial c;
+ GF2nPolynomial ut;
+ GF2nElement u;
+ GF2nPolynomial h;
+ int hDegree;
+ // 1. Set g(t) <- f(t)
+ GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
+ int gDegree = g.getDegree();
+ int i;
+
+ // 2. while deg(g) > 1
+ while (gDegree > 1)
+ {
+ do
+ {
+ // 2.1 choose random u (element of) GF(2^m)
+ u = new GF2nONBElement(this, new Random());
+ ut = new GF2nPolynomial(2, GF2nONBElement.ZERO(this));
+ // 2.2 Set c(t) <- ut
+ ut.set(1, u);
+ c = new GF2nPolynomial(ut);
+ // 2.3 For i from 1 to m-1 do
+ for (i = 1; i <= mDegree - 1; i++)
+ {
+ // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
+ c = c.multiplyAndReduce(c, g);
+ c = c.add(ut);
+ }
+ // 2.4 set h(t) <- GCD(c(t), g(t))
+ h = c.gcd(g);
+ // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
+ // step 2.1
+ hDegree = h.getDegree();
+ gDegree = g.getDegree();
+ }
+ while ((hDegree == 0) || (hDegree == gDegree));
+ // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
+ if ((hDegree << 1) > gDegree)
+ {
+ g = g.quotient(h);
+ }
+ else
+ {
+ // ... else g(t) <- h(t)
+ g = new GF2nPolynomial(h);
+ }
+ gDegree = g.getDegree();
+ }
+ // 3. Output g(0)
+ return g.at(0);
+
+ }
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected void computeCOBMatrix(GF2nField B1)
+ {
+ // we are in B0 here!
+ if (mDegree != B1.mDegree)
+ {
+ throw new IllegalArgumentException(
+ "GF2nField.computeCOBMatrix: B1 has a "
+ + "different degree and thus cannot be coverted to!");
+ }
+ int i, j;
+ GF2nElement[] gamma;
+ GF2nElement u;
+ GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < mDegree; i++)
+ {
+ COBMatrix[i] = new GF2Polynomial(mDegree);
+ }
+
+ // find Random Root
+ do
+ {
+ // u is in representation according to B1
+ u = B1.getRandomRoot(fieldPolynomial);
+ }
+ while (u.isZero());
+
+ gamma = new GF2nPolynomialElement[mDegree];
+ // build gamma matrix by squaring
+ gamma[0] = (GF2nElement)u.clone();
+ for (i = 1; i < mDegree; i++)
+ {
+ gamma[i] = gamma[i - 1].square();
+ }
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ if (gamma[i].testBit(j))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+
+ fields.addElement(B1);
+ matrices.addElement(COBMatrix);
+ B1.fields.addElement(this);
+ B1.matrices.addElement(invertMatrix(COBMatrix));
+ }
+
+ /**
+ * Computes the field polynomial for a ONB according to IEEE 1363 A.7.2
+ * (p110f).
+ *
+ * @see "P1363 A.7.2, p110f"
+ */
+ protected void computeFieldPolynomial()
+ {
+ if (mType == 1)
+ {
+ fieldPolynomial = new GF2Polynomial(mDegree + 1, "ALL");
+ }
+ else if (mType == 2)
+ {
+ // 1. q = 1
+ GF2Polynomial q = new GF2Polynomial(mDegree + 1, "ONE");
+ // 2. p = t+1
+ GF2Polynomial p = new GF2Polynomial(mDegree + 1, "X");
+ p.addToThis(q);
+ GF2Polynomial r;
+ int i;
+ // 3. for i = 1 to (m-1) do
+ for (i = 1; i < mDegree; i++)
+ {
+ // r <- q
+ r = q;
+ // q <- p
+ q = p;
+ // p = tq+r
+ p = q.shiftLeft();
+ p.addToThis(r);
+ }
+ fieldPolynomial = p;
+ }
+ }
+
+ /**
+ * Compute the inverse of a matrix a.
+ *
+ * @param a the matrix
+ * @return a-1
+ */
+ int[][] invMatrix(int[][] a)
+ {
+
+ int[][] A = new int[mDegree][mDegree];
+ A = a;
+ int[][] inv = new int[mDegree][mDegree];
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ inv[i][i] = 1;
+ }
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ for (int j = i; j < mDegree; j++)
+ {
+ A[mDegree - 1 - i][j] = A[i][i];
+ }
+ }
+ return null;
+ }
+
+ private void computeType()
+ throws RuntimeException
+ {
+ if ((mDegree & 7) == 0)
+ {
+ throw new RuntimeException(
+ "The extension degree is divisible by 8!");
+ }
+ // checking for the type
+ int s = 0;
+ int k = 0;
+ mType = 1;
+ for (int d = 0; d != 1; mType++)
+ {
+ s = mType * mDegree + 1;
+ if (IntegerFunctions.isPrime(s))
+ {
+ k = IntegerFunctions.order(2, s);
+ d = IntegerFunctions.gcd(mType * mDegree / k, mDegree);
+ }
+ }
+ mType--;
+ if (mType == 1)
+ {
+ s = (mDegree << 1) + 1;
+ if (IntegerFunctions.isPrime(s))
+ {
+ k = IntegerFunctions.order(2, s);
+ int d = IntegerFunctions.gcd((mDegree << 1) / k, mDegree);
+ if (d == 1)
+ {
+ mType++;
+ }
+ }
+ }
+ }
+
+ private void computeMultMatrix()
+ {
+
+ if ((mType & 7) != 0)
+ {
+ int p = mType * mDegree + 1;
+
+ // compute sequence F[1] ... F[p-1] via A.3.7. of 1363.
+ // F[0] will not be filled!
+ //
+ int[] F = new int[p];
+
+ int u;
+ if (mType == 1)
+ {
+ u = 1;
+ }
+ else if (mType == 2)
+ {
+ u = p - 1;
+ }
+ else
+ {
+ u = elementOfOrder(mType, p);
+ }
+
+ int w = 1;
+ int n;
+ for (int j = 0; j < mType; j++)
+ {
+ n = w;
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ F[n] = i;
+ n = (n << 1) % p;
+ if (n < 0)
+ {
+ n += p;
+ }
+ }
+ w = u * w % p;
+ if (w < 0)
+ {
+ w += p;
+ }
+ }
+
+ // building the matrix (mDegree * 2)
+ //
+ if (mType == 1)
+ {
+ for (int k = 1; k < p - 1; k++)
+ {
+ if (mMult[F[k + 1]][0] == -1)
+ {
+ mMult[F[k + 1]][0] = F[p - k];
+ }
+ else
+ {
+ mMult[F[k + 1]][1] = F[p - k];
+ }
+ }
+
+ int m_2 = mDegree >> 1;
+ for (int k = 1; k <= m_2; k++)
+ {
+
+ if (mMult[k - 1][0] == -1)
+ {
+ mMult[k - 1][0] = m_2 + k - 1;
+ }
+ else
+ {
+ mMult[k - 1][1] = m_2 + k - 1;
+ }
+
+ if (mMult[m_2 + k - 1][0] == -1)
+ {
+ mMult[m_2 + k - 1][0] = k - 1;
+ }
+ else
+ {
+ mMult[m_2 + k - 1][1] = k - 1;
+ }
+ }
+ }
+ else if (mType == 2)
+ {
+ for (int k = 1; k < p - 1; k++)
+ {
+ if (mMult[F[k + 1]][0] == -1)
+ {
+ mMult[F[k + 1]][0] = F[p - k];
+ }
+ else
+ {
+ mMult[F[k + 1]][1] = F[p - k];
+ }
+ }
+ }
+ else
+ {
+ throw new RuntimeException("only type 1 or type 2 implemented");
+ }
+ }
+ else
+ {
+ throw new RuntimeException("bisher nur fuer Gausssche Normalbasen"
+ + " implementiert");
+ }
+ }
+
+ private int elementOfOrder(int k, int p)
+ {
+ Random random = new Random();
+ int m = 0;
+ while (m == 0)
+ {
+ m = random.nextInt();
+ m %= p - 1;
+ if (m < 0)
+ {
+ m += p - 1;
+ }
+ }
+
+ int l = IntegerFunctions.order(m, p);
+
+ while (l % k != 0 || l == 0)
+ {
+ while (m == 0)
+ {
+ m = random.nextInt();
+ m %= p - 1;
+ if (m < 0)
+ {
+ m += p - 1;
+ }
+ }
+ l = IntegerFunctions.order(m, p);
+ }
+ int r = m;
+
+ l = k / l;
+
+ for (int i = 2; i <= l; i++)
+ {
+ r *= m;
+ }
+
+ return r;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java
new file mode 100644
index 000000000..97ee022c7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java
@@ -0,0 +1,587 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This class implements polynomials over GF2nElements.
+ *
+ * @see GF2nElement
+ */
+
+public class GF2nPolynomial
+{
+
+ private GF2nElement[] coeff; // keeps the coefficients of this polynomial
+
+ private int size; // the size of this polynomial
+
+ /**
+ * Creates a new PolynomialGF2n of size deg and elem as
+ * coefficients.
+ *
+ * @param deg -
+ * the maximum degree + 1
+ * @param elem -
+ * a GF2nElement
+ */
+ public GF2nPolynomial(int deg, GF2nElement elem)
+ {
+ size = deg;
+ coeff = new GF2nElement[size];
+ for (int i = 0; i < size; i++)
+ {
+ coeff[i] = (GF2nElement)elem.clone();
+ }
+ }
+
+ /**
+ * Creates a new PolynomialGF2n of size deg.
+ *
+ * @param deg the maximum degree + 1
+ */
+ private GF2nPolynomial(int deg)
+ {
+ size = deg;
+ coeff = new GF2nElement[size];
+ }
+
+ /**
+ * Creates a new PolynomialGF2n by cloning the given PolynomialGF2n a.
+ *
+ * @param a the PolynomialGF2n to clone
+ */
+ public GF2nPolynomial(GF2nPolynomial a)
+ {
+ int i;
+ coeff = new GF2nElement[a.size];
+ size = a.size;
+ for (i = 0; i < size; i++)
+ {
+ coeff[i] = (GF2nElement)a.coeff[i].clone();
+ }
+ }
+
+ /**
+ * Creates a new PolynomialGF2n from the given Bitstring polynomial
+ * over the GF2nField B1.
+ *
+ * @param polynomial the Bitstring to use
+ * @param B1 the field
+ */
+ public GF2nPolynomial(GF2Polynomial polynomial, GF2nField B1)
+ {
+ size = B1.getDegree() + 1;
+ coeff = new GF2nElement[size];
+ int i;
+ if (B1 instanceof GF2nONBField)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if (polynomial.testBit(i))
+ {
+ coeff[i] = GF2nONBElement.ONE((GF2nONBField)B1);
+ }
+ else
+ {
+ coeff[i] = GF2nONBElement.ZERO((GF2nONBField)B1);
+ }
+ }
+ }
+ else if (B1 instanceof GF2nPolynomialField)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if (polynomial.testBit(i))
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)B1);
+ }
+ else
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ZERO((GF2nPolynomialField)B1);
+ }
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n(Bitstring, GF2nField): B1 must be "
+ + "an instance of GF2nONBField or GF2nPolynomialField!");
+ }
+ }
+
+ public final void assignZeroToElements()
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ coeff[i].assignZero();
+ }
+ }
+
+ /**
+ * Returns the size (=maximum degree + 1) of this PolynomialGF2n. This is
+ * not the degree, use getDegree instead.
+ *
+ * @return the size (=maximum degree + 1) of this PolynomialGF2n.
+ */
+ public final int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns the degree of this PolynomialGF2n.
+ *
+ * @return the degree of this PolynomialGF2n.
+ */
+ public final int getDegree()
+ {
+ int i;
+ for (i = size - 1; i >= 0; i--)
+ {
+ if (!coeff[i].isZero())
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Enlarges the size of this PolynomialGF2n to k + 1.
+ *
+ * @param k the new maximum degree
+ */
+ public final void enlarge(int k)
+ {
+ if (k <= size)
+ {
+ return;
+ }
+ int i;
+ GF2nElement[] res = new GF2nElement[k];
+ System.arraycopy(coeff, 0, res, 0, size);
+ GF2nField f = coeff[0].getField();
+ if (coeff[0] instanceof GF2nPolynomialElement)
+ {
+ for (i = size; i < k; i++)
+ {
+ res[i] = GF2nPolynomialElement.ZERO((GF2nPolynomialField)f);
+ }
+ }
+ else if (coeff[0] instanceof GF2nONBElement)
+ {
+ for (i = size; i < k; i++)
+ {
+ res[i] = GF2nONBElement.ZERO((GF2nONBField)f);
+ }
+ }
+ size = k;
+ coeff = res;
+ }
+
+ public final void shrink()
+ {
+ int i = size - 1;
+ while (coeff[i].isZero() && (i > 0))
+ {
+ i--;
+ }
+ i++;
+ if (i < size)
+ {
+ GF2nElement[] res = new GF2nElement[i];
+ System.arraycopy(coeff, 0, res, 0, i);
+ coeff = res;
+ size = i;
+ }
+ }
+
+ /**
+ * Sets the coefficient at index to elem.
+ *
+ * @param index the index
+ * @param elem the GF2nElement to store as coefficient index
+ */
+ public final void set(int index, GF2nElement elem)
+ {
+ if (!(elem instanceof GF2nPolynomialElement)
+ && !(elem instanceof GF2nONBElement))
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n.set f must be an "
+ + "instance of either GF2nPolynomialElement or GF2nONBElement!");
+ }
+ coeff[index] = (GF2nElement)elem.clone();
+ }
+
+ /**
+ * Returns the coefficient at index.
+ *
+ * @param index the index
+ * @return the GF2nElement stored as coefficient index
+ */
+ public final GF2nElement at(int index)
+ {
+ return coeff[index];
+ }
+
+ /**
+ * Returns true if all coefficients equal zero.
+ *
+ * @return true if all coefficients equal zero.
+ */
+ public final boolean isZero()
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (coeff[i] != null)
+ {
+ if (!coeff[i].isZero())
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public final boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nPolynomial))
+ {
+ return false;
+ }
+
+ GF2nPolynomial otherPol = (GF2nPolynomial)other;
+
+ if (getDegree() != otherPol.getDegree())
+ {
+ return false;
+ }
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (!coeff[i].equals(otherPol.coeff[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ return getDegree() + coeff.hashCode();
+ }
+
+ /**
+ * Adds the PolynomialGF2n b to this and returns the
+ * result in a new PolynomialGF2n.
+ *
+ * @param b -
+ * the PolynomialGF2n to add
+ * @return this + b
+ * @throws DifferentFieldsException if this and b are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial add(GF2nPolynomial b)
+ throws RuntimeException
+ {
+ GF2nPolynomial result;
+ if (size() >= b.size())
+ {
+ result = new GF2nPolynomial(size());
+ int i;
+ for (i = 0; i < b.size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]);
+ }
+ for (; i < size(); i++)
+ {
+ result.coeff[i] = coeff[i];
+ }
+ }
+ else
+ {
+ result = new GF2nPolynomial(b.size());
+ int i;
+ for (i = 0; i < size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]);
+ }
+ for (; i < b.size(); i++)
+ {
+ result.coeff[i] = b.coeff[i];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies the scalar s to each coefficient of this
+ * PolynomialGF2n and returns the result in a new PolynomialGF2n.
+ *
+ * @param s the scalar to multiply
+ * @return this x s
+ * @throws DifferentFieldsException if this and s are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial scalarMultiply(GF2nElement s)
+ throws RuntimeException
+ {
+ GF2nPolynomial result = new GF2nPolynomial(size());
+ int i;
+ for (i = 0; i < size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].multiply(s); // result[i]
+ // =
+ // a[i]*s
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies this by b and returns the result in a new
+ * PolynomialGF2n.
+ *
+ * @param b the PolynomialGF2n to multiply
+ * @return this * b
+ * @throws DifferentFieldsException if this and b are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial multiply(GF2nPolynomial b)
+ throws RuntimeException
+ {
+ int i, j;
+ int aDegree = size();
+ int bDegree = b.size();
+ if (aDegree != bDegree)
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n.multiply: this and b must "
+ + "have the same size!");
+ }
+ GF2nPolynomial result = new GF2nPolynomial((aDegree << 1) - 1);
+ for (i = 0; i < size(); i++)
+ {
+ for (j = 0; j < b.size(); j++)
+ {
+ if (result.coeff[i + j] == null)
+ {
+ result.coeff[i + j] = (GF2nElement)coeff[i]
+ .multiply(b.coeff[j]);
+ }
+ else
+ {
+ result.coeff[i + j] = (GF2nElement)result.coeff[i + j]
+ .add(coeff[i].multiply(b.coeff[j]));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies this by b, reduces the result by g and
+ * returns it in a new PolynomialGF2n.
+ *
+ * @param b the PolynomialGF2n to multiply
+ * @param g the modul
+ * @return this * b mod g
+ * @throws DifferentFieldsException if this, b and g are
+ * not all defined over the same field.
+ */
+ public final GF2nPolynomial multiplyAndReduce(GF2nPolynomial b,
+ GF2nPolynomial g)
+ throws RuntimeException,
+ ArithmeticException
+ {
+ return multiply(b).reduce(g);
+ }
+
+ /**
+ * Reduces this by g and returns the result in a new
+ * PolynomialGF2n.
+ *
+ * @param g -
+ * the modulus
+ * @return this % g
+ * @throws DifferentFieldsException if this and g are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial reduce(GF2nPolynomial g)
+ throws RuntimeException, ArithmeticException
+ {
+ return remainder(g); // return this % g
+ }
+
+ /**
+ * Shifts left this by amount and stores the result in
+ * this PolynomialGF2n.
+ *
+ * @param amount the amount to shift the coefficients
+ */
+ public final void shiftThisLeft(int amount)
+ {
+ if (amount > 0)
+ {
+ int i;
+ int oldSize = size;
+ GF2nField f = coeff[0].getField();
+ enlarge(size + amount);
+ for (i = oldSize - 1; i >= 0; i--)
+ {
+ coeff[i + amount] = coeff[i];
+ }
+ if (coeff[0] instanceof GF2nPolynomialElement)
+ {
+ for (i = amount - 1; i >= 0; i--)
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ZERO((GF2nPolynomialField)f);
+ }
+ }
+ else if (coeff[0] instanceof GF2nONBElement)
+ {
+ for (i = amount - 1; i >= 0; i--)
+ {
+ coeff[i] = GF2nONBElement.ZERO((GF2nONBField)f);
+ }
+ }
+ }
+ }
+
+ public final GF2nPolynomial shiftLeft(int amount)
+ {
+ if (amount <= 0)
+ {
+ return new GF2nPolynomial(this);
+ }
+ GF2nPolynomial result = new GF2nPolynomial(size + amount, coeff[0]);
+ result.assignZeroToElements();
+ for (int i = 0; i < size; i++)
+ {
+ result.coeff[i + amount] = coeff[i];
+ }
+ return result;
+ }
+
+ /**
+ * Divides this by b and stores the result in a new
+ * PolynomialGF2n[2], quotient in result[0] and remainder in result[1].
+ *
+ * @param b the divisor
+ * @return the quotient and remainder of this / b
+ * @throws DifferentFieldsException if this and b are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial[] divide(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ GF2nPolynomial a = new GF2nPolynomial(this);
+ a.shrink();
+ GF2nPolynomial shift;
+ GF2nElement factor;
+ int bDegree = b.getDegree();
+ GF2nElement inv = (GF2nElement)b.coeff[bDegree].invert();
+ if (a.getDegree() < bDegree)
+ {
+ result[0] = new GF2nPolynomial(this);
+ result[0].assignZeroToElements();
+ result[0].shrink();
+ result[1] = new GF2nPolynomial(this);
+ result[1].shrink();
+ return result;
+ }
+ result[0] = new GF2nPolynomial(this);
+ result[0].assignZeroToElements();
+ int i = a.getDegree() - bDegree;
+ while (i >= 0)
+ {
+ factor = (GF2nElement)a.coeff[a.getDegree()].multiply(inv);
+ shift = b.scalarMultiply(factor);
+ shift.shiftThisLeft(i);
+ a = a.add(shift);
+ a.shrink();
+ result[0].coeff[i] = (GF2nElement)factor.clone();
+ i = a.getDegree() - bDegree;
+ }
+ result[1] = a;
+ result[0].shrink();
+ return result;
+ }
+
+ /**
+ * Divides this by b and stores the remainder in a new
+ * PolynomialGF2n.
+ *
+ * @param b the divisor
+ * @return the remainder this % b
+ * @throws DifferentFieldsException if this and b are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial remainder(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ result = divide(b);
+ return result[1];
+ }
+
+ /**
+ * Divides this by b and stores the quotient in a new
+ * PolynomialGF2n.
+ *
+ * @param b the divisor
+ * @return the quotient this / b
+ * @throws DifferentFieldsException if this and b are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial quotient(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ result = divide(b);
+ return result[0];
+ }
+
+ /**
+ * Computes the greatest common divisor of this and g and
+ * returns the result in a new PolynomialGF2n.
+ *
+ * @param g -
+ * a GF2nPolynomial
+ * @return gcd(this, g)
+ * @throws DifferentFieldsException if the coefficients of this and g use
+ * different fields
+ * @throws ArithmeticException if coefficients are zero.
+ */
+ public final GF2nPolynomial gcd(GF2nPolynomial g)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial a = new GF2nPolynomial(this);
+ GF2nPolynomial b = new GF2nPolynomial(g);
+ a.shrink();
+ b.shrink();
+ GF2nPolynomial c;
+ GF2nPolynomial result;
+ GF2nElement alpha;
+ while (!b.isZero())
+ {
+ c = a.remainder(b);
+ a = b;
+ b = c;
+ }
+ alpha = a.coeff[a.getDegree()];
+ result = a.scalarMultiply((GF2nElement)alpha.invert());
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java
new file mode 100644
index 000000000..50e9d7511
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java
@@ -0,0 +1,1021 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+
+/**
+ * This class implements elements of finite binary fields GF(2n)
+ * using polynomial representation. For more information on the arithmetic see
+ * for example IEEE Standard 1363 or Certicom online-tutorial.
+ *
+ * @see "GF2nField"
+ * @see GF2nPolynomialField
+ * @see GF2nONBElement
+ * @see GF2Polynomial
+ */
+public class GF2nPolynomialElement
+ extends GF2nElement
+{
+
+ // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
+ private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
+ 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
+ 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
+ 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
+
+ // the used GF2Polynomial which stores the coefficients
+ private GF2Polynomial polynomial;
+
+ /**
+ * Create a new random GF2nPolynomialElement using the given field and
+ * source of randomness.
+ *
+ * @param f the GF2nField to use
+ * @param rand the source of randomness
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, Random rand)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree);
+ randomize(rand);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field and Bitstring.
+ *
+ * @param f the GF2nPolynomialField to use
+ * @param bs the desired value as Bitstring
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, GF2Polynomial bs)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(bs);
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field f and
+ * byte[] os as value. The conversion is done according to 1363.
+ *
+ * @param f the GF2nField to use
+ * @param os the octet string to assign to this GF2nPolynomialElement
+ * @see "P1363 5.5.5 p23, OS2FEP/OS2BSP"
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, byte[] os)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree, os);
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field f and
+ * int[] is as value.
+ *
+ * @param f the GF2nField to use
+ * @param is the integer string to assign to this GF2nPolynomialElement
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, int[] is)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree, is);
+ polynomial.expandN(f.mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement by cloning the given
+ * GF2nPolynomialElement b.
+ *
+ * @param other the GF2nPolynomialElement to clone
+ */
+ public GF2nPolynomialElement(GF2nPolynomialElement other)
+ {
+ mField = other.mField;
+ mDegree = other.mDegree;
+ polynomial = new GF2Polynomial(other.polynomial);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates a new GF2nPolynomialElement by cloning this
+ * GF2nPolynomialElement.
+ *
+ * @return a copy of this element
+ */
+ public Object clone()
+ {
+ return new GF2nPolynomialElement(this);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Assigns the value 'zero' to this Polynomial.
+ */
+ void assignZero()
+ {
+ polynomial.assignZero();
+ }
+
+ /**
+ * Create the zero element.
+ *
+ * @param f the finite field
+ * @return the zero element in the given finite field
+ */
+ public static GF2nPolynomialElement ZERO(GF2nPolynomialField f)
+ {
+ GF2Polynomial polynomial = new GF2Polynomial(f.getDegree());
+ return new GF2nPolynomialElement(f, polynomial);
+ }
+
+ /**
+ * Create the one element.
+ *
+ * @param f the finite field
+ * @return the one element in the given finite field
+ */
+ public static GF2nPolynomialElement ONE(GF2nPolynomialField f)
+ {
+ GF2Polynomial polynomial = new GF2Polynomial(f.getDegree(),
+ new int[]{1});
+ return new GF2nPolynomialElement(f, polynomial);
+ }
+
+ /**
+ * Assigns the value 'one' to this Polynomial.
+ */
+ void assignOne()
+ {
+ polynomial.assignOne();
+ }
+
+ /**
+ * Assign a random value to this GF2nPolynomialElement using the specified
+ * source of randomness.
+ *
+ * @param rand the source of randomness
+ */
+ private void randomize(Random rand)
+ {
+ polynomial.expandN(mDegree);
+ polynomial.randomize(rand);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return true if this is the zero element
+ */
+ public boolean isZero()
+ {
+ return polynomial.isZero();
+ }
+
+ /**
+ * Tests if the GF2nPolynomialElement has 'one' as value.
+ *
+ * @return true if this equals one (this == 1)
+ */
+ public boolean isOne()
+ {
+ return polynomial.isOne();
+ }
+
+ /**
+ * Compare this element with another object.
+ *
+ * @param other the other object
+ * @return true if the two objects are equal, false
+ * otherwise
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nPolynomialElement))
+ {
+ return false;
+ }
+ GF2nPolynomialElement otherElem = (GF2nPolynomialElement)other;
+
+ if (mField != otherElem.mField)
+ {
+ if (!mField.getFieldPolynomial().equals(
+ otherElem.mField.getFieldPolynomial()))
+ {
+ return false;
+ }
+ }
+
+ return polynomial.equals(otherElem.polynomial);
+ }
+
+ /**
+ * @return the hash code of this element
+ */
+ public int hashCode()
+ {
+ return mField.hashCode() + polynomial.hashCode();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the value of this GF2nPolynomialElement in a new Bitstring.
+ *
+ * @return the value of this GF2nPolynomialElement in a new Bitstring
+ */
+ private GF2Polynomial getGF2Polynomial()
+ {
+ return new GF2Polynomial(polynomial);
+ }
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set.
+ *
+ * @param index the index of the bit to test
+ * @return true if the indexed bit is set
+ */
+ boolean testBit(int index)
+ {
+ return polynomial.testBit(index);
+ }
+
+ /**
+ * Returns whether the rightmost bit of the bit representation is set. This
+ * is needed for data conversion according to 1363.
+ *
+ * @return true if the rightmost bit of this element is set
+ */
+ public boolean testRightmostBit()
+ {
+ return polynomial.testBit(0);
+ }
+
+ /**
+ * Compute the sum of this element and addend.
+ *
+ * @param addend the addend
+ * @return this + other (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement add(GFElement addend)
+ throws RuntimeException
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.addToThis(addend);
+ return result;
+ }
+
+ /**
+ * Compute this + addend (overwrite this).
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void addToThis(GFElement addend)
+ throws RuntimeException
+ {
+ if (!(addend instanceof GF2nPolynomialElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nPolynomialElement)addend).mField))
+ {
+ throw new RuntimeException();
+ }
+ polynomial.addToThis(((GF2nPolynomialElement)addend).polynomial);
+ }
+
+ /**
+ * Returns this element + 'one".
+ *
+ * @return this + 'one'
+ */
+ public GF2nElement increase()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * Increases this element by 'one'.
+ */
+ public void increaseThis()
+ {
+ polynomial.increaseThis();
+ }
+
+ /**
+ * Compute the product of this element and factor.
+ *
+ * @param factor the factor
+ * @return this * factor (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement multiply(GFElement factor)
+ throws RuntimeException
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.multiplyThisBy(factor);
+ return result;
+ }
+
+ /**
+ * Compute this * factor (overwrite this).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void multiplyThisBy(GFElement factor)
+ throws RuntimeException
+ {
+ if (!(factor instanceof GF2nPolynomialElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nPolynomialElement)factor).mField))
+ {
+ throw new RuntimeException();
+ }
+ if (equals(factor))
+ {
+ squareThis();
+ return;
+ }
+ polynomial = polynomial
+ .multiply(((GF2nPolynomialElement)factor).polynomial);
+ reduceThis();
+ }
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return this-1 (newly created)
+ * @throws ArithmeticException if this is the zero element.
+ * @see GF2nPolynomialElement#invertMAIA
+ * @see GF2nPolynomialElement#invertEEA
+ * @see GF2nPolynomialElement#invertSquare
+ */
+ public GFElement invert()
+ throws ArithmeticException
+ {
+ return invertMAIA();
+ }
+
+ /**
+ * Calculates the multiplicative inverse of this and returns the
+ * result in a new GF2nPolynomialElement.
+ *
+ * @return this^(-1)
+ * @throws ArithmeticException if this equals zero
+ */
+ public GF2nPolynomialElement invertEEA()
+ throws ArithmeticException
+ {
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ GF2Polynomial b = new GF2Polynomial(mDegree + 32, "ONE");
+ b.reduceN();
+ GF2Polynomial c = new GF2Polynomial(mDegree + 32);
+ c.reduceN();
+ GF2Polynomial u = getGF2Polynomial();
+ GF2Polynomial v = mField.getFieldPolynomial();
+ GF2Polynomial h;
+ int j;
+ u.reduceN();
+ while (!u.isOne())
+ {
+ u.reduceN();
+ v.reduceN();
+ j = u.getLength() - v.getLength();
+ if (j < 0)
+ {
+ h = u;
+ u = v;
+ v = h;
+ h = b;
+ b = c;
+ c = h;
+ j = -j;
+ c.reduceN(); // this increases the performance
+ }
+ u.shiftLeftAddThis(v, j);
+ b.shiftLeftAddThis(c, j);
+ }
+ b.reduceN();
+ return new GF2nPolynomialElement((GF2nPolynomialField)mField, b);
+ }
+
+ /**
+ * Calculates the multiplicative inverse of this and returns the
+ * result in a new GF2nPolynomialElement.
+ *
+ * @return this^(-1)
+ * @throws ArithmeticException if this equals zero
+ */
+ public GF2nPolynomialElement invertSquare()
+ throws ArithmeticException
+ {
+ GF2nPolynomialElement n;
+ GF2nPolynomialElement u;
+ int i, j, k, b;
+
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ // b = (n-1)
+ b = mField.getDegree() - 1;
+ // n = a
+ n = new GF2nPolynomialElement(this);
+ n.polynomial.expandN((mDegree << 1) + 32); // increase performance
+ n.polynomial.reduceN();
+ // k = 1
+ k = 1;
+
+ // for i = (r-1) downto 0 do, r=bitlength(b)
+ for (i = IntegerFunctions.floorLog(b) - 1; i >= 0; i--)
+ {
+ // u = n
+ u = new GF2nPolynomialElement(n);
+ // for j = 1 to k do
+ for (j = 1; j <= k; j++)
+ {
+ // u = u^2
+ u.squareThisPreCalc();
+ }
+ // n = nu
+ n.multiplyThisBy(u);
+ // k = 2k
+ k <<= 1;
+ // if b(i)==1
+ if ((b & bitMask[i]) != 0)
+ {
+ // n = n^2 * b
+ n.squareThisPreCalc();
+ n.multiplyThisBy(this);
+ // k = k+1
+ k += 1;
+ }
+ }
+
+ // outpur n^2
+ n.squareThisPreCalc();
+ return n;
+ }
+
+ /**
+ * Calculates the multiplicative inverse of this using the modified
+ * almost inverse algorithm and returns the result in a new
+ * GF2nPolynomialElement.
+ *
+ * @return this^(-1)
+ * @throws ArithmeticException if this equals zero
+ */
+ public GF2nPolynomialElement invertMAIA()
+ throws ArithmeticException
+ {
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ GF2Polynomial b = new GF2Polynomial(mDegree, "ONE");
+ GF2Polynomial c = new GF2Polynomial(mDegree);
+ GF2Polynomial u = getGF2Polynomial();
+ GF2Polynomial v = mField.getFieldPolynomial();
+ GF2Polynomial h;
+ while (true)
+ {
+ while (!u.testBit(0))
+ { // x|u (x divides u)
+ u.shiftRightThis(); // u = u / x
+ if (!b.testBit(0))
+ {
+ b.shiftRightThis();
+ }
+ else
+ {
+ b.addToThis(mField.getFieldPolynomial());
+ b.shiftRightThis();
+ }
+ }
+ if (u.isOne())
+ {
+ return new GF2nPolynomialElement((GF2nPolynomialField)mField,
+ b);
+ }
+ u.reduceN();
+ v.reduceN();
+ if (u.getLength() < v.getLength())
+ {
+ h = u;
+ u = v;
+ v = h;
+ h = b;
+ b = c;
+ c = h;
+ }
+ u.addToThis(v);
+ b.addToThis(c);
+ }
+ }
+
+ /**
+ * This method is used internally to map the square()-calls within
+ * GF2nPolynomialElement to one of the possible squaring methods.
+ *
+ * @return this2 (newly created)
+ * @see GF2nPolynomialElement#squarePreCalc
+ */
+ public GF2nElement square()
+ {
+ return squarePreCalc();
+ }
+
+ /**
+ * This method is used internally to map the square()-calls within
+ * GF2nPolynomialElement to one of the possible squaring methods.
+ */
+ public void squareThis()
+ {
+ squareThisPreCalc();
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement using GF2nField's squaring matrix.
+ * This is supposed to be fast when using a polynomial (no tri- or
+ * pentanomial) as fieldpolynomial. Use squarePreCalc when using a tri- or
+ * pentanomial as fieldpolynomial instead.
+ *
+ * @return this2 (newly created)
+ * @see GF2Polynomial#vectorMult
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2nPolynomialElement#squareBitwise
+ */
+ public GF2nPolynomialElement squareMatrix()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisMatrix();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement using GF2nFields squaring matrix. This
+ * is supposed to be fast when using a polynomial (no tri- or pentanomial)
+ * as fieldpolynomial. Use squarePreCalc when using a tri- or pentanomial as
+ * fieldpolynomial instead.
+ *
+ * @see GF2Polynomial#vectorMult
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2nPolynomialElement#squareBitwise
+ */
+ public void squareThisMatrix()
+ {
+ GF2Polynomial result = new GF2Polynomial(mDegree);
+ for (int i = 0; i < mDegree; i++)
+ {
+ if (polynomial
+ .vectorMult(((GF2nPolynomialField)mField).squaringMatrix[mDegree
+ - i - 1]))
+ {
+ result.setBit(i);
+
+ }
+ }
+ polynomial = result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by shifting left its Bitstring and
+ * reducing. This is supposed to be the slowest method. Use squarePreCalc or
+ * squareMatrix instead.
+ *
+ * @return this2 (newly created)
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2Polynomial#squareThisBitwise
+ */
+ public GF2nPolynomialElement squareBitwise()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisBitwise();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by shifting left its Bitstring and
+ * reducing. This is supposed to be the slowest method. Use squarePreCalc or
+ * squareMatrix instead.
+ *
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2Polynomial#squareThisBitwise
+ */
+ public void squareThisBitwise()
+ {
+ polynomial.squareThisBitwise();
+ reduceThis();
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by using precalculated values and
+ * reducing. This is supposed to de fastest when using a trinomial or
+ * pentanomial as field polynomial. Use squareMatrix when using a ordinary
+ * polynomial as field polynomial.
+ *
+ * @return this2 (newly created)
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2Polynomial#squareThisPreCalc
+ */
+ public GF2nPolynomialElement squarePreCalc()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisPreCalc();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by using precalculated values and
+ * reducing. This is supposed to de fastest when using a tri- or pentanomial
+ * as fieldpolynomial. Use squareMatrix when using a ordinary polynomial as
+ * fieldpolynomial.
+ *
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2Polynomial#squareThisPreCalc
+ */
+ public void squareThisPreCalc()
+ {
+ polynomial.squareThisPreCalc();
+ reduceThis();
+ }
+
+ /**
+ * Calculates this to the power of k and returns the result
+ * in a new GF2nPolynomialElement.
+ *
+ * @param k the power
+ * @return this^k in a new GF2nPolynomialElement
+ */
+ public GF2nPolynomialElement power(int k)
+ {
+ if (k == 1)
+ {
+ return new GF2nPolynomialElement(this);
+ }
+
+ GF2nPolynomialElement result = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)mField);
+ if (k == 0)
+ {
+ return result;
+ }
+
+ GF2nPolynomialElement x = new GF2nPolynomialElement(this);
+ x.polynomial.expandN((x.mDegree << 1) + 32); // increase performance
+ x.polynomial.reduceN();
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ if ((k & (1 << i)) != 0)
+ {
+ result.multiplyThisBy(x);
+ }
+ x.square();
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the square root of this element and return the result in a new
+ * {@link GF2nPolynomialElement}.
+ *
+ * @return this1/2 (newly created)
+ */
+ public GF2nElement squareRoot()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareRootThis();
+ return result;
+ }
+
+ /**
+ * Compute the square root of this element.
+ */
+ public void squareRootThis()
+ {
+ // increase performance
+ polynomial.expandN((mDegree << 1) + 32);
+ polynomial.reduceN();
+ for (int i = 0; i < mField.getDegree() - 1; i++)
+ {
+ squareThis();
+ }
+ }
+
+ /**
+ * Solves the quadratic equation z2 + z = this if
+ * such a solution exists. This method returns one of the two possible
+ * solutions. The other solution is z + 1. Use z.increase() to
+ * compute this solution.
+ *
+ * @return a GF2nPolynomialElement representing one z satisfying the
+ * equation z2 + z = this
+ * @throws NoSolutionException if no solution exists
+ * @see "IEEE 1363, Annex A.4.7"
+ */
+ public GF2nElement solveQuadraticEquation()
+ throws RuntimeException
+ {
+ if (isZero())
+ {
+ return ZERO((GF2nPolynomialField)mField);
+ }
+
+ if ((mDegree & 1) == 1)
+ {
+ return halfTrace();
+ }
+
+ // TODO this can be sped-up by precomputation of p and w's
+ GF2nPolynomialElement z, w;
+ do
+ {
+ // step 1.
+ GF2nPolynomialElement p = new GF2nPolynomialElement(
+ (GF2nPolynomialField)mField, new Random());
+ // step 2.
+ z = ZERO((GF2nPolynomialField)mField);
+ w = (GF2nPolynomialElement)p.clone();
+ // step 3.
+ for (int i = 1; i < mDegree; i++)
+ {
+ // compute z = z^2 + w^2 * this
+ // and w = w^2 + p
+ z.squareThis();
+ w.squareThis();
+ z.addToThis(w.multiply(this));
+ w.addToThis(p);
+ }
+ }
+ while (w.isZero()); // step 4.
+
+ if (!equals(z.square().add(z)))
+ {
+ throw new RuntimeException();
+ }
+
+ // step 5.
+ return z;
+ }
+
+ /**
+ * Returns the trace of this GF2nPolynomialElement.
+ *
+ * @return the trace of this GF2nPolynomialElement
+ */
+ public int trace()
+ {
+ GF2nPolynomialElement t = new GF2nPolynomialElement(this);
+ int i;
+
+ for (i = 1; i < mDegree; i++)
+ {
+ t.squareThis();
+ t.addToThis(this);
+ }
+
+ if (t.isOne())
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the half-trace of this GF2nPolynomialElement.
+ *
+ * @return a GF2nPolynomialElement representing the half-trace of this
+ * GF2nPolynomialElement.
+ * @throws DegreeIsEvenException if the degree of this GF2nPolynomialElement is even.
+ */
+ private GF2nPolynomialElement halfTrace()
+ throws RuntimeException
+ {
+ if ((mDegree & 0x01) == 0)
+ {
+ throw new RuntimeException();
+ }
+ int i;
+ GF2nPolynomialElement h = new GF2nPolynomialElement(this);
+
+ for (i = 1; i <= ((mDegree - 1) >> 1); i++)
+ {
+ h.squareThis();
+ h.squareThis();
+ h.addToThis(this);
+ }
+
+ return h;
+ }
+
+ /**
+ * Reduces this GF2nPolynomialElement modulo the field-polynomial.
+ *
+ * @see GF2Polynomial#reduceTrinomial
+ * @see GF2Polynomial#reducePentanomial
+ */
+ private void reduceThis()
+ {
+ if (polynomial.getLength() > mDegree)
+ { // really reduce ?
+ if (((GF2nPolynomialField)mField).isTrinomial())
+ { // fieldpolonomial
+ // is trinomial
+ int tc;
+ try
+ {
+ tc = ((GF2nPolynomialField)mField).getTc();
+ }
+ catch (RuntimeException NATExc)
+ {
+ throw new RuntimeException(
+ "GF2nPolynomialElement.reduce: the field"
+ + " polynomial is not a trinomial");
+ }
+ if (((mDegree - tc) <= 32) // do we have to use slow
+ // bitwise reduction ?
+ || (polynomial.getLength() > (mDegree << 1)))
+ {
+ reduceTrinomialBitwise(tc);
+ return;
+ }
+ polynomial.reduceTrinomial(mDegree, tc);
+ return;
+ }
+ else if (((GF2nPolynomialField)mField).isPentanomial())
+ { // fieldpolynomial
+ // is
+ // pentanomial
+ int[] pc;
+ try
+ {
+ pc = ((GF2nPolynomialField)mField).getPc();
+ }
+ catch (RuntimeException NATExc)
+ {
+ throw new RuntimeException(
+ "GF2nPolynomialElement.reduce: the field"
+ + " polynomial is not a pentanomial");
+ }
+ if (((mDegree - pc[2]) <= 32) // do we have to use slow
+ // bitwise reduction ?
+ || (polynomial.getLength() > (mDegree << 1)))
+ {
+ reducePentanomialBitwise(pc);
+ return;
+ }
+ polynomial.reducePentanomial(mDegree, pc);
+ return;
+ }
+ else
+ { // fieldpolynomial is something else
+ polynomial = polynomial.remainder(mField.getFieldPolynomial());
+ polynomial.expandN(mDegree);
+ return;
+ }
+ }
+ if (polynomial.getLength() < mDegree)
+ {
+ polynomial.expandN(mDegree);
+ }
+ }
+
+ /**
+ * Reduce this GF2nPolynomialElement using the trinomial x^n + x^tc + 1 as
+ * fieldpolynomial. The coefficients are reduced bit by bit.
+ */
+ private void reduceTrinomialBitwise(int tc)
+ {
+ int i;
+ int k = mDegree - tc;
+ for (i = polynomial.getLength() - 1; i >= mDegree; i--)
+ {
+ if (polynomial.testBit(i))
+ {
+
+ polynomial.xorBit(i);
+ polynomial.xorBit(i - k);
+ polynomial.xorBit(i - mDegree);
+
+ }
+ }
+ polynomial.reduceN();
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Reduce this GF2nPolynomialElement using the pentanomial x^n + x^pc[2] +
+ * x^pc[1] + x^pc[0] + 1 as fieldpolynomial. The coefficients are reduced
+ * bit by bit.
+ */
+ private void reducePentanomialBitwise(int[] pc)
+ {
+ int i;
+ int k = mDegree - pc[2];
+ int l = mDegree - pc[1];
+ int m = mDegree - pc[0];
+ for (i = polynomial.getLength() - 1; i >= mDegree; i--)
+ {
+ if (polynomial.testBit(i))
+ {
+ polynomial.xorBit(i);
+ polynomial.xorBit(i - k);
+ polynomial.xorBit(i - l);
+ polynomial.xorBit(i - m);
+ polynomial.xorBit(i - mDegree);
+
+ }
+ }
+ polynomial.reduceN();
+ polynomial.expandN(mDegree);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a string representing this Bitstrings value using hexadecimal
+ * radix in MSB-first order.
+ *
+ * @return a String representing this Bitstrings value.
+ */
+ public String toString()
+ {
+ return polynomial.toString(16);
+ }
+
+ /**
+ * Returns a string representing this Bitstrings value using hexadecimal or
+ * binary radix in MSB-first order.
+ *
+ * @param radix the radix to use (2 or 16, otherwise 2 is used)
+ * @return a String representing this Bitstrings value.
+ */
+ public String toString(int radix)
+ {
+ return polynomial.toString(radix);
+ }
+
+ /**
+ * Converts this GF2nPolynomialElement to a byte[] according to 1363.
+ *
+ * @return a byte[] representing the value of this GF2nPolynomialElement
+ * @see "P1363 5.5.2 p22f BS2OSP, FE2OSP"
+ */
+ public byte[] toByteArray()
+ {
+ return polynomial.toByteArray();
+ }
+
+ /**
+ * Converts this GF2nPolynomialElement to an integer according to 1363.
+ *
+ * @return a BigInteger representing the value of this
+ * GF2nPolynomialElement
+ * @see "P1363 5.5.1 p22 BS2IP"
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ return polynomial.toFlexiBigInt();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java
new file mode 100644
index 000000000..f9ec0bca2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java
@@ -0,0 +1,553 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.util.Random;
+import java.util.Vector;
+
+
+/**
+ * This class implements the abstract class GF2nField for polynomial
+ * representation. It computes the field polynomial and the squaring matrix.
+ * GF2nField is used by GF2nPolynomialElement which implements the elements of
+ * this field.
+ *
+ * @see GF2nField
+ * @see GF2nPolynomialElement
+ */
+public class GF2nPolynomialField
+ extends GF2nField
+{
+
+ /**
+ * Matrix used for fast squaring
+ */
+ GF2Polynomial[] squaringMatrix;
+
+ // field polynomial is a trinomial
+ private boolean isTrinomial = false;
+
+ // field polynomial is a pentanomial
+ private boolean isPentanomial = false;
+
+ // middle coefficient of the field polynomial in case it is a trinomial
+ private int tc;
+
+ // middle 3 coefficients of the field polynomial in case it is a pentanomial
+ private int[] pc = new int[3];
+
+ /**
+ * constructs an instance of the finite field with 2deg
+ * elements and characteristic 2.
+ *
+ * @param deg the extention degree of this field
+ */
+ public GF2nPolynomialField(int deg)
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+ mDegree = deg;
+ computeFieldPolynomial();
+ computeSquaringMatrix();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * constructs an instance of the finite field with 2deg
+ * elements and characteristic 2.
+ *
+ * @param deg the degree of this field
+ * @param file true if you want to read the field polynomial from the
+ * file false if you want to use a random fielpolynomial
+ * (this can take very long for huge degrees)
+ */
+ public GF2nPolynomialField(int deg, boolean file)
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+ mDegree = deg;
+ if (file)
+ {
+ computeFieldPolynomial();
+ }
+ else
+ {
+ computeFieldPolynomial2();
+ }
+ computeSquaringMatrix();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * Creates a new GF2nField of degree i and uses the given
+ * polynomial as field polynomial. The polynomial is checked
+ * whether it is irreducible. This can take some time if i is huge!
+ *
+ * @param deg degree of the GF2nField
+ * @param polynomial the field polynomial to use
+ * @throws PolynomialIsNotIrreducibleException if the given polynomial is not irreducible in GF(2^i)
+ */
+ public GF2nPolynomialField(int deg, GF2Polynomial polynomial)
+ throws RuntimeException
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("degree must be at least 3");
+ }
+ if (polynomial.getLength() != deg + 1)
+ {
+ throw new RuntimeException();
+ }
+ if (!polynomial.isIrreducible())
+ {
+ throw new RuntimeException();
+ }
+ mDegree = deg;
+ // fieldPolynomial = new Bitstring(polynomial);
+ fieldPolynomial = polynomial;
+ computeSquaringMatrix();
+ int k = 2; // check if the polynomial is a trinomial or pentanomial
+ for (int j = 1; j < fieldPolynomial.getLength() - 1; j++)
+ {
+ if (fieldPolynomial.testBit(j))
+ {
+ k++;
+ if (k == 3)
+ {
+ tc = j;
+ }
+ if (k <= 5)
+ {
+ pc[k - 3] = j;
+ }
+ }
+ }
+ if (k == 3)
+ {
+ isTrinomial = true;
+ }
+ if (k == 5)
+ {
+ isPentanomial = true;
+ }
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * Returns true if the field polynomial is a trinomial. The coefficient can
+ * be retrieved using getTc().
+ *
+ * @return true if the field polynomial is a trinomial
+ */
+ public boolean isTrinomial()
+ {
+ return isTrinomial;
+ }
+
+ /**
+ * Returns true if the field polynomial is a pentanomial. The coefficients
+ * can be retrieved using getPc().
+ *
+ * @return true if the field polynomial is a pentanomial
+ */
+ public boolean isPentanomial()
+ {
+ return isPentanomial;
+ }
+
+ /**
+ * Returns the degree of the middle coefficient of the used field trinomial
+ * (x^n + x^(getTc()) + 1).
+ *
+ * @return the middle coefficient of the used field trinomial
+ * @throws GFException if the field polynomial is not a trinomial
+ */
+ public int getTc()
+ throws RuntimeException
+ {
+ if (!isTrinomial)
+ {
+ throw new RuntimeException();
+ }
+ return tc;
+ }
+
+ /**
+ * Returns the degree of the middle coefficients of the used field
+ * pentanomial (x^n + x^(getPc()[2]) + x^(getPc()[1]) + x^(getPc()[0]) + 1).
+ *
+ * @return the middle coefficients of the used field pentanomial
+ * @throws GFException if the field polynomial is not a pentanomial
+ */
+ public int[] getPc()
+ throws RuntimeException
+ {
+ if (!isPentanomial)
+ {
+ throw new RuntimeException();
+ }
+ int[] result = new int[3];
+ System.arraycopy(pc, 0, result, 0, 3);
+ return result;
+ }
+
+ /**
+ * Return row vector i of the squaring matrix.
+ *
+ * @param i the index of the row vector to return
+ * @return a copy of squaringMatrix[i]
+ * @see GF2nPolynomialElement#squareMatrix
+ */
+ public GF2Polynomial getSquaringVector(int i)
+ {
+ return new GF2Polynomial(squaringMatrix[i]);
+ }
+
+ /**
+ * Compute a random root of the given GF2Polynomial.
+ *
+ * @param polynomial the polynomial
+ * @return a random root of polynomial
+ */
+ protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
+ {
+ // We are in B1!!!
+ GF2nPolynomial c;
+ GF2nPolynomial ut;
+ GF2nElement u;
+ GF2nPolynomial h;
+ int hDegree;
+ // 1. Set g(t) <- f(t)
+ GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
+ int gDegree = g.getDegree();
+ int i;
+
+ // 2. while deg(g) > 1
+ while (gDegree > 1)
+ {
+ do
+ {
+ // 2.1 choose random u (element of) GF(2^m)
+ u = new GF2nPolynomialElement(this, new Random());
+ ut = new GF2nPolynomial(2, GF2nPolynomialElement.ZERO(this));
+ // 2.2 Set c(t) <- ut
+ ut.set(1, u);
+ c = new GF2nPolynomial(ut);
+ // 2.3 For i from 1 to m-1 do
+ for (i = 1; i <= mDegree - 1; i++)
+ {
+ // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
+ c = c.multiplyAndReduce(c, g);
+ c = c.add(ut);
+ }
+ // 2.4 set h(t) <- GCD(c(t), g(t))
+ h = c.gcd(g);
+ // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
+ // step 2.1
+ hDegree = h.getDegree();
+ gDegree = g.getDegree();
+ }
+ while ((hDegree == 0) || (hDegree == gDegree));
+ // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
+ if ((hDegree << 1) > gDegree)
+ {
+ g = g.quotient(h);
+ }
+ else
+ {
+ // ... else g(t) <- h(t)
+ g = new GF2nPolynomial(h);
+ }
+ gDegree = g.getDegree();
+ }
+ // 3. Output g(0)
+ return g.at(0);
+
+ }
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected void computeCOBMatrix(GF2nField B1)
+ {
+ // we are in B0 here!
+ if (mDegree != B1.mDegree)
+ {
+ throw new IllegalArgumentException(
+ "GF2nPolynomialField.computeCOBMatrix: B1 has a different "
+ + "degree and thus cannot be coverted to!");
+ }
+ if (B1 instanceof GF2nONBField)
+ {
+ // speedup (calculation is done in PolynomialElements instead of
+ // ONB)
+ B1.computeCOBMatrix(this);
+ return;
+ }
+ int i, j;
+ GF2nElement[] gamma;
+ GF2nElement u;
+ GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < mDegree; i++)
+ {
+ COBMatrix[i] = new GF2Polynomial(mDegree);
+ }
+
+ // find Random Root
+ do
+ {
+ // u is in representation according to B1
+ u = B1.getRandomRoot(fieldPolynomial);
+ }
+ while (u.isZero());
+
+ // build gamma matrix by multiplying by u
+ if (u instanceof GF2nONBElement)
+ {
+ gamma = new GF2nONBElement[mDegree];
+ gamma[mDegree - 1] = GF2nONBElement.ONE((GF2nONBField)B1);
+ }
+ else
+ {
+ gamma = new GF2nPolynomialElement[mDegree];
+ gamma[mDegree - 1] = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)B1);
+ }
+ gamma[mDegree - 2] = u;
+ for (i = mDegree - 3; i >= 0; i--)
+ {
+ gamma[i] = (GF2nElement)gamma[i + 1].multiply(u);
+ }
+ if (B1 instanceof GF2nONBField)
+ {
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ // TODO remember: ONB treats its Bits in reverse order !!!
+ if (gamma[i].testBit(mDegree - j - 1))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ if (gamma[i].testBit(j))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+ }
+
+ // store field and matrix for further use
+ fields.addElement(B1);
+ matrices.addElement(COBMatrix);
+ // store field and inverse matrix for further use in B1
+ B1.fields.addElement(this);
+ B1.matrices.addElement(invertMatrix(COBMatrix));
+ }
+
+ /**
+ * Computes a new squaring matrix used for fast squaring.
+ *
+ * @see GF2nPolynomialElement#square
+ */
+ private void computeSquaringMatrix()
+ {
+ GF2Polynomial[] d = new GF2Polynomial[mDegree - 1];
+ int i, j;
+ squaringMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < squaringMatrix.length; i++)
+ {
+ squaringMatrix[i] = new GF2Polynomial(mDegree, "ZERO");
+ }
+
+ for (i = 0; i < mDegree - 1; i++)
+ {
+ d[i] = new GF2Polynomial(1, "ONE").shiftLeft(mDegree + i)
+ .remainder(fieldPolynomial);
+ }
+ for (i = 1; i <= Math.abs(mDegree >> 1); i++)
+ {
+ for (j = 1; j <= mDegree; j++)
+ {
+ if (d[mDegree - (i << 1)].testBit(mDegree - j))
+ {
+ squaringMatrix[j - 1].setBit(mDegree - i);
+ }
+ }
+ }
+ for (i = Math.abs(mDegree >> 1) + 1; i <= mDegree; i++)
+ {
+ squaringMatrix[(i << 1) - mDegree - 1].setBit(mDegree - i);
+ }
+
+ }
+
+ /**
+ * Computes the field polynomial. This can take a long time for big degrees.
+ */
+ protected void computeFieldPolynomial()
+ {
+ if (testTrinomials())
+ {
+ return;
+ }
+ if (testPentanomials())
+ {
+ return;
+ }
+ testRandom();
+ }
+
+ /**
+ * Computes the field polynomial. This can take a long time for big degrees.
+ */
+ protected void computeFieldPolynomial2()
+ {
+ if (testTrinomials())
+ {
+ return;
+ }
+ if (testPentanomials())
+ {
+ return;
+ }
+ testRandom();
+ }
+
+ /**
+ * Tests all trinomials of degree (n+1) until a irreducible is found and
+ * stores the result in field polynomial. Returns false if no
+ * irreducible trinomial exists in GF(2^n). This can take very long for huge
+ * degrees.
+ *
+ * @return true if an irreducible trinomial is found
+ */
+ private boolean testTrinomials()
+ {
+ int i, l;
+ boolean done = false;
+ l = 0;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ fieldPolynomial.setBit(0);
+ fieldPolynomial.setBit(mDegree);
+ for (i = 1; (i < mDegree) && !done; i++)
+ {
+ fieldPolynomial.setBit(i);
+ done = fieldPolynomial.isIrreducible();
+ l++;
+ if (done)
+ {
+ isTrinomial = true;
+ tc = i;
+ return done;
+ }
+ fieldPolynomial.resetBit(i);
+ done = fieldPolynomial.isIrreducible();
+ }
+
+ return done;
+ }
+
+ /**
+ * Tests all pentanomials of degree (n+1) until a irreducible is found and
+ * stores the result in field polynomial. Returns false if no
+ * irreducible pentanomial exists in GF(2^n). This can take very long for
+ * huge degrees.
+ *
+ * @return true if an irreducible pentanomial is found
+ */
+ private boolean testPentanomials()
+ {
+ int i, j, k, l;
+ boolean done = false;
+ l = 0;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ fieldPolynomial.setBit(0);
+ fieldPolynomial.setBit(mDegree);
+ for (i = 1; (i <= (mDegree - 3)) && !done; i++)
+ {
+ fieldPolynomial.setBit(i);
+ for (j = i + 1; (j <= (mDegree - 2)) && !done; j++)
+ {
+ fieldPolynomial.setBit(j);
+ for (k = j + 1; (k <= (mDegree - 1)) && !done; k++)
+ {
+ fieldPolynomial.setBit(k);
+ if (((mDegree & 1) != 0) | ((i & 1) != 0) | ((j & 1) != 0)
+ | ((k & 1) != 0))
+ {
+ done = fieldPolynomial.isIrreducible();
+ l++;
+ if (done)
+ {
+ isPentanomial = true;
+ pc[0] = i;
+ pc[1] = j;
+ pc[2] = k;
+ return done;
+ }
+ }
+ fieldPolynomial.resetBit(k);
+ }
+ fieldPolynomial.resetBit(j);
+ }
+ fieldPolynomial.resetBit(i);
+ }
+
+ return done;
+ }
+
+ /**
+ * Tests random polynomials of degree (n+1) until an irreducible is found
+ * and stores the result in field polynomial. This can take very
+ * long for huge degrees.
+ *
+ * @return true
+ */
+ private boolean testRandom()
+ {
+ int l;
+ boolean done = false;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ l = 0;
+ while (!done)
+ {
+ l++;
+ fieldPolynomial.randomize();
+ fieldPolynomial.setBit(mDegree);
+ fieldPolynomial.setBit(0);
+ if (fieldPolynomial.isIrreducible())
+ {
+ done = true;
+ return done;
+ }
+ }
+
+ return done;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java
new file mode 100644
index 000000000..c33f19565
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java
@@ -0,0 +1,158 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+
+/**
+ * This interface defines a finite field element. It is implemented by the
+ * classes {@link GFPElement} and {@link GF2nElement}.
+ *
+ * @see GFPElement
+ * @see GF2nElement
+ */
+public interface GFElement
+{
+
+ /**
+ * @return a copy of this GFElement
+ */
+ Object clone();
+
+ // /////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Compare this curve with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ boolean equals(Object other);
+
+ /**
+ * @return the hash code of this element
+ */
+ int hashCode();
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return true if this is the zero element
+ */
+ boolean isZero();
+
+ /**
+ * Checks whether this element is one.
+ *
+ * @return true if this is the one element
+ */
+ boolean isOne();
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the sum of this element and the addend.
+ *
+ * @param addend the addend
+ * @return this + other (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement add(GFElement addend)
+ throws RuntimeException;
+
+ /**
+ * Compute the sum of this element and the addend, overwriting this element.
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void addToThis(GFElement addend)
+ throws RuntimeException;
+
+ /**
+ * Compute the difference of this element and minuend.
+ *
+ * @param minuend the minuend
+ * @return this - minuend (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement subtract(GFElement minuend)
+ throws RuntimeException;
+
+ /**
+ * Compute the difference of this element and minuend,
+ * overwriting this element.
+ *
+ * @param minuend the minuend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void subtractFromThis(GFElement minuend);
+
+ /**
+ * Compute the product of this element and factor.
+ *
+ * @param factor the factor
+ * @return this * factor (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement multiply(GFElement factor)
+ throws RuntimeException;
+
+ /**
+ * Compute this * factor (overwrite this).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void multiplyThisBy(GFElement factor)
+ throws RuntimeException;
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return this-1 (newly created)
+ * @throws ArithmeticException if this is the zero element.
+ */
+ GFElement invert()
+ throws ArithmeticException;
+
+ // /////////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns this element as FlexiBigInt. The conversion is P1363-conform.
+ *
+ * @return this element as BigInt
+ */
+ BigInteger toFlexiBigInt();
+
+ /**
+ * Returns this element as byte array. The conversion is P1363-conform.
+ *
+ * @return this element as byte array
+ */
+ byte[] toByteArray();
+
+ /**
+ * Return a String representation of this element.
+ *
+ * @return String representation of this element
+ */
+ String toString();
+
+ /**
+ * Return a String representation of this element. radix
+ * specifies the radix of the String representation.
+ *
+ * @param radix specifies the radix of the String representation
+ * @return String representation of this element with the specified radix
+ */
+ String toString(int radix);
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java
new file mode 100644
index 000000000..2249d936e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java
@@ -0,0 +1,310 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes decoding operations of an irreducible binary Goppa code.
+ * A check matrix H of the Goppa code and an irreducible Goppa polynomial are
+ * used the operations are worked over a finite field GF(2^m)
+ *
+ * @see GF2mField
+ * @see PolynomialGF2mSmallM
+ */
+public final class GoppaCode
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private GoppaCode()
+ {
+ // empty
+ }
+
+ /**
+ * This class is a container for two instances of {@link GF2Matrix} and one
+ * instance of {@link Permutation}. It is used to hold the systematic form
+ * S*H*P = (Id|M) of the check matrix H as returned by
+ * {@link GoppaCode#computeSystematicForm(GF2Matrix, SecureRandom)}.
+ *
+ * @see GF2Matrix
+ * @see Permutation
+ */
+ public static class MaMaPe
+ {
+
+ private GF2Matrix s, h;
+
+ private Permutation p;
+
+ /**
+ * Construct a new {@link MaMaPe} container with the given parameters.
+ *
+ * @param s the first matrix
+ * @param h the second matrix
+ * @param p the permutation
+ */
+ public MaMaPe(GF2Matrix s, GF2Matrix h, Permutation p)
+ {
+ this.s = s;
+ this.h = h;
+ this.p = p;
+ }
+
+ /**
+ * @return the first matrix
+ */
+ public GF2Matrix getFirstMatrix()
+ {
+ return s;
+ }
+
+ /**
+ * @return the second matrix
+ */
+ public GF2Matrix getSecondMatrix()
+ {
+ return h;
+ }
+
+ /**
+ * @return the permutation
+ */
+ public Permutation getPermutation()
+ {
+ return p;
+ }
+ }
+
+ /**
+ * This class is a container for an instance of {@link GF2Matrix} and one
+ * int[]. It is used to hold a generator matrix and the set of indices such
+ * that the submatrix of the generator matrix consisting of the specified
+ * columns is the identity.
+ *
+ * @see GF2Matrix
+ * @see Permutation
+ */
+ public static class MatrixSet
+ {
+
+ private GF2Matrix g;
+
+ private int[] setJ;
+
+ /**
+ * Construct a new {@link MatrixSet} container with the given
+ * parameters.
+ *
+ * @param g the generator matrix
+ * @param setJ the set of indices such that the submatrix of the
+ * generator matrix consisting of the specified columns
+ * is the identity
+ */
+ public MatrixSet(GF2Matrix g, int[] setJ)
+ {
+ this.g = g;
+ this.setJ = setJ;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getG()
+ {
+ return g;
+ }
+
+ /**
+ * @return the set of indices such that the submatrix of the generator
+ * matrix consisting of the specified columns is the identity
+ */
+ public int[] getSetJ()
+ {
+ return setJ;
+ }
+ }
+
+ /**
+ * Construct the check matrix of a Goppa code in canonical form from the
+ * irreducible Goppa polynomial over the finite field
+ * GF(2m).
+ *
+ * @param field the finite field
+ * @param gp the irreducible Goppa polynomial
+ */
+ public static GF2Matrix createCanonicalCheckMatrix(GF2mField field,
+ PolynomialGF2mSmallM gp)
+ {
+ int m = field.getDegree();
+ int n = 1 << m;
+ int t = gp.getDegree();
+
+ /* create matrix H over GF(2^m) */
+
+ int[][] hArray = new int[t][n];
+
+ // create matrix YZ
+ int[][] yz = new int[t][n];
+ for (int j = 0; j < n; j++)
+ {
+ // here j is used as index and as element of field GF(2^m)
+ yz[0][j] = field.inverse(gp.evaluateAt(j));
+ }
+
+ for (int i = 1; i < t; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ // here j is used as index and as element of field GF(2^m)
+ yz[i][j] = field.mult(yz[i - 1][j], j);
+ }
+ }
+
+ // create matrix H = XYZ
+ for (int i = 0; i < t; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ for (int k = 0; k <= i; k++)
+ {
+ hArray[i][j] = field.add(hArray[i][j], field.mult(yz[k][j],
+ gp.getCoefficient(t + k - i)));
+ }
+ }
+ }
+
+ /* convert to matrix over GF(2) */
+
+ int[][] result = new int[t * m][(n + 31) >>> 5];
+
+ for (int j = 0; j < n; j++)
+ {
+ int q = j >>> 5;
+ int r = 1 << (j & 0x1f);
+ for (int i = 0; i < t; i++)
+ {
+ int e = hArray[i][j];
+ for (int u = 0; u < m; u++)
+ {
+ int b = (e >>> u) & 1;
+ if (b != 0)
+ {
+ int ind = (i + 1) * m - u - 1;
+ result[ind][q] ^= r;
+ }
+ }
+ }
+ }
+
+ return new GF2Matrix(n, result);
+ }
+
+ /**
+ * Given a check matrix H, compute matrices S,
+ * M, and a random permutation P such that
+ * S*H*P = (Id|M). Return S^-1, M, and
+ * P as {@link MaMaPe}. The matrix (Id | M) is called
+ * the systematic form of H.
+ *
+ * @param h the check matrix
+ * @param sr a source of randomness
+ * @return the tuple (S^-1, M, P)
+ */
+ public static MaMaPe computeSystematicForm(GF2Matrix h, SecureRandom sr)
+ {
+ int n = h.getNumColumns();
+ GF2Matrix hp, sInv;
+ GF2Matrix s = null;
+ Permutation p;
+ boolean found = false;
+
+ do
+ {
+ p = new Permutation(n, sr);
+ hp = (GF2Matrix)h.rightMultiply(p);
+ sInv = hp.getLeftSubMatrix();
+ try
+ {
+ found = true;
+ s = (GF2Matrix)sInv.computeInverse();
+ }
+ catch (ArithmeticException ae)
+ {
+ found = false;
+ }
+ }
+ while (!found);
+
+ GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp);
+ GF2Matrix m = shp.getRightSubMatrix();
+
+ return new MaMaPe(sInv, m, p);
+ }
+
+ /**
+ * Find an error vector e over GF(2) from an input
+ * syndrome s over GF(2m).
+ *
+ * @param syndVec the syndrome
+ * @param field the finite field
+ * @param gp the irreducible Goppa polynomial
+ * @param sqRootMatrix the matrix for computing square roots in
+ * (GF(2m))t
+ * @return the error vector
+ */
+ public static GF2Vector syndromeDecode(GF2Vector syndVec, GF2mField field,
+ PolynomialGF2mSmallM gp, PolynomialGF2mSmallM[] sqRootMatrix)
+ {
+
+ int n = 1 << field.getDegree();
+
+ // the error vector
+ GF2Vector errors = new GF2Vector(n);
+
+ // if the syndrome vector is zero, the error vector is also zero
+ if (!syndVec.isZero())
+ {
+ // convert syndrome vector to polynomial over GF(2^m)
+ PolynomialGF2mSmallM syndrome = new PolynomialGF2mSmallM(syndVec
+ .toExtensionFieldVector(field));
+
+ // compute T = syndrome^-1 mod gp
+ PolynomialGF2mSmallM t = syndrome.modInverse(gp);
+
+ // compute tau = sqRoot(T + X) mod gp
+ PolynomialGF2mSmallM tau = t.addMonomial(1);
+ tau = tau.modSquareRootMatrix(sqRootMatrix);
+
+ // compute polynomials a and b satisfying a + b*tau = 0 mod gp
+ PolynomialGF2mSmallM[] ab = tau.modPolynomialToFracton(gp);
+
+ // compute the polynomial a^2 + X*b^2
+ PolynomialGF2mSmallM a2 = ab[0].multiply(ab[0]);
+ PolynomialGF2mSmallM b2 = ab[1].multiply(ab[1]);
+ PolynomialGF2mSmallM xb2 = b2.multWithMonomial(1);
+ PolynomialGF2mSmallM a2plusXb2 = a2.add(xb2);
+
+ // normalize a^2 + X*b^2 to obtain the error locator polynomial
+ int headCoeff = a2plusXb2.getHeadCoefficient();
+ int invHeadCoeff = field.inverse(headCoeff);
+ PolynomialGF2mSmallM elp = a2plusXb2.multWithElement(invHeadCoeff);
+
+ // for all elements i of GF(2^m)
+ for (int i = 0; i < n; i++)
+ {
+ // evaluate the error locator polynomial at i
+ int z = elp.evaluateAt(i);
+ // if polynomial evaluates to zero
+ if (z == 0)
+ {
+ // set the i-th coefficient of the error vector
+ errors.setBit(i);
+ }
+ }
+ }
+
+ return errors;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java
new file mode 100644
index 000000000..f031abf05
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java
@@ -0,0 +1,203 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+/**
+ *
+ *
+ *
+ */
+public final class IntUtils
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private IntUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Compare two int arrays. No null checks are performed.
+ *
+ * @param left the first int array
+ * @param right the second int array
+ * @return the result of the comparison
+ */
+ public static boolean equals(int[] left, int[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Return a clone of the given int array. No null checks are performed.
+ *
+ * @param array the array to clone
+ * @return the clone of the given array
+ */
+ public static int[] clone(int[] array)
+ {
+ int[] result = new int[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Fill the given int array with the given value.
+ *
+ * @param array the array
+ * @param value the value
+ */
+ public static void fill(int[] array, int value)
+ {
+ for (int i = array.length - 1; i >= 0; i--)
+ {
+ array[i] = value;
+ }
+ }
+
+ /**
+ * Sorts this array of integers according to the Quicksort algorithm. After
+ * calling this method this array is sorted in ascending order with the
+ * smallest integer taking position 0 in the array.
+ *
+ *
+ * This implementation is based on the quicksort algorithm as described in
+ * Data Structures In Java
by Thomas A. Standish, Chapter 10,
+ * ISBN 0-201-30564-X.
+ *
+ * @param source the array of integers that needs to be sorted.
+ */
+ public static void quicksort(int[] source)
+ {
+ quicksort(source, 0, source.length - 1);
+ }
+
+ /**
+ * Sort a subarray of a source array. The subarray is specified by its start
+ * and end index.
+ *
+ * @param source the int array to be sorted
+ * @param left the start index of the subarray
+ * @param right the end index of the subarray
+ */
+ public static void quicksort(int[] source, int left, int right)
+ {
+ if (right > left)
+ {
+ int index = partition(source, left, right, right);
+ quicksort(source, left, index - 1);
+ quicksort(source, index + 1, right);
+ }
+ }
+
+ /**
+ * Split a subarray of a source array into two partitions. The left
+ * partition contains elements that have value less than or equal to the
+ * pivot element, the right partition contains the elements that have larger
+ * value.
+ *
+ * @param source the int array whose subarray will be splitted
+ * @param left the start position of the subarray
+ * @param right the end position of the subarray
+ * @param pivotIndex the index of the pivot element inside the array
+ * @return the new index of the pivot element inside the array
+ */
+ private static int partition(int[] source, int left, int right,
+ int pivotIndex)
+ {
+
+ int pivot = source[pivotIndex];
+ source[pivotIndex] = source[right];
+ source[right] = pivot;
+
+ int index = left;
+
+ for (int i = left; i < right; i++)
+ {
+ if (source[i] <= pivot)
+ {
+ int tmp = source[index];
+ source[index] = source[i];
+ source[i] = tmp;
+ index++;
+ }
+ }
+
+ int tmp = source[index];
+ source[index] = source[right];
+ source[right] = tmp;
+
+ return index;
+ }
+
+ /**
+ * Generates a subarray of a given int array.
+ *
+ * @param input -
+ * the input int array
+ * @param start -
+ * the start index
+ * @param end -
+ * the end index
+ * @return a subarray of input, ranging from start to
+ * end
+ */
+ public static int[] subArray(final int[] input, final int start,
+ final int end)
+ {
+ int[] result = new int[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Convert an int array to a {@link FlexiBigInt} array.
+ *
+ * @param input the int array
+ * @return the {@link FlexiBigInt} array
+ */
+ public static BigInteger[] toFlexiBigIntArray(int[] input)
+ {
+ BigInteger[] result = new BigInteger[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = BigInteger.valueOf(input[i]);
+ }
+ return result;
+ }
+
+ /**
+ * @param input an int array
+ * @return a human readable form of the given int array
+ */
+ public static String toString(int[] input)
+ {
+ String result = "";
+ for (int i = 0; i < input.length; i++)
+ {
+ result += input[i] + " ";
+ }
+ return result;
+ }
+
+ /**
+ * @param input an int arary
+ * @return the int array as hex string
+ */
+ public static String toHexString(int[] input)
+ {
+ return ByteUtils.toHexString(BigEndianConversions.toByteArray(input));
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java
new file mode 100644
index 000000000..ca004311b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java
@@ -0,0 +1,1424 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Class of number-theory related functions for use with integers represented as
+ * int's or BigInteger objects.
+ */
+public final class IntegerFunctions
+{
+
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ private static final BigInteger FOUR = BigInteger.valueOf(4);
+
+ private static final int[] SMALL_PRIMES = {3, 5, 7, 11, 13, 17, 19, 23,
+ 29, 31, 37, 41};
+
+ private static final long SMALL_PRIME_PRODUCT = 3L * 5 * 7 * 11 * 13 * 17
+ * 19 * 23 * 29 * 31 * 37 * 41;
+
+ private static SecureRandom sr = null;
+
+ // the jacobi function uses this lookup table
+ private static final int[] jacobiTable = {0, 1, 0, -1, 0, -1, 0, 1};
+
+ private IntegerFunctions()
+ {
+ // empty
+ }
+
+ /**
+ * Computes the value of the Jacobi symbol (A|B). The following properties
+ * hold for the Jacobi symbol which makes it a very efficient way to
+ * evaluate the Legendre symbol
+ *
+ * (A|B) = 0 IF gcd(A,B) > 1
+ * (-1|B) = 1 IF n = 1 (mod 1)
+ * (-1|B) = -1 IF n = 3 (mod 4)
+ * (A|B) (C|B) = (AC|B)
+ * (A|B) (A|C) = (A|CB)
+ * (A|B) = (C|B) IF A = C (mod B)
+ * (2|B) = 1 IF N = 1 OR 7 (mod 8)
+ * (2|B) = 1 IF N = 3 OR 5 (mod 8)
+ *
+ *
+ * @param A integer value
+ * @param B integer value
+ * @return value of the jacobi symbol (A|B)
+ */
+ public static int jacobi(BigInteger A, BigInteger B)
+ {
+ BigInteger a, b, v;
+ long k = 1;
+
+ k = 1;
+
+ // test trivial cases
+ if (B.equals(ZERO))
+ {
+ a = A.abs();
+ return a.equals(ONE) ? 1 : 0;
+ }
+
+ if (!A.testBit(0) && !B.testBit(0))
+ {
+ return 0;
+ }
+
+ a = A;
+ b = B;
+
+ if (b.signum() == -1)
+ { // b < 0
+ b = b.negate(); // b = -b
+ if (a.signum() == -1)
+ {
+ k = -1;
+ }
+ }
+
+ v = ZERO;
+ while (!b.testBit(0))
+ {
+ v = v.add(ONE); // v = v + 1
+ b = b.divide(TWO); // b = b/2
+ }
+
+ if (v.testBit(0))
+ {
+ k = k * jacobiTable[a.intValue() & 7];
+ }
+
+ if (a.signum() < 0)
+ { // a < 0
+ if (b.testBit(1))
+ {
+ k = -k; // k = -k
+ }
+ a = a.negate(); // a = -a
+ }
+
+ // main loop
+ while (a.signum() != 0)
+ {
+ v = ZERO;
+ while (!a.testBit(0))
+ { // a is even
+ v = v.add(ONE);
+ a = a.divide(TWO);
+ }
+ if (v.testBit(0))
+ {
+ k = k * jacobiTable[b.intValue() & 7];
+ }
+
+ if (a.compareTo(b) < 0)
+ { // a < b
+ // swap and correct intermediate result
+ BigInteger x = a;
+ a = b;
+ b = x;
+ if (a.testBit(1) && b.testBit(1))
+ {
+ k = -k;
+ }
+ }
+ a = a.subtract(b);
+ }
+
+ return b.equals(ONE) ? (int)k : 0;
+ }
+
+ /**
+ * Computes the square root of a BigInteger modulo a prime employing the
+ * Shanks-Tonelli algorithm.
+ *
+ * @param a value out of which we extract the square root
+ * @param p prime modulus that determines the underlying field
+ * @return a number b such that b2 = a (mod p) if
+ * a is a quadratic residue modulo p.
+ * @throws NoQuadraticResidueException if a is a quadratic non-residue modulo p
+ */
+ public static BigInteger ressol(BigInteger a, BigInteger p)
+ throws IllegalArgumentException
+ {
+
+ BigInteger v = null;
+
+ if (a.compareTo(ZERO) < 0)
+ {
+ a = a.add(p);
+ }
+
+ if (a.equals(ZERO))
+ {
+ return ZERO;
+ }
+
+ if (p.equals(TWO))
+ {
+ return a;
+ }
+
+ // p = 3 mod 4
+ if (p.testBit(0) && p.testBit(1))
+ {
+ if (jacobi(a, p) == 1)
+ { // a quadr. residue mod p
+ v = p.add(ONE); // v = p+1
+ v = v.shiftRight(2); // v = v/4
+ return a.modPow(v, p); // return a^v mod p
+ // return --> a^((p+1)/4) mod p
+ }
+ throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p);
+ }
+
+ long t = 0;
+
+ // initialization
+ // compute k and s, where p = 2^s (2k+1) +1
+
+ BigInteger k = p.subtract(ONE); // k = p-1
+ long s = 0;
+ while (!k.testBit(0))
+ { // while k is even
+ s++; // s = s+1
+ k = k.shiftRight(1); // k = k/2
+ }
+
+ k = k.subtract(ONE); // k = k - 1
+ k = k.shiftRight(1); // k = k/2
+
+ // initial values
+ BigInteger r = a.modPow(k, p); // r = a^k mod p
+
+ BigInteger n = r.multiply(r).remainder(p); // n = r^2 % p
+ n = n.multiply(a).remainder(p); // n = n * a % p
+ r = r.multiply(a).remainder(p); // r = r * a %p
+
+ if (n.equals(ONE))
+ {
+ return r;
+ }
+
+ // non-quadratic residue
+ BigInteger z = TWO; // z = 2
+ while (jacobi(z, p) == 1)
+ {
+ // while z quadratic residue
+ z = z.add(ONE); // z = z + 1
+ }
+
+ v = k;
+ v = v.multiply(TWO); // v = 2k
+ v = v.add(ONE); // v = 2k + 1
+ BigInteger c = z.modPow(v, p); // c = z^v mod p
+
+ // iteration
+ while (n.compareTo(ONE) == 1)
+ { // n > 1
+ k = n; // k = n
+ t = s; // t = s
+ s = 0;
+
+ while (!k.equals(ONE))
+ { // k != 1
+ k = k.multiply(k).mod(p); // k = k^2 % p
+ s++; // s = s + 1
+ }
+
+ t -= s; // t = t - s
+ if (t == 0)
+ {
+ throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p);
+ }
+
+ v = ONE;
+ for (long i = 0; i < t - 1; i++)
+ {
+ v = v.shiftLeft(1); // v = 1 * 2^(t - 1)
+ }
+ c = c.modPow(v, p); // c = c^v mod p
+ r = r.multiply(c).remainder(p); // r = r * c % p
+ c = c.multiply(c).remainder(p); // c = c^2 % p
+ n = n.multiply(c).mod(p); // n = n * c % p
+ }
+ return r;
+ }
+
+ /**
+ * Computes the greatest common divisor of the two specified integers
+ *
+ * @param u - first integer
+ * @param v - second integer
+ * @return gcd(a, b)
+ */
+ public static int gcd(int u, int v)
+ {
+ return BigInteger.valueOf(u).gcd(BigInteger.valueOf(v)).intValue();
+ }
+
+ /**
+ * Extended euclidian algorithm (computes gcd and representation).
+ *
+ * @param a the first integer
+ * @param b the second integer
+ * @return (g,u,v), where g = gcd(abs(a),abs(b)) = ua + vb
+ */
+ public static int[] extGCD(int a, int b)
+ {
+ BigInteger ba = BigInteger.valueOf(a);
+ BigInteger bb = BigInteger.valueOf(b);
+ BigInteger[] bresult = extgcd(ba, bb);
+ int[] result = new int[3];
+ result[0] = bresult[0].intValue();
+ result[1] = bresult[1].intValue();
+ result[2] = bresult[2].intValue();
+ return result;
+ }
+
+ public static BigInteger divideAndRound(BigInteger a, BigInteger b)
+ {
+ if (a.signum() < 0)
+ {
+ return divideAndRound(a.negate(), b).negate();
+ }
+ if (b.signum() < 0)
+ {
+ return divideAndRound(a, b.negate()).negate();
+ }
+ return a.shiftLeft(1).add(b).divide(b.shiftLeft(1));
+ }
+
+ public static BigInteger[] divideAndRound(BigInteger[] a, BigInteger b)
+ {
+ BigInteger[] out = new BigInteger[a.length];
+ for (int i = 0; i < a.length; i++)
+ {
+ out[i] = divideAndRound(a[i], b);
+ }
+ return out;
+ }
+
+ /**
+ * Compute the smallest integer that is greater than or equal to the
+ * logarithm to the base 2 of the given BigInteger.
+ *
+ * @param a the integer
+ * @return ceil[log(a)]
+ */
+ public static int ceilLog(BigInteger a)
+ {
+ int result = 0;
+ BigInteger p = ONE;
+ while (p.compareTo(a) < 0)
+ {
+ result++;
+ p = p.shiftLeft(1);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the smallest integer that is greater than or equal to the
+ * logarithm to the base 2 of the given integer.
+ *
+ * @param a the integer
+ * @return ceil[log(a)]
+ */
+ public static int ceilLog(int a)
+ {
+ int log = 0;
+ int i = 1;
+ while (i < a)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * Compute ceil(log_256 n), the number of bytes needed to encode
+ * the integer n.
+ *
+ * @param n the integer
+ * @return the number of bytes needed to encode n
+ */
+ public static int ceilLog256(int n)
+ {
+ if (n == 0)
+ {
+ return 1;
+ }
+ int m;
+ if (n < 0)
+ {
+ m = -n;
+ }
+ else
+ {
+ m = n;
+ }
+
+ int d = 0;
+ while (m > 0)
+ {
+ d++;
+ m >>>= 8;
+ }
+ return d;
+ }
+
+ /**
+ * Compute ceil(log_256 n), the number of bytes needed to encode
+ * the long integer n.
+ *
+ * @param n the long integer
+ * @return the number of bytes needed to encode n
+ */
+ public static int ceilLog256(long n)
+ {
+ if (n == 0)
+ {
+ return 1;
+ }
+ long m;
+ if (n < 0)
+ {
+ m = -n;
+ }
+ else
+ {
+ m = n;
+ }
+
+ int d = 0;
+ while (m > 0)
+ {
+ d++;
+ m >>>= 8;
+ }
+ return d;
+ }
+
+ /**
+ * Compute the integer part of the logarithm to the base 2 of the given
+ * integer.
+ *
+ * @param a the integer
+ * @return floor[log(a)]
+ */
+ public static int floorLog(BigInteger a)
+ {
+ int result = -1;
+ BigInteger p = ONE;
+ while (p.compareTo(a) <= 0)
+ {
+ result++;
+ p = p.shiftLeft(1);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the integer part of the logarithm to the base 2 of the given
+ * integer.
+ *
+ * @param a the integer
+ * @return floor[log(a)]
+ */
+ public static int floorLog(int a)
+ {
+ int h = 0;
+ if (a <= 0)
+ {
+ return -1;
+ }
+ int p = a >>> 1;
+ while (p > 0)
+ {
+ h++;
+ p >>>= 1;
+ }
+
+ return h;
+ }
+
+ /**
+ * Compute the largest h with 2^h | a if a!=0.
+ *
+ * @param a an integer
+ * @return the largest h with 2^h | a if a!=0,
+ * 0 otherwise
+ */
+ public static int maxPower(int a)
+ {
+ int h = 0;
+ if (a != 0)
+ {
+ int p = 1;
+ while ((a & p) == 0)
+ {
+ h++;
+ p <<= 1;
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * @param a an integer
+ * @return the number of ones in the binary representation of an integer
+ * a
+ */
+ public static int bitCount(int a)
+ {
+ int h = 0;
+ while (a != 0)
+ {
+ h += a & 1;
+ a >>>= 1;
+ }
+
+ return h;
+ }
+
+ /**
+ * determines the order of g modulo p, p prime and 1 < g < p. This algorithm
+ * is only efficient for small p (see X9.62-1998, p. 68).
+ *
+ * @param g an integer with 1 < g < p
+ * @param p a prime
+ * @return the order k of g (that is k is the smallest integer with
+ * gk = 1 mod p
+ */
+ public static int order(int g, int p)
+ {
+ int b, j;
+
+ b = g % p; // Reduce g mod p first.
+ j = 1;
+
+ // Check whether g == 0 mod p (avoiding endless loop).
+ if (b == 0)
+ {
+ throw new IllegalArgumentException(g + " is not an element of Z/("
+ + p + "Z)^*; it is not meaningful to compute its order.");
+ }
+
+ // Compute the order of g mod p:
+ while (b != 1)
+ {
+ b *= g;
+ b %= p;
+ if (b < 0)
+ {
+ b += p;
+ }
+ j++;
+ }
+
+ return j;
+ }
+
+ /**
+ * Reduces an integer into a given interval
+ *
+ * @param n - the integer
+ * @param begin - left bound of the interval
+ * @param end - right bound of the interval
+ * @return n reduced into [begin,end]
+ */
+ public static BigInteger reduceInto(BigInteger n, BigInteger begin,
+ BigInteger end)
+ {
+ return n.subtract(begin).mod(end.subtract(begin)).add(begin);
+ }
+
+ /**
+ * Compute ae.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @return ae
+ */
+ public static int pow(int a, int e)
+ {
+ int result = 1;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result *= a;
+ }
+ a *= a;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Compute ae.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @return ae
+ */
+ public static long pow(long a, int e)
+ {
+ long result = 1;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result *= a;
+ }
+ a *= a;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Compute ae mod n.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @param n the modulus
+ * @return ae mod n
+ */
+ public static int modPow(int a, int e, int n)
+ {
+ if (n <= 0 || (n * n) > Integer.MAX_VALUE || e < 0)
+ {
+ return 0;
+ }
+ int result = 1;
+ a = (a % n + n) % n;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result = (result * a) % n;
+ }
+ a = (a * a) % n;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Extended euclidian algorithm (computes gcd and representation).
+ *
+ * @param a - the first integer
+ * @param b - the second integer
+ * @return (d,u,v), where d = gcd(a,b) = ua + vb
+ */
+ public static BigInteger[] extgcd(BigInteger a, BigInteger b)
+ {
+ BigInteger u = ONE;
+ BigInteger v = ZERO;
+ BigInteger d = a;
+ if (b.signum() != 0)
+ {
+ BigInteger v1 = ZERO;
+ BigInteger v3 = b;
+ while (v3.signum() != 0)
+ {
+ BigInteger[] tmp = d.divideAndRemainder(v3);
+ BigInteger q = tmp[0];
+ BigInteger t3 = tmp[1];
+ BigInteger t1 = u.subtract(q.multiply(v1));
+ u = v1;
+ d = v3;
+ v1 = t1;
+ v3 = t3;
+ }
+ v = d.subtract(a.multiply(u)).divide(b);
+ }
+ return new BigInteger[]{d, u, v};
+ }
+
+ /**
+ * Computation of the least common multiple of a set of BigIntegers.
+ *
+ * @param numbers - the set of numbers
+ * @return the lcm(numbers)
+ */
+ public static BigInteger leastCommonMultiple(BigInteger[] numbers)
+ {
+ int n = numbers.length;
+ BigInteger result = numbers[0];
+ for (int i = 1; i < n; i++)
+ {
+ BigInteger gcd = result.gcd(numbers[i]);
+ result = result.multiply(numbers[i]).divide(gcd);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a long integer whose value is (a mod m). This method
+ * differs from % in that it always returns a non-negative
+ * integer.
+ *
+ * @param a value on which the modulo operation has to be performed.
+ * @param m the modulus.
+ * @return a mod m
+ */
+ public static long mod(long a, long m)
+ {
+ long result = a % m;
+ if (result < 0)
+ {
+ result += m;
+ }
+ return result;
+ }
+
+ /**
+ * Computes the modular inverse of an integer a
+ *
+ * @param a - the integer to invert
+ * @param mod - the modulus
+ * @return a-1 mod n
+ */
+ public static int modInverse(int a, int mod)
+ {
+ return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod))
+ .intValue();
+ }
+
+ /**
+ * Computes the modular inverse of an integer a
+ *
+ * @param a - the integer to invert
+ * @param mod - the modulus
+ * @return a-1 mod n
+ */
+ public static long modInverse(long a, long mod)
+ {
+ return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod))
+ .longValue();
+ }
+
+ /**
+ * Tests whether an integer a is power of another integer
+ * p.
+ *
+ * @param a - the first integer
+ * @param p - the second integer
+ * @return n if a = p^n or -1 otherwise
+ */
+ public static int isPower(int a, int p)
+ {
+ if (a <= 0)
+ {
+ return -1;
+ }
+ int n = 0;
+ int d = a;
+ while (d > 1)
+ {
+ if (d % p != 0)
+ {
+ return -1;
+ }
+ d /= p;
+ n++;
+ }
+ return n;
+ }
+
+ /**
+ * Find and return the least non-trivial divisor of an integer a.
+ *
+ * @param a - the integer
+ * @return divisor p >1 or 1 if a = -1,0,1
+ */
+ public static int leastDiv(int a)
+ {
+ if (a < 0)
+ {
+ a = -a;
+ }
+ if (a == 0)
+ {
+ return 1;
+ }
+ if ((a & 1) == 0)
+ {
+ return 2;
+ }
+ int p = 3;
+ while (p <= (a / p))
+ {
+ if ((a % p) == 0)
+ {
+ return p;
+ }
+ p += 2;
+ }
+
+ return a;
+ }
+
+ /**
+ * Miller-Rabin-Test, determines wether the given integer is probably prime
+ * or composite. This method returns true if the given integer is
+ * prime with probability 1 - 2-20.
+ *
+ * @param n the integer to test for primality
+ * @return true if the given integer is prime with probability
+ * 2-100, false otherwise
+ */
+ public static boolean isPrime(int n)
+ {
+ if (n < 2)
+ {
+ return false;
+ }
+ if (n == 2)
+ {
+ return true;
+ }
+ if ((n & 1) == 0)
+ {
+ return false;
+ }
+ if (n < 42)
+ {
+ for (int i = 0; i < SMALL_PRIMES.length; i++)
+ {
+ if (n == SMALL_PRIMES[i])
+ {
+ return true;
+ }
+ }
+ }
+
+ if ((n % 3 == 0) || (n % 5 == 0) || (n % 7 == 0) || (n % 11 == 0)
+ || (n % 13 == 0) || (n % 17 == 0) || (n % 19 == 0)
+ || (n % 23 == 0) || (n % 29 == 0) || (n % 31 == 0)
+ || (n % 37 == 0) || (n % 41 == 0))
+ {
+ return false;
+ }
+
+ return BigInteger.valueOf(n).isProbablePrime(20);
+ }
+
+ /**
+ * Short trial-division test to find out whether a number is not prime. This
+ * test is usually used before a Miller-Rabin primality test.
+ *
+ * @param candidate the number to test
+ * @return true if the number has no factor of the tested primes,
+ * false if the number is definitely composite
+ */
+ public static boolean passesSmallPrimeTest(BigInteger candidate)
+ {
+ final int[] smallPrime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
+ 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
+ 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
+ 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
+ 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
+ 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449,
+ 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
+ 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
+ 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761,
+ 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
+ 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937,
+ 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019,
+ 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087,
+ 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153,
+ 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229,
+ 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297,
+ 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381,
+ 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453,
+ 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499};
+
+ for (int i = 0; i < smallPrime.length; i++)
+ {
+ if (candidate.mod(BigInteger.valueOf(smallPrime[i])).equals(
+ ZERO))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the largest prime smaller than the given integer
+ *
+ * @param n - upper bound
+ * @return the largest prime smaller than n, or 1 if
+ * n <= 2
+ */
+ public static int nextSmallerPrime(int n)
+ {
+ if (n <= 2)
+ {
+ return 1;
+ }
+
+ if (n == 3)
+ {
+ return 2;
+ }
+
+ if ((n & 1) == 0)
+ {
+ n--;
+ }
+ else
+ {
+ n -= 2;
+ }
+
+ while (n > 3 & !isPrime(n))
+ {
+ n -= 2;
+ }
+ return n;
+ }
+
+ /**
+ * Compute the next probable prime greater than n with the
+ * specified certainty.
+ *
+ * @param n a integer number
+ * @param certainty the certainty that the generated number is prime
+ * @return the next prime greater than n
+ */
+ public static BigInteger nextProbablePrime(BigInteger n, int certainty)
+ {
+
+ if (n.signum() < 0 || n.signum() == 0 || n.equals(ONE))
+ {
+ return TWO;
+ }
+
+ BigInteger result = n.add(ONE);
+
+ // Ensure an odd number
+ if (!result.testBit(0))
+ {
+ result = result.add(ONE);
+ }
+
+ while (true)
+ {
+ // Do cheap "pre-test" if applicable
+ if (result.bitLength() > 6)
+ {
+ long r = result.remainder(
+ BigInteger.valueOf(SMALL_PRIME_PRODUCT)).longValue();
+ if ((r % 3 == 0) || (r % 5 == 0) || (r % 7 == 0)
+ || (r % 11 == 0) || (r % 13 == 0) || (r % 17 == 0)
+ || (r % 19 == 0) || (r % 23 == 0) || (r % 29 == 0)
+ || (r % 31 == 0) || (r % 37 == 0) || (r % 41 == 0))
+ {
+ result = result.add(TWO);
+ continue; // Candidate is composite; try another
+ }
+ }
+
+ // All candidates of bitLength 2 and 3 are prime by this point
+ if (result.bitLength() < 4)
+ {
+ return result;
+ }
+
+ // The expensive test
+ if (result.isProbablePrime(certainty))
+ {
+ return result;
+ }
+
+ result = result.add(TWO);
+ }
+ }
+
+ /**
+ * Compute the next probable prime greater than n with the default
+ * certainty (20).
+ *
+ * @param n a integer number
+ * @return the next prime greater than n
+ */
+ public static BigInteger nextProbablePrime(BigInteger n)
+ {
+ return nextProbablePrime(n, 20);
+ }
+
+ /**
+ * Computes the next prime greater than n.
+ *
+ * @param n a integer number
+ * @return the next prime greater than n
+ */
+ public static BigInteger nextPrime(long n)
+ {
+ long i;
+ boolean found = false;
+ long result = 0;
+
+ if (n <= 1)
+ {
+ return BigInteger.valueOf(2);
+ }
+ if (n == 2)
+ {
+ return BigInteger.valueOf(3);
+ }
+
+ for (i = n + 1 + (n & 1); (i <= n << 1) && !found; i += 2)
+ {
+ for (long j = 3; (j <= i >> 1) && !found; j += 2)
+ {
+ if (i % j == 0)
+ {
+ found = true;
+ }
+ }
+ if (found)
+ {
+ found = false;
+ }
+ else
+ {
+ result = i;
+ found = true;
+ }
+ }
+ return BigInteger.valueOf(result);
+ }
+
+ /**
+ * Computes the binomial coefficient (n|t) ("n over t"). Formula:
+ *
+ *
+ *
+ * @param n - the "upper" integer
+ * @param t - the "lower" integer
+ * @return the binomialcoefficient "n over t" as BigInteger
+ */
+ public static BigInteger binomial(int n, int t)
+ {
+
+ BigInteger result = ONE;
+
+ if (n == 0)
+ {
+ if (t == 0)
+ {
+ return result;
+ }
+ return ZERO;
+ }
+
+ // the property (n|t) = (n|n-t) be used to reduce numbers of operations
+ if (t > (n >>> 1))
+ {
+ t = n - t;
+ }
+
+ for (int i = 1; i <= t; i++)
+ {
+ result = (result.multiply(BigInteger.valueOf(n - (i - 1))))
+ .divide(BigInteger.valueOf(i));
+ }
+
+ return result;
+ }
+
+ public static BigInteger randomize(BigInteger upperBound)
+ {
+ if (sr == null)
+ {
+ sr = new SecureRandom();
+ }
+ return randomize(upperBound, sr);
+ }
+
+ public static BigInteger randomize(BigInteger upperBound,
+ SecureRandom prng)
+ {
+ int blen = upperBound.bitLength();
+ BigInteger randomNum = BigInteger.valueOf(0);
+
+ if (prng == null)
+ {
+ prng = sr != null ? sr : new SecureRandom();
+ }
+
+ for (int i = 0; i < 20; i++)
+ {
+ randomNum = new BigInteger(blen, prng);
+ if (randomNum.compareTo(upperBound) < 0)
+ {
+ return randomNum;
+ }
+ }
+ return randomNum.mod(upperBound);
+ }
+
+ /**
+ * Extract the truncated square root of a BigInteger.
+ *
+ * @param a - value out of which we extract the square root
+ * @return the truncated square root of a
+ */
+ public static BigInteger squareRoot(BigInteger a)
+ {
+ int bl;
+ BigInteger result, remainder, b;
+
+ if (a.compareTo(ZERO) < 0)
+ {
+ throw new ArithmeticException(
+ "cannot extract root of negative number" + a + ".");
+ }
+
+ bl = a.bitLength();
+ result = ZERO;
+ remainder = ZERO;
+
+ // if the bit length is odd then extra step
+ if ((bl & 1) != 0)
+ {
+ result = result.add(ONE);
+ bl--;
+ }
+
+ while (bl > 0)
+ {
+ remainder = remainder.multiply(FOUR);
+ remainder = remainder.add(BigInteger.valueOf((a.testBit(--bl) ? 2
+ : 0)
+ + (a.testBit(--bl) ? 1 : 0)));
+ b = result.multiply(FOUR).add(ONE);
+ result = result.multiply(TWO);
+ if (remainder.compareTo(b) != -1)
+ {
+ result = result.add(ONE);
+ remainder = remainder.subtract(b);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Takes an approximation of the root from an integer base, using newton's
+ * algorithm
+ *
+ * @param base the base to take the root from
+ * @param root the root, for example 2 for a square root
+ */
+ public static float intRoot(int base, int root)
+ {
+ float gNew = base / root;
+ float gOld = 0;
+ int counter = 0;
+ while (Math.abs(gOld - gNew) > 0.0001)
+ {
+ float gPow = floatPow(gNew, root);
+ while (Float.isInfinite(gPow))
+ {
+ gNew = (gNew + gOld) / 2;
+ gPow = floatPow(gNew, root);
+ }
+ counter += 1;
+ gOld = gNew;
+ gNew = gOld - (gPow - base) / (root * floatPow(gOld, root - 1));
+ }
+ return gNew;
+ }
+
+ /**
+ * Calculation of a logarithmus of a float param
+ *
+ * @param param
+ * @return
+ */
+ public static float floatLog(float param)
+ {
+ double arg = (param - 1) / (param + 1);
+ double arg2 = arg;
+ int counter = 1;
+ float result = (float)arg;
+
+ while (arg2 > 0.001)
+ {
+ counter += 2;
+ arg2 *= arg * arg;
+ result += (1. / counter) * arg2;
+ }
+ return 2 * result;
+ }
+
+ /**
+ * int power of a base float, only use for small ints
+ *
+ * @param f
+ * @param i
+ * @return
+ */
+ public static float floatPow(float f, int i)
+ {
+ float g = 1;
+ for (; i > 0; i--)
+ {
+ g *= f;
+ }
+ return g;
+ }
+
+ /**
+ * calculate the logarithm to the base 2.
+ *
+ * @param x any double value
+ * @return log_2(x)
+ * @deprecated use MathFunctions.log(double) instead
+ */
+ public static double log(double x)
+ {
+ if (x > 0 && x < 1)
+ {
+ double d = 1 / x;
+ double result = -log(d);
+ return result;
+ }
+
+ int tmp = 0;
+ double tmp2 = 1;
+ double d = x;
+
+ while (d > 2)
+ {
+ d = d / 2;
+ tmp += 1;
+ tmp2 *= 2;
+ }
+ double rem = x / tmp2;
+ rem = logBKM(rem);
+ return tmp + rem;
+ }
+
+ /**
+ * calculate the logarithm to the base 2.
+ *
+ * @param x any long value >=1
+ * @return log_2(x)
+ * @deprecated use MathFunctions.log(long) instead
+ */
+ public static double log(long x)
+ {
+ int tmp = floorLog(BigInteger.valueOf(x));
+ long tmp2 = 1 << tmp;
+ double rem = (double)x / (double)tmp2;
+ rem = logBKM(rem);
+ return tmp + rem;
+ }
+
+ /**
+ * BKM Algorithm to calculate logarithms to the base 2.
+ *
+ * @param arg a double value with 1<= arg<= 4.768462058
+ * @return log_2(arg)
+ * @deprecated use MathFunctions.logBKM(double) instead
+ */
+ private static double logBKM(double arg)
+ {
+ double ae[] = // A_e[k] = log_2 (1 + 0.5^k)
+ {
+ 1.0000000000000000000000000000000000000000000000000000000000000000000000000000,
+ 0.5849625007211561814537389439478165087598144076924810604557526545410982276485,
+ 0.3219280948873623478703194294893901758648313930245806120547563958159347765589,
+ 0.1699250014423123629074778878956330175196288153849621209115053090821964552970,
+ 0.0874628412503394082540660108104043540112672823448206881266090643866965081686,
+ 0.0443941193584534376531019906736094674630459333742491317685543002674288465967,
+ 0.0223678130284545082671320837460849094932677948156179815932199216587899627785,
+ 0.0112272554232541203378805844158839407281095943600297940811823651462712311786,
+ 0.0056245491938781069198591026740666017211096815383520359072957784732489771013,
+ 0.0028150156070540381547362547502839489729507927389771959487826944878598909400,
+ 0.0014081943928083889066101665016890524233311715793462235597709051792834906001,
+ 0.0007042690112466432585379340422201964456668872087249334581924550139514213168,
+ 0.0003521774803010272377989609925281744988670304302127133979341729842842377649,
+ 0.0001760994864425060348637509459678580940163670081839283659942864068257522373,
+ 0.0000880524301221769086378699983597183301490534085738474534831071719854721939,
+ 0.0000440268868273167176441087067175806394819146645511899503059774914593663365,
+ 0.0000220136113603404964890728830697555571275493801909791504158295359319433723,
+ 0.0000110068476674814423006223021573490183469930819844945565597452748333526464,
+ 0.0000055034343306486037230640321058826431606183125807276574241540303833251704,
+ 0.0000027517197895612831123023958331509538486493412831626219340570294203116559,
+ 0.0000013758605508411382010566802834037147561973553922354232704569052932922954,
+ 0.0000006879304394358496786728937442939160483304056131990916985043387874690617,
+ 0.0000003439652607217645360118314743718005315334062644619363447395987584138324,
+ 0.0000001719826406118446361936972479533123619972434705828085978955697643547921,
+ 0.0000000859913228686632156462565208266682841603921494181830811515318381744650,
+ 0.0000000429956620750168703982940244684787907148132725669106053076409624949917,
+ 0.0000000214978311976797556164155504126645192380395989504741781512309853438587,
+ 0.0000000107489156388827085092095702361647949603617203979413516082280717515504,
+ 0.0000000053744578294520620044408178949217773318785601260677517784797554422804,
+ 0.0000000026872289172287079490026152352638891824761667284401180026908031182361,
+ 0.0000000013436144592400232123622589569799954658536700992739887706412976115422,
+ 0.0000000006718072297764289157920422846078078155859484240808550018085324187007,
+ 0.0000000003359036149273187853169587152657145221968468364663464125722491530858,
+ 0.0000000001679518074734354745159899223037458278711244127245990591908996412262,
+ 0.0000000000839759037391617577226571237484864917411614198675604731728132152582,
+ 0.0000000000419879518701918839775296677020135040214077417929807824842667285938,
+ 0.0000000000209939759352486932678195559552767641474249812845414125580747434389,
+ 0.0000000000104969879676625344536740142096218372850561859495065136990936290929,
+ 0.0000000000052484939838408141817781356260462777942148580518406975851213868092,
+ 0.0000000000026242469919227938296243586262369156865545638305682553644113887909,
+ 0.0000000000013121234959619935994960031017850191710121890821178731821983105443,
+ 0.0000000000006560617479811459709189576337295395590603644549624717910616347038,
+ 0.0000000000003280308739906102782522178545328259781415615142931952662153623493,
+ 0.0000000000001640154369953144623242936888032768768777422997704541618141646683,
+ 0.0000000000000820077184976595619616930350508356401599552034612281802599177300,
+ 0.0000000000000410038592488303636807330652208397742314215159774270270147020117,
+ 0.0000000000000205019296244153275153381695384157073687186580546938331088730952,
+ 0.0000000000000102509648122077001764119940017243502120046885379813510430378661,
+ 0.0000000000000051254824061038591928917243090559919209628584150482483994782302,
+ 0.0000000000000025627412030519318726172939815845367496027046030028595094737777,
+ 0.0000000000000012813706015259665053515049475574143952543145124550608158430592,
+ 0.0000000000000006406853007629833949364669629701200556369782295210193569318434,
+ 0.0000000000000003203426503814917330334121037829290364330169106716787999052925,
+ 0.0000000000000001601713251907458754080007074659337446341494733882570243497196,
+ 0.0000000000000000800856625953729399268240176265844257044861248416330071223615,
+ 0.0000000000000000400428312976864705191179247866966320469710511619971334577509,
+ 0.0000000000000000200214156488432353984854413866994246781519154793320684126179,
+ 0.0000000000000000100107078244216177339743404416874899847406043033792202127070,
+ 0.0000000000000000050053539122108088756700751579281894640362199287591340285355,
+ 0.0000000000000000025026769561054044400057638132352058574658089256646014899499,
+ 0.0000000000000000012513384780527022205455634651853807110362316427807660551208,
+ 0.0000000000000000006256692390263511104084521222346348012116229213309001913762,
+ 0.0000000000000000003128346195131755552381436585278035120438976487697544916191,
+ 0.0000000000000000001564173097565877776275512286165232838833090480508502328437,
+ 0.0000000000000000000782086548782938888158954641464170239072244145219054734086,
+ 0.0000000000000000000391043274391469444084776945327473574450334092075712154016,
+ 0.0000000000000000000195521637195734722043713378812583900953755962557525252782,
+ 0.0000000000000000000097760818597867361022187915943503728909029699365320287407,
+ 0.0000000000000000000048880409298933680511176764606054809062553340323879609794,
+ 0.0000000000000000000024440204649466840255609083961603140683286362962192177597,
+ 0.0000000000000000000012220102324733420127809717395445504379645613448652614939,
+ 0.0000000000000000000006110051162366710063906152551383735699323415812152114058,
+ 0.0000000000000000000003055025581183355031953399739107113727036860315024588989,
+ 0.0000000000000000000001527512790591677515976780735407368332862218276873443537,
+ 0.0000000000000000000000763756395295838757988410584167137033767056170417508383,
+ 0.0000000000000000000000381878197647919378994210346199431733717514843471513618,
+ 0.0000000000000000000000190939098823959689497106436628681671067254111334889005,
+ 0.0000000000000000000000095469549411979844748553534196582286585751228071408728,
+ 0.0000000000000000000000047734774705989922374276846068851506055906657137209047,
+ 0.0000000000000000000000023867387352994961187138442777065843718711089344045782,
+ 0.0000000000000000000000011933693676497480593569226324192944532044984865894525,
+ 0.0000000000000000000000005966846838248740296784614396011477934194852481410926,
+ 0.0000000000000000000000002983423419124370148392307506484490384140516252814304,
+ 0.0000000000000000000000001491711709562185074196153830361933046331030629430117,
+ 0.0000000000000000000000000745855854781092537098076934460888486730708440475045,
+ 0.0000000000000000000000000372927927390546268549038472050424734256652501673274,
+ 0.0000000000000000000000000186463963695273134274519237230207489851150821191330,
+ 0.0000000000000000000000000093231981847636567137259618916352525606281553180093,
+ 0.0000000000000000000000000046615990923818283568629809533488457973317312233323,
+ 0.0000000000000000000000000023307995461909141784314904785572277779202790023236,
+ 0.0000000000000000000000000011653997730954570892157452397493151087737428485431,
+ 0.0000000000000000000000000005826998865477285446078726199923328593402722606924,
+ 0.0000000000000000000000000002913499432738642723039363100255852559084863397344,
+ 0.0000000000000000000000000001456749716369321361519681550201473345138307215067,
+ 0.0000000000000000000000000000728374858184660680759840775119123438968122488047,
+ 0.0000000000000000000000000000364187429092330340379920387564158411083803465567,
+ 0.0000000000000000000000000000182093714546165170189960193783228378441837282509,
+ 0.0000000000000000000000000000091046857273082585094980096891901482445902524441,
+ 0.0000000000000000000000000000045523428636541292547490048446022564529197237262,
+ 0.0000000000000000000000000000022761714318270646273745024223029238091160103901};
+ int n = 53;
+ double x = 1;
+ double y = 0;
+ double z;
+ double s = 1;
+ int k;
+
+ for (k = 0; k < n; k++)
+ {
+ z = x + x * s;
+ if (z <= arg)
+ {
+ x = z;
+ y += ae[k];
+ }
+ s *= 0.5;
+ }
+ return y;
+ }
+
+ public static boolean isIncreasing(int[] a)
+ {
+ for (int i = 1; i < a.length; i++)
+ {
+ if (a[i - 1] >= a[i])
+ {
+ System.out.println("a[" + (i - 1) + "] = " + a[i - 1] + " >= "
+ + a[i] + " = a[" + i + "]");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static byte[] integerToOctets(BigInteger val)
+ {
+ byte[] valBytes = val.abs().toByteArray();
+
+ // check whether the array includes a sign bit
+ if ((val.bitLength() & 7) != 0)
+ {
+ return valBytes;
+ }
+ // get rid of the sign bit (first byte)
+ byte[] tmp = new byte[val.bitLength() >> 3];
+ System.arraycopy(valBytes, 1, tmp, 0, tmp.length);
+ return tmp;
+ }
+
+ public static BigInteger octetsToInteger(byte[] data, int offset,
+ int length)
+ {
+ byte[] val = new byte[length + 1];
+
+ val[0] = 0;
+ System.arraycopy(data, offset, val, 1, length);
+ return new BigInteger(val);
+ }
+
+ public static BigInteger octetsToInteger(byte[] data)
+ {
+ return octetsToInteger(data, 0, data.length);
+ }
+
+ public static void main(String[] args)
+ {
+ System.out.println("test");
+ // System.out.println(intRoot(37, 5));
+ // System.out.println(floatPow((float)2.5, 4));
+ System.out.println(floatLog(10));
+ System.out.println("test2");
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java
new file mode 100644
index 000000000..111695218
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java
@@ -0,0 +1,230 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This is a utility class containing data type conversions using little-endian
+ * byte order.
+ *
+ * @see BigEndianConversions
+ */
+public final class LittleEndianConversions
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private LittleEndianConversions()
+ {
+ // empty
+ }
+
+ /**
+ * Convert an octet string of length 4 to an integer. No length checking is
+ * performed.
+ *
+ * @param input the byte array holding the octet string
+ * @return an integer representing the octet string input
+ * @throws ArithmeticException if the length of the given octet string is larger than 4.
+ */
+ public static int OS2IP(byte[] input)
+ {
+ return ((input[0] & 0xff)) | ((input[1] & 0xff) << 8)
+ | ((input[2] & 0xff) << 16) | ((input[3] & 0xff)) << 24;
+ }
+
+ /**
+ * Convert an byte array of length 4 beginning at offset into an
+ * integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting integer
+ */
+ public static int OS2IP(byte[] input, int inOff)
+ {
+ int result = input[inOff++] & 0xff;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff] & 0xff) << 24;
+ return result;
+ }
+
+ /**
+ * Convert a byte array of the given length beginning at offset
+ * into an integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @param inLen the length of the encoding
+ * @return the resulting integer
+ */
+ public static int OS2IP(byte[] input, int inOff, int inLen)
+ {
+ int result = 0;
+ for (int i = inLen - 1; i >= 0; i--)
+ {
+ result |= (input[inOff + i] & 0xff) << (8 * i);
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array of length 8 beginning at inOff into a
+ * long integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting long integer
+ */
+ public static long OS2LIP(byte[] input, int inOff)
+ {
+ long result = input[inOff++] & 0xff;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= ((long)input[inOff++] & 0xff) << 24;
+ result |= ((long)input[inOff++] & 0xff) << 32;
+ result |= ((long)input[inOff++] & 0xff) << 40;
+ result |= ((long)input[inOff++] & 0xff) << 48;
+ result |= ((long)input[inOff++] & 0xff) << 56;
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4.
+ *
+ * @param x the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(int x)
+ {
+ byte[] result = new byte[4];
+ result[0] = (byte)x;
+ result[1] = (byte)(x >>> 8);
+ result[2] = (byte)(x >>> 16);
+ result[3] = (byte)(x >>> 24);
+ return result;
+ }
+
+ /**
+ * Convert an integer into a byte array beginning at the specified offset.
+ *
+ * @param value the integer to convert
+ * @param output the byte array to hold the result
+ * @param outOff the integer offset into the byte array
+ */
+ public static void I2OSP(int value, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)value;
+ output[outOff++] = (byte)(value >>> 8);
+ output[outOff++] = (byte)(value >>> 16);
+ output[outOff++] = (byte)(value >>> 24);
+ }
+
+ /**
+ * Convert an integer to a byte array beginning at the specified offset. No
+ * length checking is performed (i.e., if the integer cannot be encoded with
+ * length octets, it is truncated).
+ *
+ * @param value the integer to convert
+ * @param output the byte array to hold the result
+ * @param outOff the integer offset into the byte array
+ * @param outLen the length of the encoding
+ */
+ public static void I2OSP(int value, byte[] output, int outOff, int outLen)
+ {
+ for (int i = outLen - 1; i >= 0; i--)
+ {
+ output[outOff + i] = (byte)(value >>> (8 * i));
+ }
+ }
+
+ /**
+ * Convert an integer to a byte array of length 8.
+ *
+ * @param input the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(long input)
+ {
+ byte[] output = new byte[8];
+ output[0] = (byte)input;
+ output[1] = (byte)(input >>> 8);
+ output[2] = (byte)(input >>> 16);
+ output[3] = (byte)(input >>> 24);
+ output[4] = (byte)(input >>> 32);
+ output[5] = (byte)(input >>> 40);
+ output[6] = (byte)(input >>> 48);
+ output[7] = (byte)(input >>> 56);
+ return output;
+ }
+
+ /**
+ * Convert an integer to a byte array of length 8.
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ */
+ public static void I2OSP(long input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)input;
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 32);
+ output[outOff++] = (byte)(input >>> 40);
+ output[outOff++] = (byte)(input >>> 48);
+ output[outOff] = (byte)(input >>> 56);
+ }
+
+ /**
+ * Convert an int array to a byte array of the specified length. No length
+ * checking is performed (i.e., if the last integer cannot be encoded with
+ * length % 4 octets, it is truncated).
+ *
+ * @param input the int array
+ * @param outLen the length of the converted array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(int[] input, int outLen)
+ {
+ int intLen = input.length;
+ byte[] result = new byte[outLen];
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ I2OSP(input[i], result, index);
+ }
+ I2OSP(input[intLen - 1], result, index, outLen - index);
+ return result;
+ }
+
+ /**
+ * Convert a byte array to an int array.
+ *
+ * @param input the byte array
+ * @return the converted array
+ */
+ public static int[] toIntArray(byte[] input)
+ {
+ int intLen = (input.length + 3) / 4;
+ int lastLen = input.length & 0x03;
+ int[] result = new int[intLen];
+
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ result[i] = OS2IP(input, index);
+ }
+ if (lastLen != 0)
+ {
+ result[intLen - 1] = OS2IP(input, index, lastLen);
+ }
+ else
+ {
+ result[intLen - 1] = OS2IP(input, index);
+ }
+
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java
new file mode 100644
index 000000000..170c5a217
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java
@@ -0,0 +1,131 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This abstract class defines matrices. It holds the number of rows and the
+ * number of columns of the matrix and defines some basic methods.
+ */
+public abstract class Matrix
+{
+
+ /**
+ * number of rows
+ */
+ protected int numRows;
+
+ /**
+ * number of columns
+ */
+ protected int numColumns;
+
+ // ----------------------------------------------------
+ // some constants (matrix types)
+ // ----------------------------------------------------
+
+ /**
+ * zero matrix
+ */
+ public static final char MATRIX_TYPE_ZERO = 'Z';
+
+ /**
+ * unit matrix
+ */
+ public static final char MATRIX_TYPE_UNIT = 'I';
+
+ /**
+ * random lower triangular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_LT = 'L';
+
+ /**
+ * random upper triangular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_UT = 'U';
+
+ /**
+ * random regular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_REGULAR = 'R';
+
+ // ----------------------------------------------------
+ // getters
+ // ----------------------------------------------------
+
+ /**
+ * @return the number of rows in the matrix
+ */
+ public int getNumRows()
+ {
+ return numRows;
+ }
+
+ /**
+ * @return the number of columns in the binary matrix
+ */
+ public int getNumColumns()
+ {
+ return numColumns;
+ }
+
+ /**
+ * @return the encoded matrix, i.e., this matrix in byte array form.
+ */
+ public abstract byte[] getEncoded();
+
+ // ----------------------------------------------------
+ // arithmetic
+ // ----------------------------------------------------
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ */
+ public abstract Matrix computeInverse();
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return true if this is the zero matrix
+ */
+ public abstract boolean isZero();
+
+ /**
+ * Compute the product of this matrix and another matrix.
+ *
+ * @param a the other matrix
+ * @return this * a (newly created)
+ */
+ public abstract Matrix rightMultiply(Matrix a);
+
+ /**
+ * Compute the product of this matrix and a permutation.
+ *
+ * @param p the permutation
+ * @return this * p (newly created)
+ */
+ public abstract Matrix rightMultiply(Permutation p);
+
+ /**
+ * Compute the product of a vector and this matrix. If the length of the
+ * vector is greater than the number of rows of this matrix, the matrix is
+ * multiplied by each m-bit part of the vector.
+ *
+ * @param vector a vector
+ * @return vector * this (newly created)
+ */
+ public abstract Vector leftMultiply(Vector vector);
+
+ /**
+ * Compute the product of this matrix and a vector.
+ *
+ * @param vector a vector
+ * @return this * vector (newly created)
+ */
+ public abstract Vector rightMultiply(Vector vector);
+
+ /**
+ * @return a human readable form of the matrix.
+ */
+ public abstract String toString();
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java
new file mode 100644
index 000000000..2f1b505b3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java
@@ -0,0 +1,247 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class implements permutations of the set {0,1,...,n-1} for some given n
+ * > 0, i.e., ordered sequences containing each number m (0 <=
+ * m < n)
+ * once and only once.
+ */
+public class Permutation
+{
+
+ /**
+ * perm holds the elements of the permutation vector, i.e. [perm(0),
+ * perm(1), ..., perm(n-1)]
+ */
+ private int[] perm;
+
+ /**
+ * Create the identity permutation of the given size.
+ *
+ * @param n the size of the permutation
+ */
+ public Permutation(int n)
+ {
+ if (n <= 0)
+ {
+ throw new IllegalArgumentException("invalid length");
+ }
+
+ perm = new int[n];
+ for (int i = n - 1; i >= 0; i--)
+ {
+ perm[i] = i;
+ }
+ }
+
+ /**
+ * Create a permutation using the given permutation vector.
+ *
+ * @param perm the permutation vector
+ */
+ public Permutation(int[] perm)
+ {
+ if (!isPermutation(perm))
+ {
+ throw new IllegalArgumentException(
+ "array is not a permutation vector");
+ }
+
+ this.perm = IntUtils.clone(perm);
+ }
+
+ /**
+ * Create a permutation from an encoded permutation.
+ *
+ * @param enc the encoded permutation
+ */
+ public Permutation(byte[] enc)
+ {
+ if (enc.length <= 4)
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ int n = LittleEndianConversions.OS2IP(enc, 0);
+ int size = IntegerFunctions.ceilLog256(n - 1);
+
+ if (enc.length != 4 + n * size)
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ perm = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ perm[i] = LittleEndianConversions.OS2IP(enc, 4 + i * size, size);
+ }
+
+ if (!isPermutation(perm))
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ }
+
+ /**
+ * Create a random permutation of the given size.
+ *
+ * @param n the size of the permutation
+ * @param sr the source of randomness
+ */
+ public Permutation(int n, SecureRandom sr)
+ {
+ if (n <= 0)
+ {
+ throw new IllegalArgumentException("invalid length");
+ }
+
+ perm = new int[n];
+
+ int[] help = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ help[i] = i;
+ }
+
+ int k = n;
+ for (int j = 0; j < n; j++)
+ {
+ int i = RandUtils.nextInt(sr, k);
+ k--;
+ perm[j] = help[i];
+ help[i] = help[k];
+ }
+ }
+
+ /**
+ * Encode this permutation as byte array.
+ *
+ * @return the encoded permutation
+ */
+ public byte[] getEncoded()
+ {
+ int n = perm.length;
+ int size = IntegerFunctions.ceilLog256(n - 1);
+ byte[] result = new byte[4 + n * size];
+ LittleEndianConversions.I2OSP(n, result, 0);
+ for (int i = 0; i < n; i++)
+ {
+ LittleEndianConversions.I2OSP(perm[i], result, 4 + i * size, size);
+ }
+ return result;
+ }
+
+ /**
+ * @return the permutation vector (perm(0),perm(1),...,perm(n-1))
+ */
+ public int[] getVector()
+ {
+ return IntUtils.clone(perm);
+ }
+
+ /**
+ * Compute the inverse permutation P-1.
+ *
+ * @return this-1
+ */
+ public Permutation computeInverse()
+ {
+ Permutation result = new Permutation(perm.length);
+ for (int i = perm.length - 1; i >= 0; i--)
+ {
+ result.perm[perm[i]] = i;
+ }
+ return result;
+ }
+
+ /**
+ * Compute the product of this permutation and another permutation.
+ *
+ * @param p the other permutation
+ * @return this * p
+ */
+ public Permutation rightMultiply(Permutation p)
+ {
+ if (p.perm.length != perm.length)
+ {
+ throw new IllegalArgumentException("length mismatch");
+ }
+ Permutation result = new Permutation(perm.length);
+ for (int i = perm.length - 1; i >= 0; i--)
+ {
+ result.perm[i] = perm[p.perm[i]];
+ }
+ return result;
+ }
+
+ /**
+ * checks if given object is equal to this permutation.
+ *
+ * The method returns false whenever the given object is not permutation.
+ *
+ * @param other -
+ * permutation
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof Permutation))
+ {
+ return false;
+ }
+ Permutation otherPerm = (Permutation)other;
+
+ return IntUtils.equals(perm, otherPerm.perm);
+ }
+
+ /**
+ * @return a human readable form of the permutation
+ */
+ public String toString()
+ {
+ String result = "[" + perm[0];
+ for (int i = 1; i < perm.length; i++)
+ {
+ result += ", " + perm[i];
+ }
+ result += "]";
+ return result;
+ }
+
+ /**
+ * @return the hash code of this permutation
+ */
+ public int hashCode()
+ {
+ return perm.hashCode();
+ }
+
+ /**
+ * Check that the given array corresponds to a permutation of the set
+ * {0, 1, ..., n-1}.
+ *
+ * @param perm permutation vector
+ * @return true if perm represents an n-permutation and false otherwise
+ */
+ private boolean isPermutation(int[] perm)
+ {
+ int n = perm.length;
+ boolean[] onlyOnce = new boolean[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ if ((perm[i] < 0) || (perm[i] >= n) || onlyOnce[perm[i]])
+ {
+ return false;
+ }
+ onlyOnce[perm[i]] = true;
+ }
+
+ return true;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java
new file mode 100644
index 000000000..adab34ccd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java
@@ -0,0 +1,1125 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes operations with polynomials from the ring R =
+ * GF(2^m)[X], where 2 <= m <=31.
+ *
+ * @see GF2mField
+ * @see PolynomialRingGF2m
+ */
+public class PolynomialGF2mSmallM
+{
+
+ /**
+ * the finite field GF(2^m)
+ */
+ private GF2mField field;
+
+ /**
+ * the degree of this polynomial
+ */
+ private int degree;
+
+ /**
+ * For the polynomial representation the map f: R->Z*,
+ * poly(X) -> [coef_0, coef_1, ...] is used, where
+ * coef_i is the ith coefficient of the polynomial
+ * represented as int (see {@link GF2mField}). The polynomials are stored
+ * as int arrays.
+ */
+ private int[] coefficients;
+
+ /*
+ * some types of polynomials
+ */
+
+ /**
+ * Constant used for polynomial construction (see constructor
+ * {@link #PolynomialGF2mSmallM(GF2mField, int, char, SecureRandom)}).
+ */
+ public static final char RANDOM_IRREDUCIBLE_POLYNOMIAL = 'I';
+
+ /**
+ * Construct the zero polynomial over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ */
+ public PolynomialGF2mSmallM(GF2mField field)
+ {
+ this.field = field;
+ degree = -1;
+ coefficients = new int[1];
+ }
+
+ /**
+ * Construct a polynomial over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ * @param deg degree of polynomial
+ * @param typeOfPolynomial type of polynomial
+ * @param sr PRNG
+ */
+ public PolynomialGF2mSmallM(GF2mField field, int deg,
+ char typeOfPolynomial, SecureRandom sr)
+ {
+ this.field = field;
+
+ switch (typeOfPolynomial)
+ {
+ case PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL:
+ coefficients = createRandomIrreduciblePolynomial(deg, sr);
+ break;
+ default:
+ throw new IllegalArgumentException(" Error: type "
+ + typeOfPolynomial
+ + " is not defined for GF2smallmPolynomial");
+ }
+ computeDegree();
+ }
+
+ /**
+ * Create an irreducible polynomial with the given degree over the field
+ * GF(2^m).
+ *
+ * @param deg polynomial degree
+ * @param sr source of randomness
+ * @return the generated irreducible polynomial
+ */
+ private int[] createRandomIrreduciblePolynomial(int deg, SecureRandom sr)
+ {
+ int[] resCoeff = new int[deg + 1];
+ resCoeff[deg] = 1;
+ resCoeff[0] = field.getRandomNonZeroElement(sr);
+ for (int i = 1; i < deg; i++)
+ {
+ resCoeff[i] = field.getRandomElement(sr);
+ }
+ while (!isIrreducible(resCoeff))
+ {
+ int n = RandUtils.nextInt(sr, deg);
+ if (n == 0)
+ {
+ resCoeff[0] = field.getRandomNonZeroElement(sr);
+ }
+ else
+ {
+ resCoeff[n] = field.getRandomElement(sr);
+ }
+ }
+ return resCoeff;
+ }
+
+ /**
+ * Construct a monomial of the given degree over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ * @param degree the degree of the monomial
+ */
+ public PolynomialGF2mSmallM(GF2mField field, int degree)
+ {
+ this.field = field;
+ this.degree = degree;
+ coefficients = new int[degree + 1];
+ coefficients[degree] = 1;
+ }
+
+ /**
+ * Construct the polynomial over the given finite field GF(2^m) from the
+ * given coefficient vector.
+ *
+ * @param field finite field GF2m
+ * @param coeffs the coefficient vector
+ */
+ public PolynomialGF2mSmallM(GF2mField field, int[] coeffs)
+ {
+ this.field = field;
+ coefficients = normalForm(coeffs);
+ computeDegree();
+ }
+
+ /**
+ * Create a polynomial over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ * @param enc byte[] polynomial in byte array form
+ */
+ public PolynomialGF2mSmallM(GF2mField field, byte[] enc)
+ {
+ this.field = field;
+
+ // decodes polynomial
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if ((enc.length % count) != 0)
+ {
+ throw new IllegalArgumentException(
+ " Error: byte array is not encoded polynomial over given finite field GF2m");
+ }
+
+ coefficients = new int[enc.length / count];
+ count = 0;
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ coefficients[i] ^= (enc[count++] & 0x000000ff) << j;
+ }
+ if (!this.field.isElementOfThisField(coefficients[i]))
+ {
+ throw new IllegalArgumentException(
+ " Error: byte array is not encoded polynomial over given finite field GF2m");
+ }
+ }
+ // if HC = 0 for non-zero polynomial, returns error
+ if ((coefficients.length != 1)
+ && (coefficients[coefficients.length - 1] == 0))
+ {
+ throw new IllegalArgumentException(
+ " Error: byte array is not encoded polynomial over given finite field GF2m");
+ }
+ computeDegree();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link PolynomialGF2mSmallM}
+ */
+ public PolynomialGF2mSmallM(PolynomialGF2mSmallM other)
+ {
+ // field needs not to be cloned since it is immutable
+ field = other.field;
+ degree = other.degree;
+ coefficients = IntUtils.clone(other.coefficients);
+ }
+
+ /**
+ * Create a polynomial over the finite field GF(2^m) out of the given
+ * coefficient vector. The finite field is also obtained from the
+ * {@link GF2mVector}.
+ *
+ * @param vect the coefficient vector
+ */
+ public PolynomialGF2mSmallM(GF2mVector vect)
+ {
+ this(vect.getField(), vect.getIntArrayForm());
+ }
+
+ /*
+ * ------------------------
+ */
+
+ /**
+ * Return the degree of this polynomial
+ *
+ * @return int degree of this polynomial if this is zero polynomial return
+ * -1
+ */
+ public int getDegree()
+ {
+ int d = coefficients.length - 1;
+ if (coefficients[d] == 0)
+ {
+ return -1;
+ }
+ return d;
+ }
+
+ /**
+ * @return the head coefficient of this polynomial
+ */
+ public int getHeadCoefficient()
+ {
+ if (degree == -1)
+ {
+ return 0;
+ }
+ return coefficients[degree];
+ }
+
+ /**
+ * Return the head coefficient of a polynomial.
+ *
+ * @param a the polynomial
+ * @return the head coefficient of a
+ */
+ private static int headCoefficient(int[] a)
+ {
+ int degree = computeDegree(a);
+ if (degree == -1)
+ {
+ return 0;
+ }
+ return a[degree];
+ }
+
+ /**
+ * Return the coefficient with the given index.
+ *
+ * @param index the index
+ * @return the coefficient with the given index
+ */
+ public int getCoefficient(int index)
+ {
+ if ((index < 0) || (index > degree))
+ {
+ return 0;
+ }
+ return coefficients[index];
+ }
+
+ /**
+ * Returns encoded polynomial, i.e., this polynomial in byte array form
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] res = new byte[coefficients.length * count];
+ count = 0;
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ res[count++] = (byte)(coefficients[i] >>> j);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Evaluate this polynomial p at a value e (in
+ * GF(2^m)) with the Horner scheme.
+ *
+ * @param e the element of the finite field GF(2^m)
+ * @return this(e)
+ */
+ public int evaluateAt(int e)
+ {
+ int result = coefficients[degree];
+ for (int i = degree - 1; i >= 0; i--)
+ {
+ result = field.mult(result, e) ^ coefficients[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compute the sum of this polynomial and the given polynomial.
+ *
+ * @param addend the addend
+ * @return this + a (newly created)
+ */
+ public PolynomialGF2mSmallM add(PolynomialGF2mSmallM addend)
+ {
+ int[] resultCoeff = add(coefficients, addend.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Add the given polynomial to this polynomial (overwrite this).
+ *
+ * @param addend the addend
+ */
+ public void addToThis(PolynomialGF2mSmallM addend)
+ {
+ coefficients = add(coefficients, addend.coefficients);
+ computeDegree();
+ }
+
+ /**
+ * Compute the sum of two polynomials a and b over the finite field
+ * GF(2^m).
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @return a + b
+ */
+ private int[] add(int[] a, int[] b)
+ {
+ int[] result, addend;
+ if (a.length < b.length)
+ {
+ result = new int[b.length];
+ System.arraycopy(b, 0, result, 0, b.length);
+ addend = a;
+ }
+ else
+ {
+ result = new int[a.length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ addend = b;
+ }
+
+ for (int i = addend.length - 1; i >= 0; i--)
+ {
+ result[i] = field.add(result[i], addend[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the sum of this polynomial and the monomial of the given degree.
+ *
+ * @param degree the degree of the monomial
+ * @return this + X^k
+ */
+ public PolynomialGF2mSmallM addMonomial(int degree)
+ {
+ int[] monomial = new int[degree + 1];
+ monomial[degree] = 1;
+ int[] resultCoeff = add(coefficients, monomial);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of this polynomial with an element from GF(2^m).
+ *
+ * @param element an element of the finite field GF(2^m)
+ * @return this * element (newly created)
+ * @throws ArithmeticException if element is not an element of the finite
+ * field this polynomial is defined over.
+ */
+ public PolynomialGF2mSmallM multWithElement(int element)
+ {
+ if (!field.isElementOfThisField(element))
+ {
+ throw new ArithmeticException(
+ "Not an element of the finite field this polynomial is defined over.");
+ }
+ int[] resultCoeff = multWithElement(coefficients, element);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Multiply this polynomial with an element from GF(2^m).
+ *
+ * @param element an element of the finite field GF(2^m)
+ * @throws ArithmeticException if element is not an element of the finite
+ * field this polynomial is defined over.
+ */
+ public void multThisWithElement(int element)
+ {
+ if (!field.isElementOfThisField(element))
+ {
+ throw new ArithmeticException(
+ "Not an element of the finite field this polynomial is defined over.");
+ }
+ coefficients = multWithElement(coefficients, element);
+ computeDegree();
+ }
+
+ /**
+ * Compute the product of a polynomial a with an element from the finite
+ * field GF(2^m).
+ *
+ * @param a the polynomial
+ * @param element an element of the finite field GF(2^m)
+ * @return a * element
+ */
+ private int[] multWithElement(int[] a, int element)
+ {
+ int degree = computeDegree(a);
+ if (degree == -1 || element == 0)
+ {
+ return new int[1];
+ }
+
+ if (element == 1)
+ {
+ return IntUtils.clone(a);
+ }
+
+ int[] result = new int[degree + 1];
+ for (int i = degree; i >= 0; i--)
+ {
+ result[i] = field.mult(a[i], element);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this polynomial with a monomial X^k.
+ *
+ * @param k the degree of the monomial
+ * @return this * X^k
+ */
+ public PolynomialGF2mSmallM multWithMonomial(int k)
+ {
+ int[] resultCoeff = multWithMonomial(coefficients, k);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of a polynomial with a monomial X^k.
+ *
+ * @param a the polynomial
+ * @param k the degree of the monomial
+ * @return a * X^k
+ */
+ private static int[] multWithMonomial(int[] a, int k)
+ {
+ int d = computeDegree(a);
+ if (d == -1)
+ {
+ return new int[1];
+ }
+ int[] result = new int[d + k + 1];
+ System.arraycopy(a, 0, result, k, d + 1);
+ return result;
+ }
+
+ /**
+ * Divide this polynomial by the given polynomial.
+ *
+ * @param f a polynomial
+ * @return polynomial pair = {q,r} where this = q*f+r and deg(r) <
+ * deg(f);
+ */
+ public PolynomialGF2mSmallM[] div(PolynomialGF2mSmallM f)
+ {
+ int[][] resultCoeffs = div(coefficients, f.coefficients);
+ return new PolynomialGF2mSmallM[]{
+ new PolynomialGF2mSmallM(field, resultCoeffs[0]),
+ new PolynomialGF2mSmallM(field, resultCoeffs[1])};
+ }
+
+ /**
+ * Compute the result of the division of two polynomials over the field
+ * GF(2^m).
+ *
+ * @param a the first polynomial
+ * @param f the second polynomial
+ * @return int[][] {q,r}, where a = q*f+r and deg(r) < deg(f);
+ */
+ private int[][] div(int[] a, int[] f)
+ {
+ int df = computeDegree(f);
+ int da = computeDegree(a) + 1;
+ if (df == -1)
+ {
+ throw new ArithmeticException("Division by zero.");
+ }
+ int[][] result = new int[2][];
+ result[0] = new int[1];
+ result[1] = new int[da];
+ int hc = headCoefficient(f);
+ hc = field.inverse(hc);
+ result[0][0] = 0;
+ System.arraycopy(a, 0, result[1], 0, result[1].length);
+ while (df <= computeDegree(result[1]))
+ {
+ int[] q;
+ int[] coeff = new int[1];
+ coeff[0] = field.mult(headCoefficient(result[1]), hc);
+ q = multWithElement(f, coeff[0]);
+ int n = computeDegree(result[1]) - df;
+ q = multWithMonomial(q, n);
+ coeff = multWithMonomial(coeff, n);
+ result[0] = add(coeff, result[0]);
+ result[1] = add(q, result[1]);
+ }
+ return result;
+ }
+
+ /**
+ * Return the greatest common divisor of this and a polynomial f
+ *
+ * @param f polynomial
+ * @return GCD(this, f)
+ */
+ public PolynomialGF2mSmallM gcd(PolynomialGF2mSmallM f)
+ {
+ int[] resultCoeff = gcd(coefficients, f.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Return the greatest common divisor of two polynomials over the field
+ * GF(2^m).
+ *
+ * @param f the first polynomial
+ * @param g the second polynomial
+ * @return gcd(f, g)
+ */
+ private int[] gcd(int[] f, int[] g)
+ {
+ int[] a = f;
+ int[] b = g;
+ if (computeDegree(a) == -1)
+ {
+ return b;
+ }
+ while (computeDegree(b) != -1)
+ {
+ int[] c = mod(a, b);
+ a = new int[b.length];
+ System.arraycopy(b, 0, a, 0, a.length);
+ b = new int[c.length];
+ System.arraycopy(c, 0, b, 0, b.length);
+ }
+ int coeff = field.inverse(headCoefficient(a));
+ return multWithElement(a, coeff);
+ }
+
+ /**
+ * Compute the product of this polynomial and the given factor using a
+ * Karatzuba like scheme.
+ *
+ * @param factor the polynomial
+ * @return this * factor
+ */
+ public PolynomialGF2mSmallM multiply(PolynomialGF2mSmallM factor)
+ {
+ int[] resultCoeff = multiply(coefficients, factor.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of two polynomials over the field GF(2^m)
+ * using a Karatzuba like multiplication.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @return a * b
+ */
+ private int[] multiply(int[] a, int[] b)
+ {
+ int[] mult1, mult2;
+ if (computeDegree(a) < computeDegree(b))
+ {
+ mult1 = b;
+ mult2 = a;
+ }
+ else
+ {
+ mult1 = a;
+ mult2 = b;
+ }
+
+ mult1 = normalForm(mult1);
+ mult2 = normalForm(mult2);
+
+ if (mult2.length == 1)
+ {
+ return multWithElement(mult1, mult2[0]);
+ }
+
+ int d1 = mult1.length;
+ int d2 = mult2.length;
+ int[] result = new int[d1 + d2 - 1];
+
+ if (d2 != d1)
+ {
+ int[] res1 = new int[d2];
+ int[] res2 = new int[d1 - d2];
+ System.arraycopy(mult1, 0, res1, 0, res1.length);
+ System.arraycopy(mult1, d2, res2, 0, res2.length);
+ res1 = multiply(res1, mult2);
+ res2 = multiply(res2, mult2);
+ res2 = multWithMonomial(res2, d2);
+ result = add(res1, res2);
+ }
+ else
+ {
+ d2 = (d1 + 1) >>> 1;
+ int d = d1 - d2;
+ int[] firstPartMult1 = new int[d2];
+ int[] firstPartMult2 = new int[d2];
+ int[] secondPartMult1 = new int[d];
+ int[] secondPartMult2 = new int[d];
+ System
+ .arraycopy(mult1, 0, firstPartMult1, 0,
+ firstPartMult1.length);
+ System.arraycopy(mult1, d2, secondPartMult1, 0,
+ secondPartMult1.length);
+ System
+ .arraycopy(mult2, 0, firstPartMult2, 0,
+ firstPartMult2.length);
+ System.arraycopy(mult2, d2, secondPartMult2, 0,
+ secondPartMult2.length);
+ int[] helpPoly1 = add(firstPartMult1, secondPartMult1);
+ int[] helpPoly2 = add(firstPartMult2, secondPartMult2);
+ int[] res1 = multiply(firstPartMult1, firstPartMult2);
+ int[] res2 = multiply(helpPoly1, helpPoly2);
+ int[] res3 = multiply(secondPartMult1, secondPartMult2);
+ res2 = add(res2, res1);
+ res2 = add(res2, res3);
+ res3 = multWithMonomial(res3, d2);
+ result = add(res2, res3);
+ result = multWithMonomial(result, d2);
+ result = add(result, res1);
+ }
+
+ return result;
+ }
+
+ /*
+ * ---------------- PART II ----------------
+ *
+ */
+
+ /**
+ * Check a polynomial for irreducibility over the field GF(2^m).
+ *
+ * @param a the polynomial to check
+ * @return true if a is irreducible, false otherwise
+ */
+ private boolean isIrreducible(int[] a)
+ {
+ if (a[0] == 0)
+ {
+ return false;
+ }
+ int d = computeDegree(a) >> 1;
+ int[] u = {0, 1};
+ final int[] Y = {0, 1};
+ int fieldDegree = field.getDegree();
+ for (int i = 0; i < d; i++)
+ {
+ for (int j = fieldDegree - 1; j >= 0; j--)
+ {
+ u = modMultiply(u, u, a);
+ }
+ u = normalForm(u);
+ int[] g = gcd(add(u, Y), a);
+ if (computeDegree(g) != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Reduce this polynomial modulo another polynomial.
+ *
+ * @param f the reduction polynomial
+ * @return this mod f
+ */
+ public PolynomialGF2mSmallM mod(PolynomialGF2mSmallM f)
+ {
+ int[] resultCoeff = mod(coefficients, f.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Reduce a polynomial modulo another polynomial.
+ *
+ * @param a the polynomial
+ * @param f the reduction polynomial
+ * @return a mod f
+ */
+ private int[] mod(int[] a, int[] f)
+ {
+ int df = computeDegree(f);
+ if (df == -1)
+ {
+ throw new ArithmeticException("Division by zero");
+ }
+ int[] result = new int[a.length];
+ int hc = headCoefficient(f);
+ hc = field.inverse(hc);
+ System.arraycopy(a, 0, result, 0, result.length);
+ while (df <= computeDegree(result))
+ {
+ int[] q;
+ int coeff = field.mult(headCoefficient(result), hc);
+ q = multWithMonomial(f, computeDegree(result) - df);
+ q = multWithElement(q, coeff);
+ result = add(q, result);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the product of this polynomial and another polynomial modulo a
+ * third polynomial.
+ *
+ * @param a another polynomial
+ * @param b the reduction polynomial
+ * @return this * a mod b
+ */
+ public PolynomialGF2mSmallM modMultiply(PolynomialGF2mSmallM a,
+ PolynomialGF2mSmallM b)
+ {
+ int[] resultCoeff = modMultiply(coefficients, a.coefficients,
+ b.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Square this polynomial using a squaring matrix.
+ *
+ * @param matrix the squaring matrix
+ * @return this^2 modulo the reduction polynomial implicitly
+ * given via the squaring matrix
+ */
+ public PolynomialGF2mSmallM modSquareMatrix(PolynomialGF2mSmallM[] matrix)
+ {
+
+ int length = matrix.length;
+
+ int[] resultCoeff = new int[length];
+ int[] thisSquare = new int[length];
+
+ // square each entry of this polynomial
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ thisSquare[i] = field.mult(coefficients[i], coefficients[i]);
+ }
+
+ // do matrix-vector multiplication
+ for (int i = 0; i < length; i++)
+ {
+ // compute scalar product of i-th row and coefficient vector
+ for (int j = 0; j < length; j++)
+ {
+ if (i >= matrix[j].coefficients.length)
+ {
+ continue;
+ }
+ int scalarTerm = field.mult(matrix[j].coefficients[i],
+ thisSquare[j]);
+ resultCoeff[i] = field.add(resultCoeff[i], scalarTerm);
+ }
+ }
+
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of two polynomials modulo a third polynomial over the
+ * finite field GF(2^m).
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @param g the reduction polynomial
+ * @return a * b mod g
+ */
+ private int[] modMultiply(int[] a, int[] b, int[] g)
+ {
+ return mod(multiply(a, b), g);
+ }
+
+ /**
+ * Compute the square root of this polynomial modulo the given polynomial.
+ *
+ * @param a the reduction polynomial
+ * @return this^(1/2) mod a
+ */
+ public PolynomialGF2mSmallM modSquareRoot(PolynomialGF2mSmallM a)
+ {
+ int[] resultCoeff = IntUtils.clone(coefficients);
+ int[] help = modMultiply(resultCoeff, resultCoeff, a.coefficients);
+ while (!isEqual(help, coefficients))
+ {
+ resultCoeff = normalForm(help);
+ help = modMultiply(resultCoeff, resultCoeff, a.coefficients);
+ }
+
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the square root of this polynomial using a square root matrix.
+ *
+ * @param matrix the matrix for computing square roots in
+ * (GF(2^m))^t the polynomial ring defining the
+ * square root matrix
+ * @return this^(1/2) modulo the reduction polynomial implicitly
+ * given via the square root matrix
+ */
+ public PolynomialGF2mSmallM modSquareRootMatrix(
+ PolynomialGF2mSmallM[] matrix)
+ {
+
+ int length = matrix.length;
+
+ int[] resultCoeff = new int[length];
+
+ // do matrix multiplication
+ for (int i = 0; i < length; i++)
+ {
+ // compute scalar product of i-th row and j-th column
+ for (int j = 0; j < length; j++)
+ {
+ if (i >= matrix[j].coefficients.length)
+ {
+ continue;
+ }
+ if (j < coefficients.length)
+ {
+ int scalarTerm = field.mult(matrix[j].coefficients[i],
+ coefficients[j]);
+ resultCoeff[i] = field.add(resultCoeff[i], scalarTerm);
+ }
+ }
+ }
+
+ // compute the square root of each entry of the result coefficients
+ for (int i = 0; i < length; i++)
+ {
+ resultCoeff[i] = field.sqRoot(resultCoeff[i]);
+ }
+
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the result of the division of this polynomial by another
+ * polynomial modulo a third polynomial.
+ *
+ * @param divisor the divisor
+ * @param modulus the reduction polynomial
+ * @return this * divisor^(-1) mod modulus
+ */
+ public PolynomialGF2mSmallM modDiv(PolynomialGF2mSmallM divisor,
+ PolynomialGF2mSmallM modulus)
+ {
+ int[] resultCoeff = modDiv(coefficients, divisor.coefficients,
+ modulus.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the result of the division of two polynomials modulo a third
+ * polynomial over the field GF(2^m).
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @param g the reduction polynomial
+ * @return a * b^(-1) mod g
+ */
+ private int[] modDiv(int[] a, int[] b, int[] g)
+ {
+ int[] r0 = normalForm(g);
+ int[] r1 = mod(b, g);
+ int[] s0 = {0};
+ int[] s1 = mod(a, g);
+ int[] s2;
+ int[][] q;
+ while (computeDegree(r1) != -1)
+ {
+ q = div(r0, r1);
+ r0 = normalForm(r1);
+ r1 = normalForm(q[1]);
+ s2 = add(s0, modMultiply(q[0], s1, g));
+ s0 = normalForm(s1);
+ s1 = normalForm(s2);
+
+ }
+ int hc = headCoefficient(r0);
+ s0 = multWithElement(s0, field.inverse(hc));
+ return s0;
+ }
+
+ /**
+ * Compute the inverse of this polynomial modulo the given polynomial.
+ *
+ * @param a the reduction polynomial
+ * @return this^(-1) mod a
+ */
+ public PolynomialGF2mSmallM modInverse(PolynomialGF2mSmallM a)
+ {
+ int[] unit = {1};
+ int[] resultCoeff = modDiv(unit, coefficients, a.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute a polynomial pair (a,b) from this polynomial and the given
+ * polynomial g with the property b*this = a mod g and deg(a)<=deg(g)/2.
+ *
+ * @param g the reduction polynomial
+ * @return PolynomialGF2mSmallM[] {a,b} with b*this = a mod g and deg(a)<=
+ * deg(g)/2
+ */
+ public PolynomialGF2mSmallM[] modPolynomialToFracton(PolynomialGF2mSmallM g)
+ {
+ int dg = g.degree >> 1;
+ int[] a0 = normalForm(g.coefficients);
+ int[] a1 = mod(coefficients, g.coefficients);
+ int[] b0 = {0};
+ int[] b1 = {1};
+ while (computeDegree(a1) > dg)
+ {
+ int[][] q = div(a0, a1);
+ a0 = a1;
+ a1 = q[1];
+ int[] b2 = add(b0, modMultiply(q[0], b1, g.coefficients));
+ b0 = b1;
+ b1 = b2;
+ }
+
+ return new PolynomialGF2mSmallM[]{
+ new PolynomialGF2mSmallM(field, a1),
+ new PolynomialGF2mSmallM(field, b1)};
+ }
+
+ /**
+ * checks if given object is equal to this polynomial.
+ *
+ * The method returns false whenever the given object is not polynomial over
+ * GF(2^m).
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (other == null || !(other instanceof PolynomialGF2mSmallM))
+ {
+ return false;
+ }
+
+ PolynomialGF2mSmallM p = (PolynomialGF2mSmallM)other;
+
+ if ((field.equals(p.field)) && (degree == p.degree)
+ && (isEqual(coefficients, p.coefficients)))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Compare two polynomials given as int arrays.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @return true if a and b represent the
+ * same polynomials, false otherwise
+ */
+ private static boolean isEqual(int[] a, int[] b)
+ {
+ int da = computeDegree(a);
+ int db = computeDegree(b);
+ if (da != db)
+ {
+ return false;
+ }
+ for (int i = 0; i <= da; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ int hash = field.hashCode();
+ for (int j = 0; j < coefficients.length; j++)
+ {
+ hash = hash * 31 + coefficients[j];
+ }
+ return hash;
+ }
+
+ /**
+ * Returns a human readable form of the polynomial.
+ *
+ *
+ * @return a human readable form of the polynomial.
+ */
+ public String toString()
+ {
+ String str = " Polynomial over " + field.toString() + ": \n";
+
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ str = str + field.elementToStr(coefficients[i]) + "Y^" + i + "+";
+ }
+ str = str + ";";
+
+ return str;
+ }
+
+ /**
+ * Compute the degree of this polynomial. If this is the zero polynomial,
+ * the degree is -1.
+ */
+ private void computeDegree()
+ {
+ for (degree = coefficients.length - 1; degree >= 0
+ && coefficients[degree] == 0; degree--)
+ {
+ ;
+ }
+ }
+
+ /**
+ * Compute the degree of a polynomial.
+ *
+ * @param a the polynomial
+ * @return the degree of the polynomial a. If a is
+ * the zero polynomial, return -1.
+ */
+ private static int computeDegree(int[] a)
+ {
+ int degree;
+ for (degree = a.length - 1; degree >= 0 && a[degree] == 0; degree--)
+ {
+ ;
+ }
+ return degree;
+ }
+
+ /**
+ * Strip leading zero coefficients from the given polynomial.
+ *
+ * @param a the polynomial
+ * @return the reduced polynomial
+ */
+ private static int[] normalForm(int[] a)
+ {
+ int d = computeDegree(a);
+
+ // if a is the zero polynomial
+ if (d == -1)
+ {
+ // return new zero polynomial
+ return new int[1];
+ }
+
+ // if a already is in normal form
+ if (a.length == d + 1)
+ {
+ // return a clone of a
+ return IntUtils.clone(a);
+ }
+
+ // else, reduce a
+ int[] result = new int[d + 1];
+ System.arraycopy(a, 0, result, 0, d + 1);
+ return result;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java
new file mode 100644
index 000000000..1dbb320f7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java
@@ -0,0 +1,278 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class describes operations with polynomials over finite field GF(2), i e
+ * polynomial ring R = GF(2)[X]. All operations are defined only for polynomials
+ * with degree <=32. For the polynomial representation the map f: R->Z,
+ * poly(X)->poly(2) is used, where integers have the binary representation. For
+ * example: X^7+X^3+X+1 -> (00...0010001011)=139 Also for polynomials type
+ * Integer is used.
+ *
+ * @see GF2mField
+ */
+public final class PolynomialRingGF2
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private PolynomialRingGF2()
+ {
+ // empty
+ }
+
+ /**
+ * Return sum of two polyomials
+ *
+ * @param p polynomial
+ * @param q polynomial
+ * @return p+q
+ */
+
+ public static int add(int p, int q)
+ {
+ return p ^ q;
+ }
+
+ /**
+ * Return product of two polynomials
+ *
+ * @param p polynomial
+ * @param q polynomial
+ * @return p*q
+ */
+
+ public static long multiply(int p, int q)
+ {
+ long result = 0;
+ if (q != 0)
+ {
+ long q1 = q & 0x00000000ffffffffL;
+
+ while (p != 0)
+ {
+ byte b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ result ^= q1;
+ }
+ p >>>= 1;
+ q1 <<= 1;
+
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the product of two polynomials modulo a third polynomial.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @param r the reduction polynomial
+ * @return a * b mod r
+ */
+ public static int modMultiply(int a, int b, int r)
+ {
+ int result = 0;
+ int p = remainder(a, r);
+ int q = remainder(b, r);
+ if (q != 0)
+ {
+ int d = 1 << degree(r);
+
+ while (p != 0)
+ {
+ byte pMod2 = (byte)(p & 0x01);
+ if (pMod2 == 1)
+ {
+ result ^= q;
+ }
+ p >>>= 1;
+ q <<= 1;
+ if (q >= d)
+ {
+ q ^= r;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return the degree of a polynomial
+ *
+ * @param p polynomial p
+ * @return degree(p)
+ */
+
+ public static int degree(int p)
+ {
+ int result = -1;
+ while (p != 0)
+ {
+ result++;
+ p >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Return the degree of a polynomial
+ *
+ * @param p polynomial p
+ * @return degree(p)
+ */
+
+ public static int degree(long p)
+ {
+ int result = 0;
+ while (p != 0)
+ {
+ result++;
+ p >>>= 1;
+ }
+ return result - 1;
+ }
+
+ /**
+ * Return the remainder of a polynomial division of two polynomials.
+ *
+ * @param p dividend
+ * @param q divisor
+ * @return p mod q
+ */
+ public static int remainder(int p, int q)
+ {
+ int result = p;
+
+ if (q == 0)
+ {
+ System.err.println("Error: to be divided by 0");
+ return 0;
+ }
+
+ while (degree(result) >= degree(q))
+ {
+ result ^= q << (degree(result) - degree(q));
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the rest of devision two polynomials
+ *
+ * @param p polinomial
+ * @param q polinomial
+ * @return p mod q
+ */
+
+ public static int rest(long p, int q)
+ {
+ long p1 = p;
+ if (q == 0)
+ {
+ System.err.println("Error: to be divided by 0");
+ return 0;
+ }
+ long q1 = q & 0x00000000ffffffffL;
+ while ((p1 >>> 32) != 0)
+ {
+ p1 ^= q1 << (degree(p1) - degree(q1));
+ }
+
+ int result = (int)(p1 & 0xffffffff);
+ while (degree(result) >= degree(q))
+ {
+ result ^= q << (degree(result) - degree(q));
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the greatest common divisor of two polynomials
+ *
+ * @param p polinomial
+ * @param q polinomial
+ * @return GCD(p, q)
+ */
+
+ public static int gcd(int p, int q)
+ {
+ int a, b, c;
+ a = p;
+ b = q;
+ while (b != 0)
+ {
+ c = remainder(a, b);
+ a = b;
+ b = c;
+
+ }
+ return a;
+ }
+
+ /**
+ * Checking polynomial for irreducibility
+ *
+ * @param p polinomial
+ * @return true if p is irreducible and false otherwise
+ */
+
+ public static boolean isIrreducible(int p)
+ {
+ if (p == 0)
+ {
+ return false;
+ }
+ int d = degree(p) >>> 1;
+ int u = 2;
+ for (int i = 0; i < d; i++)
+ {
+ u = modMultiply(u, u, p);
+ if (gcd(u ^ 2, p) != 1)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Creates irreducible polynomial with degree d
+ *
+ * @param deg polynomial degree
+ * @return irreducible polynomial p
+ */
+ public static int getIrreduciblePolynomial(int deg)
+ {
+ if (deg < 0)
+ {
+ System.err.println("The Degree is negative");
+ return 0;
+ }
+ if (deg > 31)
+ {
+ System.err.println("The Degree is more then 31");
+ return 0;
+ }
+ if (deg == 0)
+ {
+ return 1;
+ }
+ int a = 1 << deg;
+ a++;
+ int b = 1 << (deg + 1);
+ for (int i = a; i < b; i += 2)
+ {
+ if (isIrreducible(i))
+ {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java
new file mode 100644
index 000000000..efa7ce8ac
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java
@@ -0,0 +1,175 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class represents polynomial rings GF(2^m)[X]/p(X) for
+ * m<<;32. If p(X) is irreducible, the polynomial ring
+ * is in fact an extension field of GF(2^m).
+ */
+public class PolynomialRingGF2m
+{
+
+ /**
+ * the finite field this polynomial ring is defined over
+ */
+ private GF2mField field;
+
+ /**
+ * the reduction polynomial
+ */
+ private PolynomialGF2mSmallM p;
+
+ /**
+ * the squaring matrix for this polynomial ring (given as the array of its
+ * row vectors)
+ */
+ protected PolynomialGF2mSmallM[] sqMatrix;
+
+ /**
+ * the matrix for computing square roots in this polynomial ring (given as
+ * the array of its row vectors). This matrix is computed as the inverse of
+ * the squaring matrix.
+ */
+ protected PolynomialGF2mSmallM[] sqRootMatrix;
+
+ /**
+ * Constructor.
+ *
+ * @param field the finite field
+ * @param p the reduction polynomial
+ */
+ public PolynomialRingGF2m(GF2mField field, PolynomialGF2mSmallM p)
+ {
+ this.field = field;
+ this.p = p;
+ computeSquaringMatrix();
+ computeSquareRootMatrix();
+ }
+
+ /**
+ * @return the squaring matrix for this polynomial ring
+ */
+ public PolynomialGF2mSmallM[] getSquaringMatrix()
+ {
+ return sqMatrix;
+ }
+
+ /**
+ * @return the matrix for computing square roots for this polynomial ring
+ */
+ public PolynomialGF2mSmallM[] getSquareRootMatrix()
+ {
+ return sqRootMatrix;
+ }
+
+ /**
+ * Compute the squaring matrix for this polynomial ring, using the base
+ * field and the reduction polynomial.
+ */
+ private void computeSquaringMatrix()
+ {
+ int numColumns = p.getDegree();
+ sqMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = 0; i < numColumns >> 1; i++)
+ {
+ int[] monomCoeffs = new int[(i << 1) + 1];
+ monomCoeffs[i << 1] = 1;
+ sqMatrix[i] = new PolynomialGF2mSmallM(field, monomCoeffs);
+ }
+ for (int i = numColumns >> 1; i < numColumns; i++)
+ {
+ int[] monomCoeffs = new int[(i << 1) + 1];
+ monomCoeffs[i << 1] = 1;
+ PolynomialGF2mSmallM monomial = new PolynomialGF2mSmallM(field,
+ monomCoeffs);
+ sqMatrix[i] = monomial.mod(p);
+ }
+ }
+
+ /**
+ * Compute the matrix for computing square roots in this polynomial ring by
+ * inverting the squaring matrix.
+ */
+ private void computeSquareRootMatrix()
+ {
+ int numColumns = p.getDegree();
+
+ // clone squaring matrix
+ PolynomialGF2mSmallM[] tmpMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = new PolynomialGF2mSmallM(sqMatrix[i]);
+ }
+
+ // initialize square root matrix as unit matrix
+ sqRootMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ sqRootMatrix[i] = new PolynomialGF2mSmallM(field, i);
+ }
+
+ // simultaneously compute Gaussian reduction of squaring matrix and unit
+ // matrix
+ for (int i = 0; i < numColumns; i++)
+ {
+ // if diagonal element is zero
+ if (tmpMatrix[i].getCoefficient(i) == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same row
+ for (int j = i + 1; j < numColumns; j++)
+ {
+ if (tmpMatrix[j].getCoefficient(i) != 0)
+ {
+ // found it, swap columns ...
+ foundNonZero = true;
+ swapColumns(tmpMatrix, i, j);
+ swapColumns(sqRootMatrix, i, j);
+ // ... and quit searching
+ j = numColumns;
+ continue;
+ }
+ }
+ // if no non-zero element was found
+ if (!foundNonZero)
+ {
+ // the matrix is not invertible
+ throw new ArithmeticException(
+ "Squaring matrix is not invertible.");
+ }
+ }
+
+ // normalize i-th column
+ int coef = tmpMatrix[i].getCoefficient(i);
+ int invCoef = field.inverse(coef);
+ tmpMatrix[i].multThisWithElement(invCoef);
+ sqRootMatrix[i].multThisWithElement(invCoef);
+
+ // normalize all other columns
+ for (int j = 0; j < numColumns; j++)
+ {
+ if (j != i)
+ {
+ coef = tmpMatrix[j].getCoefficient(i);
+ if (coef != 0)
+ {
+ PolynomialGF2mSmallM tmpSqColumn = tmpMatrix[i]
+ .multWithElement(coef);
+ PolynomialGF2mSmallM tmpInvColumn = sqRootMatrix[i]
+ .multWithElement(coef);
+ tmpMatrix[j].addToThis(tmpSqColumn);
+ sqRootMatrix[j].addToThis(tmpInvColumn);
+ }
+ }
+ }
+ }
+ }
+
+ private static void swapColumns(PolynomialGF2mSmallM[] matrix, int first,
+ int second)
+ {
+ PolynomialGF2mSmallM tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java
new file mode 100644
index 000000000..34862d7c3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+public class RandUtils
+{
+ static int nextInt(SecureRandom rand, int n)
+ {
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31);
+ }
+
+ int bits, value;
+ do
+ {
+ bits = rand.nextInt() >>> 1;
+ value = bits % n;
+ }
+ while (bits - value + (n - 1) < 0);
+
+ return value;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java
new file mode 100644
index 000000000..7f33d735f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java
@@ -0,0 +1,69 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This abstract class defines vectors. It holds the length of vector.
+ */
+public abstract class Vector
+{
+
+ /**
+ * the length of this vector
+ */
+ protected int length;
+
+ /**
+ * @return the length of this vector
+ */
+ public final int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * @return this vector as byte array
+ */
+ public abstract byte[] getEncoded();
+
+ /**
+ * Return whether this is the zero vector (i.e., all elements are zero).
+ *
+ * @return true if this is the zero vector, false
+ * otherwise
+ */
+ public abstract boolean isZero();
+
+ /**
+ * Add another vector to this vector.
+ *
+ * @param addend the other vector
+ * @return this + addend
+ */
+ public abstract Vector add(Vector addend);
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return this*p = p*this
+ */
+ public abstract Vector multiply(Permutation p);
+
+ /**
+ * Check if the given object is equal to this vector.
+ *
+ * @param other vector
+ * @return the result of the comparison
+ */
+ public abstract boolean equals(Object other);
+
+ /**
+ * @return the hash code of this vector
+ */
+ public abstract int hashCode();
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public abstract String toString();
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java
new file mode 100644
index 000000000..eec9ebf79
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java
@@ -0,0 +1,54 @@
+package org.spongycastle.pqc.math.ntru.euclid;
+
+import java.math.BigInteger;
+
+/**
+ * Extended Euclidean Algorithm in BigInteger
s
+ */
+public class BigIntEuclidean
+{
+ public BigInteger x, y, gcd;
+
+ private BigIntEuclidean()
+ {
+ }
+
+ /**
+ * Runs the EEA on two BigInteger
s
+ * Implemented from pseudocode on Wikipedia.
+ *
+ * @param a
+ * @param b
+ * @return a BigIntEuclidean
object that contains the result in the variables x
, y
, and gcd
+ */
+ public static BigIntEuclidean calculate(BigInteger a, BigInteger b)
+ {
+ BigInteger x = BigInteger.ZERO;
+ BigInteger lastx = BigInteger.ONE;
+ BigInteger y = BigInteger.ONE;
+ BigInteger lasty = BigInteger.ZERO;
+ while (!b.equals(BigInteger.ZERO))
+ {
+ BigInteger[] quotientAndRemainder = a.divideAndRemainder(b);
+ BigInteger quotient = quotientAndRemainder[0];
+
+ BigInteger temp = a;
+ a = b;
+ b = quotientAndRemainder[1];
+
+ temp = x;
+ x = lastx.subtract(quotient.multiply(x));
+ lastx = temp;
+
+ temp = y;
+ y = lasty.subtract(quotient.multiply(y));
+ lasty = temp;
+ }
+
+ BigIntEuclidean result = new BigIntEuclidean();
+ result.x = lastx;
+ result.y = lasty;
+ result.gcd = a;
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java
new file mode 100644
index 000000000..185fcc709
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java
@@ -0,0 +1,51 @@
+package org.spongycastle.pqc.math.ntru.euclid;
+
+/**
+ * Extended Euclidean Algorithm in int
s
+ */
+public class IntEuclidean
+{
+ public int x, y, gcd;
+
+ private IntEuclidean()
+ {
+ }
+
+ /**
+ * Runs the EEA on two int
s
+ * Implemented from pseudocode on Wikipedia.
+ *
+ * @param a
+ * @param b
+ * @return a IntEuclidean
object that contains the result in the variables x
, y
, and gcd
+ */
+ public static IntEuclidean calculate(int a, int b)
+ {
+ int x = 0;
+ int lastx = 1;
+ int y = 1;
+ int lasty = 0;
+ while (b != 0)
+ {
+ int quotient = a / b;
+
+ int temp = a;
+ a = b;
+ b = temp % b;
+
+ temp = x;
+ x = lastx - quotient * x;
+ lastx = temp;
+
+ temp = y;
+ y = lasty - quotient * y;
+ lasty = temp;
+ }
+
+ IntEuclidean result = new IntEuclidean();
+ result.x = lastx;
+ result.y = lasty;
+ result.gcd = a;
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java
new file mode 100644
index 000000000..a496060c1
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java
@@ -0,0 +1,258 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+
+/**
+ * A polynomial with {@link BigDecimal} coefficients.
+ * Some methods (like add
) change the polynomial, others (like mult
) do
+ * not but return the result as a new polynomial.
+ */
+public class BigDecimalPolynomial
+{
+ private static final BigDecimal ZERO = new BigDecimal("0");
+ private static final BigDecimal ONE_HALF = new BigDecimal("0.5");
+
+ BigDecimal[] coeffs;
+
+ /**
+ * Constructs a new polynomial with N
coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ BigDecimalPolynomial(int N)
+ {
+ coeffs = new BigDecimal[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ BigDecimalPolynomial(BigDecimal[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a BigDecimalPolynomial
from a BigIntPolynomial
. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public BigDecimalPolynomial(BigIntPolynomial p)
+ {
+ int N = p.coeffs.length;
+ coeffs = new BigDecimal[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = new BigDecimal(p.coeffs[i]);
+ }
+ }
+
+ /**
+ * Divides all coefficients by 2.
+ */
+ public void halve()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].multiply(ONE_HALF);
+ }
+ }
+
+ /**
+ * Multiplies the polynomial by another. Does not change this polynomial
+ * but returns the result as a new polynomial.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigDecimalPolynomial mult(BigIntPolynomial poly2)
+ {
+ return mult(new BigDecimalPolynomial(poly2));
+ }
+
+ /**
+ * Multiplies the polynomial by another, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigDecimalPolynomial mult(BigDecimalPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigDecimalPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
+ }
+ c.coeffs = copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private BigDecimalPolynomial multRecursive(BigDecimalPolynomial poly2)
+ {
+ BigDecimal[] a = coeffs;
+ BigDecimal[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 1)
+ {
+ BigDecimal[] c = coeffs.clone();
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ c[i] = c[i].multiply(poly2.coeffs[0]);
+ }
+ return new BigDecimalPolynomial(c);
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ BigDecimalPolynomial a1 = new BigDecimalPolynomial(copyOf(a, n1));
+ BigDecimalPolynomial a2 = new BigDecimalPolynomial(copyOfRange(a, n1, n));
+ BigDecimalPolynomial b1 = new BigDecimalPolynomial(copyOf(b, n1));
+ BigDecimalPolynomial b2 = new BigDecimalPolynomial(copyOfRange(b, n1, n));
+
+ BigDecimalPolynomial A = (BigDecimalPolynomial)a1.clone();
+ A.add(a2);
+ BigDecimalPolynomial B = (BigDecimalPolynomial)b1.clone();
+ B.add(b2);
+
+ BigDecimalPolynomial c1 = a1.multRecursive(b1);
+ BigDecimalPolynomial c2 = a2.multRecursive(b2);
+ BigDecimalPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ BigDecimalPolynomial c = new BigDecimalPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(BigDecimalPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].add(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b
+ */
+ void sub(BigDecimalPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].subtract(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Rounds all coefficients to the nearest integer.
+ *
+ * @return a new polynomial with BigInteger
coefficients
+ */
+ public BigIntPolynomial round()
+ {
+ int N = coeffs.length;
+ BigIntPolynomial p = new BigIntPolynomial(N);
+ for (int i = 0; i < N; i++)
+ {
+ p.coeffs[i] = coeffs[i].setScale(0, BigDecimal.ROUND_HALF_EVEN).toBigInteger();
+ }
+ return p;
+ }
+
+ /**
+ * Makes a copy of the polynomial that is independent of the original.
+ */
+ public Object clone()
+ {
+ return new BigDecimalPolynomial(coeffs.clone());
+ }
+
+ private BigDecimal[] copyOf(BigDecimal[] a, int length)
+ {
+ BigDecimal[] tmp = new BigDecimal[length];
+
+ System.arraycopy(a, 0, tmp, 0, a.length < length ? a.length : length);
+
+ return tmp;
+ }
+
+ private BigDecimal[] copyOfRange(BigDecimal[] a, int from, int to)
+ {
+ int newLength = to - from;
+ BigDecimal[] tmp = new BigDecimal[to - from];
+
+ System.arraycopy(a, from, tmp, 0, (a.length - from) < newLength ? (a.length - from) : newLength);
+
+ return tmp;
+ }
+
+ public BigDecimal[] getCoeffs()
+ {
+ BigDecimal[] tmp = new BigDecimal[coeffs.length];
+
+ System.arraycopy(coeffs, 0, tmp, 0, coeffs.length);
+
+ return tmp;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
new file mode 100644
index 000000000..ceb8a6071
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
@@ -0,0 +1,394 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial with {@link BigInteger} coefficients.
+ * Some methods (like add
) change the polynomial, others (like mult
) do
+ * not but return the result as a new polynomial.
+ */
+public class BigIntPolynomial
+{
+ private final static double LOG_10_2 = Math.log10(2);
+
+ BigInteger[] coeffs;
+
+ /**
+ * Constructs a new polynomial with N
coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ BigIntPolynomial(int N)
+ {
+ coeffs = new BigInteger[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ BigIntPolynomial(BigInteger[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a BigIntPolynomial
from a IntegerPolynomial
. The two polynomials are
+ * independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public BigIntPolynomial(IntegerPolynomial p)
+ {
+ coeffs = new BigInteger[p.coeffs.length];
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = BigInteger.valueOf(p.coeffs[i]);
+ }
+ }
+
+ /**
+ * Generates a random polynomial with numOnes
coefficients equal to 1,
+ * numNegOnes
coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ * @return
+ */
+ static BigIntPolynomial generateRandomSmall(int N, int numOnes, int numNegOnes)
+ {
+ List coeffs = new ArrayList();
+ for (int i = 0; i < numOnes; i++)
+ {
+ coeffs.add(Constants.BIGINT_ONE);
+ }
+ for (int i = 0; i < numNegOnes; i++)
+ {
+ coeffs.add(BigInteger.valueOf(-1));
+ }
+ while (coeffs.size() < N)
+ {
+ coeffs.add(Constants.BIGINT_ZERO);
+ }
+ Collections.shuffle(coeffs, new SecureRandom());
+
+ BigIntPolynomial poly = new BigIntPolynomial(N);
+ for (int i = 0; i < coeffs.size(); i++)
+ {
+ poly.coeffs[i] = (BigInteger)coeffs.get(i);
+ }
+ return poly;
+ }
+
+ /**
+ * Multiplies the polynomial by another, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.
+ * Both polynomials must have the same number of coefficients.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigIntPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private BigIntPolynomial multRecursive(BigIntPolynomial poly2)
+ {
+ BigInteger[] a = coeffs;
+ BigInteger[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 1)
+ {
+ BigInteger[] c = Arrays.clone(coeffs);
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ c[i] = c[i].multiply(poly2.coeffs[0]);
+ }
+ return new BigIntPolynomial(c);
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ BigIntPolynomial a1 = new BigIntPolynomial(Arrays.copyOf(a, n1));
+ BigIntPolynomial a2 = new BigIntPolynomial(Arrays.copyOfRange(a, n1, n));
+ BigIntPolynomial b1 = new BigIntPolynomial(Arrays.copyOf(b, n1));
+ BigIntPolynomial b2 = new BigIntPolynomial(Arrays.copyOfRange(b, n1, n));
+
+ BigIntPolynomial A = (BigIntPolynomial)a1.clone();
+ A.add(a2);
+ BigIntPolynomial B = (BigIntPolynomial)b1.clone();
+ B.add(b2);
+
+ BigIntPolynomial c1 = a1.multRecursive(b1);
+ BigIntPolynomial c2 = a2.multRecursive(b2);
+ BigIntPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ BigIntPolynomial c = new BigIntPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod modulus
.
+ *
+ * @param b another polynomial
+ */
+ void add(BigIntPolynomial b, BigInteger modulus)
+ {
+ add(b);
+ mod(modulus);
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(BigIntPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].add(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void sub(BigIntPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].subtract(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a BigInteger
. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ public void mult(BigInteger factor)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].multiply(factor);
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a int
. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ void mult(int factor)
+ {
+ mult(BigInteger.valueOf(factor));
+ }
+
+ /**
+ * Divides each coefficient by a BigInteger
and rounds the result to the nearest whole number.
+ * Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param divisor the number to divide by
+ */
+ public void div(BigInteger divisor)
+ {
+ BigInteger d = divisor.add(Constants.BIGINT_ONE).divide(BigInteger.valueOf(2));
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].compareTo(Constants.BIGINT_ZERO) > 0 ? coeffs[i].add(d) : coeffs[i].add(d.negate());
+ coeffs[i] = coeffs[i].divide(divisor);
+ }
+ }
+
+ /**
+ * Divides each coefficient by a BigDecimal
and rounds the result to decimalPlaces
places.
+ *
+ * @param divisor the number to divide by
+ * @param decimalPlaces the number of fractional digits to round the result to
+ * @return a new BigDecimalPolynomial
+ */
+ public BigDecimalPolynomial div(BigDecimal divisor, int decimalPlaces)
+ {
+ BigInteger max = maxCoeffAbs();
+ int coeffLength = (int)(max.bitLength() * LOG_10_2) + 1;
+ // factor = 1/divisor
+ BigDecimal factor = Constants.BIGDEC_ONE.divide(divisor, coeffLength + decimalPlaces + 1, BigDecimal.ROUND_HALF_EVEN);
+
+ // multiply each coefficient by factor
+ BigDecimalPolynomial p = new BigDecimalPolynomial(coeffs.length);
+ for (int i = 0; i < coeffs.length; i++)
+ // multiply, then truncate after decimalPlaces so subsequent operations aren't slowed down
+ {
+ p.coeffs[i] = new BigDecimal(coeffs[i]).multiply(factor).setScale(decimalPlaces, BigDecimal.ROUND_HALF_EVEN);
+ }
+
+ return p;
+ }
+
+ /**
+ * Returns the base10 length of the largest coefficient.
+ *
+ * @return length of the longest coefficient
+ */
+ public int getMaxCoeffLength()
+ {
+ return (int)(maxCoeffAbs().bitLength() * LOG_10_2) + 1;
+ }
+
+ private BigInteger maxCoeffAbs()
+ {
+ BigInteger max = coeffs[0].abs();
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ BigInteger coeff = coeffs[i].abs();
+ if (coeff.compareTo(max) > 0)
+ {
+ max = coeff;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * Takes each coefficient modulo a number.
+ *
+ * @param modulus
+ */
+ public void mod(BigInteger modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].mod(modulus);
+ }
+ }
+
+ /**
+ * Returns the sum of all coefficients, i.e. evaluates the polynomial at 0.
+ *
+ * @return the sum of all coefficients
+ */
+ BigInteger sumCoeffs()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum = sum.add(coeffs[i]);
+ }
+ return sum;
+ }
+
+ /**
+ * Makes a copy of the polynomial that is independent of the original.
+ */
+ public Object clone()
+ {
+ return new BigIntPolynomial(coeffs.clone());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(coeffs);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ BigIntPolynomial other = (BigIntPolynomial)obj;
+ if (!Arrays.areEqual(coeffs, other.coeffs))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public BigInteger[] getCoeffs()
+ {
+ return Arrays.clone(coeffs);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java
new file mode 100644
index 000000000..f29ce0664
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java
@@ -0,0 +1,12 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class Constants
+{
+ static final BigInteger BIGINT_ZERO = BigInteger.valueOf(0);
+ static final BigInteger BIGINT_ONE = BigInteger.valueOf(1);
+
+ static final BigDecimal BIGDEC_ONE = BigDecimal.valueOf(1);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java
new file mode 100644
index 000000000..007e72499
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java
@@ -0,0 +1,142 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.pqc.math.ntru.util.Util;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A TernaryPolynomial
with a "high" number of nonzero coefficients.
+ */
+public class DenseTernaryPolynomial
+ extends IntegerPolynomial
+ implements TernaryPolynomial
+{
+
+ /**
+ * Constructs a new DenseTernaryPolynomial
with N
coefficients.
+ *
+ * @param N the number of coefficients
+ */
+ DenseTernaryPolynomial(int N)
+ {
+ super(N);
+ checkTernarity();
+ }
+
+ /**
+ * Constructs a DenseTernaryPolynomial
from a IntegerPolynomial
. The two polynomials are
+ * independent of each other.
+ *
+ * @param intPoly the original polynomial
+ */
+ public DenseTernaryPolynomial(IntegerPolynomial intPoly)
+ {
+ this(intPoly.coeffs);
+ }
+
+ /**
+ * Constructs a new DenseTernaryPolynomial
with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public DenseTernaryPolynomial(int[] coeffs)
+ {
+ super(coeffs);
+ checkTernarity();
+ }
+
+ private void checkTernarity()
+ {
+ for (int i = 0; i != coeffs.length; i++)
+ {
+ int c = coeffs[i];
+ if (c < -1 || c > 1)
+ {
+ throw new IllegalStateException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
+ }
+ }
+ }
+
+ /**
+ * Generates a random polynomial with numOnes
coefficients equal to 1,
+ * numNegOnes
coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ */
+ public static DenseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
+ return new DenseTernaryPolynomial(coeffs);
+ }
+
+ /**
+ * Generates a polynomial with coefficients randomly selected from {-1, 0, 1}
.
+ *
+ * @param N number of coefficients
+ */
+ public static DenseTernaryPolynomial generateRandom(int N, SecureRandom random)
+ {
+ DenseTernaryPolynomial poly = new DenseTernaryPolynomial(N);
+ for (int i = 0; i < N; i++)
+ {
+ poly.coeffs[i] = random.nextInt(3) - 1;
+ }
+ return poly;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ // even on 32-bit systems, LongPolynomial5 multiplies faster than IntegerPolynomial
+ if (modulus == 2048)
+ {
+ IntegerPolynomial poly2Pos = (IntegerPolynomial)poly2.clone();
+ poly2Pos.modPositive(2048);
+ LongPolynomial5 poly5 = new LongPolynomial5(poly2Pos);
+ return poly5.mult(this).toIntegerPolynomial();
+ }
+ else
+ {
+ return super.mult(poly2, modulus);
+ }
+ }
+
+ public int[] getOnes()
+ {
+ int N = coeffs.length;
+ int[] ones = new int[N];
+ int onesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ if (c == 1)
+ {
+ ones[onesIdx++] = i;
+ }
+ }
+ return Arrays.copyOf(ones, onesIdx);
+ }
+
+ public int[] getNegOnes()
+ {
+ int N = coeffs.length;
+ int[] negOnes = new int[N];
+ int negOnesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ if (c == -1)
+ {
+ negOnes[negOnesIdx++] = i;
+ }
+ }
+ return Arrays.copyOf(negOnes, negOnesIdx);
+ }
+
+ public int size()
+ {
+ return coeffs.length;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java
new file mode 100644
index 000000000..9605b4c91
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java
@@ -0,0 +1,1358 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.spongycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+import org.spongycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.spongycastle.pqc.math.ntru.util.Util;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial with int
coefficients.
+ * Some methods (like add
) change the polynomial, others (like mult
) do
+ * not but return the result as a new polynomial.
+ */
+public class IntegerPolynomial
+ implements Polynomial
+{
+ private static final int NUM_EQUAL_RESULTANTS = 3;
+ /**
+ * Prime numbers > 4500 for resultant computation. Starting them below ~4400 causes incorrect results occasionally.
+ * Fortunately, 4500 is about the optimum number for performance.
+ * This array contains enough prime numbers so primes never have to be computed on-line for any standard {@link org.spongycastle.pqc.crypto.ntru.NTRUSigningParameters}.
+ */
+ private static final int[] PRIMES = new int[]{
+ 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
+ 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
+ 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
+ 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
+ 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003,
+ 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
+ 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179,
+ 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
+ 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387,
+ 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
+ 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
+ 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
+ 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693,
+ 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
+ 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857,
+ 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
+ 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053,
+ 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
+ 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
+ 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
+ 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367,
+ 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
+ 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571,
+ 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
+ 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
+ 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
+ 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917,
+ 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
+ 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
+ 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
+ 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
+ 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
+ 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
+ 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
+ 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
+ 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
+ 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
+ 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
+ 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017,
+ 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111,
+ 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219,
+ 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291,
+ 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387,
+ 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501,
+ 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597,
+ 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
+ 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741,
+ 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831,
+ 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929,
+ 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
+ 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109,
+ 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199,
+ 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283,
+ 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377,
+ 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439,
+ 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533,
+ 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631,
+ 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
+ 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811,
+ 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887,
+ 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973};
+ private static final List BIGINT_PRIMES;
+
+ static
+ {
+ BIGINT_PRIMES = new ArrayList();
+ for (int i = 0; i != PRIMES.length; i++)
+ {
+ BIGINT_PRIMES.add(BigInteger.valueOf(PRIMES[i]));
+ }
+ }
+
+ public int[] coeffs;
+
+ /**
+ * Constructs a new polynomial with N
coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ public IntegerPolynomial(int N)
+ {
+ coeffs = new int[N];
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public IntegerPolynomial(int[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a IntegerPolynomial
from a BigIntPolynomial
. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public IntegerPolynomial(BigIntPolynomial p)
+ {
+ coeffs = new int[p.coeffs.length];
+ for (int i = 0; i < p.coeffs.length; i++)
+ {
+ coeffs[i] = p.coeffs[i].intValue();
+ }
+ }
+
+ /**
+ * Decodes a byte array to a polynomial with N
ternary coefficients
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Sves(byte[] data, int N)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Sves(data, N));
+ }
+
+ /**
+ * Converts a byte array produced by {@link #toBinary3Tight()} to a polynomial.
+ *
+ * @param b a byte array
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Tight(byte[] b, int N)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Tight(b, N));
+ }
+
+ /**
+ * Reads data produced by {@link #toBinary3Tight()} from an input stream and converts it to a polynomial.
+ *
+ * @param is an input stream
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Tight(InputStream is, int N)
+ throws IOException
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Tight(is, N));
+ }
+
+ /**
+ * Returns a polynomial with N coefficients between 0
and q-1
.
+ * q
must be a power of 2.
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary(byte[] data, int N, int q)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeModQ(data, N, q));
+ }
+
+ /**
+ * Returns a polynomial with N coefficients between 0
and q-1
.
+ * q
must be a power of 2.
+ * Ignores any excess bytes.
+ *
+ * @param is an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary(InputStream is, int N, int q)
+ throws IOException
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeModQ(is, N, q));
+ }
+
+ /**
+ * Encodes a polynomial with ternary coefficients to binary.
+ * coeffs[2*i]
and coeffs[2*i+1]
must not both equal -1 for any integer i,
+ * so this method is only safe to use with polynomials produced by
res is set to fromBinary3Sves()
.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary3Sves()
+ {
+ return ArrayEncoder.encodeMod3Sves(coeffs);
+ }
+
+ /**
+ * Converts a polynomial with ternary coefficients to binary.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary3Tight()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = coeffs.length - 1; i >= 0; i--)
+ {
+ sum = sum.multiply(BigInteger.valueOf(3));
+ sum = sum.add(BigInteger.valueOf(coeffs[i] + 1));
+ }
+
+ int size = (BigInteger.valueOf(3).pow(coeffs.length).bitLength() + 7) / 8;
+ byte[] arr = sum.toByteArray();
+
+ if (arr.length < size)
+ {
+ // pad with leading zeros so arr.length==size
+ byte[] arr2 = new byte[size];
+ System.arraycopy(arr, 0, arr2, size - arr.length, arr.length);
+ return arr2;
+ }
+
+ if (arr.length > size)
+ // drop sign bit
+ {
+ arr = Arrays.copyOfRange(arr, 1, arr.length);
+ }
+ return arr;
+ }
+
+ /**
+ * Encodes a polynomial whose coefficients are between 0 and q, to binary. q must be a power of 2.
+ *
+ * @param q
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary(int q)
+ {
+ return ArrayEncoder.encodeModQ(coeffs, q);
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the values mod modulus and the indices mod N
+ */
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the indices mod N
+ */
+ public IntegerPolynomial mult(IntegerPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ IntegerPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] += c.coeffs[k];
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ return new BigIntPolynomial(this).mult(poly2);
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private IntegerPolynomial multRecursive(IntegerPolynomial poly2)
+ {
+ int[] a = coeffs;
+ int[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 32)
+ {
+ int cn = 2 * n - 1;
+ IntegerPolynomial c = new IntegerPolynomial(new int[cn]);
+ for (int k = 0; k < cn; k++)
+ {
+ for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
+ {
+ c.coeffs[k] += b[i] * a[k - i];
+ }
+ }
+ return c;
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ IntegerPolynomial a1 = new IntegerPolynomial(Arrays.copyOf(a, n1));
+ IntegerPolynomial a2 = new IntegerPolynomial(Arrays.copyOfRange(a, n1, n));
+ IntegerPolynomial b1 = new IntegerPolynomial(Arrays.copyOf(b, n1));
+ IntegerPolynomial b2 = new IntegerPolynomial(Arrays.copyOfRange(b, n1, n));
+
+ IntegerPolynomial A = (IntegerPolynomial)a1.clone();
+ A.add(a2);
+ IntegerPolynomial B = (IntegerPolynomial)b1.clone();
+ B.add(b2);
+
+ IntegerPolynomial c1 = a1.multRecursive(b1);
+ IntegerPolynomial c2 = a2.multRecursive(b2);
+ IntegerPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ IntegerPolynomial c = new IntegerPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] += c3.coeffs[i];
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] += c2.coeffs[i];
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Computes the inverse mod q; q
must be a power of 2.
+ * Returns null
if the polynomial is not invertible.
+ *
+ * @param q the modulus
+ * @return a new polynomial
+ */
+ public IntegerPolynomial invertFq(int q)
+ {
+ int N = coeffs.length;
+ int k = 0;
+ IntegerPolynomial b = new IntegerPolynomial(N + 1);
+ b.coeffs[0] = 1;
+ IntegerPolynomial c = new IntegerPolynomial(N + 1);
+ IntegerPolynomial f = new IntegerPolynomial(N + 1);
+ f.coeffs = Arrays.copyOf(coeffs, N + 1);
+ f.modPositive(2);
+ // set g(x) = x^N â 1
+ IntegerPolynomial g = new IntegerPolynomial(N + 1);
+ g.coeffs[0] = 1;
+ g.coeffs[N] = 1;
+ while (true)
+ {
+ while (f.coeffs[0] == 0)
+ {
+ for (int i = 1; i <= N; i++)
+ {
+ f.coeffs[i - 1] = f.coeffs[i]; // f(x) = f(x) / x
+ c.coeffs[N + 1 - i] = c.coeffs[N - i]; // c(x) = c(x) * x
+ }
+ f.coeffs[N] = 0;
+ c.coeffs[0] = 0;
+ k++;
+ if (f.equalsZero())
+ {
+ return null; // not invertible
+ }
+ }
+ if (f.equalsOne())
+ {
+ break;
+ }
+ if (f.degree() < g.degree())
+ {
+ // exchange f and g
+ IntegerPolynomial temp = f;
+ f = g;
+ g = temp;
+ // exchange b and c
+ temp = b;
+ b = c;
+ c = temp;
+ }
+ f.add(g, 2);
+ b.add(c, 2);
+ }
+
+ if (b.coeffs[N] != 0)
+ {
+ return null;
+ }
+ // Fq(x) = x^(N-k) * b(x)
+ IntegerPolynomial Fq = new IntegerPolynomial(N);
+ int j = 0;
+ k %= N;
+ for (int i = N - 1; i >= 0; i--)
+ {
+ j = i - k;
+ if (j < 0)
+ {
+ j += N;
+ }
+ Fq.coeffs[j] = b.coeffs[i];
+ }
+
+ return mod2ToModq(Fq, q);
+ }
+
+ /**
+ * Computes the inverse mod q from the inverse mod 2
+ *
+ * @param Fq
+ * @param q
+ * @return The inverse of this polynomial mod q
+ */
+ private IntegerPolynomial mod2ToModq(IntegerPolynomial Fq, int q)
+ {
+ if (Util.is64BitJVM() && q == 2048)
+ {
+ LongPolynomial2 thisLong = new LongPolynomial2(this);
+ LongPolynomial2 FqLong = new LongPolynomial2(Fq);
+ int v = 2;
+ while (v < q)
+ {
+ v *= 2;
+ LongPolynomial2 temp = (LongPolynomial2)FqLong.clone();
+ temp.mult2And(v - 1);
+ FqLong = thisLong.mult(FqLong).mult(FqLong);
+ temp.subAnd(FqLong, v - 1);
+ FqLong = temp;
+ }
+ return FqLong.toIntegerPolynomial();
+ }
+ else
+ {
+ int v = 2;
+ while (v < q)
+ {
+ v *= 2;
+ IntegerPolynomial temp = new IntegerPolynomial(Arrays.copyOf(Fq.coeffs, Fq.coeffs.length));
+ temp.mult2(v);
+ Fq = mult(Fq, v).mult(Fq, v);
+ temp.sub(Fq, v);
+ Fq = temp;
+ }
+ return Fq;
+ }
+ }
+
+ /**
+ * Computes the inverse mod 3.
+ * Returns null
if the polynomial is not invertible.
+ *
+ * @return a new polynomial
+ */
+ public IntegerPolynomial invertF3()
+ {
+ int N = coeffs.length;
+ int k = 0;
+ IntegerPolynomial b = new IntegerPolynomial(N + 1);
+ b.coeffs[0] = 1;
+ IntegerPolynomial c = new IntegerPolynomial(N + 1);
+ IntegerPolynomial f = new IntegerPolynomial(N + 1);
+ f.coeffs = Arrays.copyOf(coeffs, N + 1);
+ f.modPositive(3);
+ // set g(x) = x^N â 1
+ IntegerPolynomial g = new IntegerPolynomial(N + 1);
+ g.coeffs[0] = -1;
+ g.coeffs[N] = 1;
+ while (true)
+ {
+ while (f.coeffs[0] == 0)
+ {
+ for (int i = 1; i <= N; i++)
+ {
+ f.coeffs[i - 1] = f.coeffs[i]; // f(x) = f(x) / x
+ c.coeffs[N + 1 - i] = c.coeffs[N - i]; // c(x) = c(x) * x
+ }
+ f.coeffs[N] = 0;
+ c.coeffs[0] = 0;
+ k++;
+ if (f.equalsZero())
+ {
+ return null; // not invertible
+ }
+ }
+ if (f.equalsAbsOne())
+ {
+ break;
+ }
+ if (f.degree() < g.degree())
+ {
+ // exchange f and g
+ IntegerPolynomial temp = f;
+ f = g;
+ g = temp;
+ // exchange b and c
+ temp = b;
+ b = c;
+ c = temp;
+ }
+ if (f.coeffs[0] == g.coeffs[0])
+ {
+ f.sub(g, 3);
+ b.sub(c, 3);
+ }
+ else
+ {
+ f.add(g, 3);
+ b.add(c, 3);
+ }
+ }
+
+ if (b.coeffs[N] != 0)
+ {
+ return null;
+ }
+ // Fp(x) = [+-] x^(N-k) * b(x)
+ IntegerPolynomial Fp = new IntegerPolynomial(N);
+ int j = 0;
+ k %= N;
+ for (int i = N - 1; i >= 0; i--)
+ {
+ j = i - k;
+ if (j < 0)
+ {
+ j += N;
+ }
+ Fp.coeffs[j] = f.coeffs[0] * b.coeffs[i];
+ }
+
+ Fp.ensurePositive(3);
+ return Fp;
+ }
+
+ /**
+ * Resultant of this polynomial with x^n-1
using a probabilistic algorithm.
+ *
+ * Unlike EESS, this implementation does not compute all resultants modulo primes
+ * such that their product exceeds the maximum possible resultant, but rather stops
+ * when NUM_EQUAL_RESULTANTS
consecutive modular resultants are equal.
+ * This means the return value may be incorrect. Experiments show this happens in
+ * about 1 out of 100 cases when N=439
and NUM_EQUAL_RESULTANTS=2
,
+ * so the likelyhood of leaving the loop too early is (1/100)^(NUM_EQUAL_RESULTANTS-1)
.
+ *
+ * Because of the above, callers must verify the output and try a different polynomial if necessary.
+ *
+ * @return (rho, res)
satisfying res = rho*this + t*(x^n-1)
for some integer t
.
+ */
+ public Resultant resultant()
+ {
+ int N = coeffs.length;
+
+ // Compute resultants modulo prime numbers. Continue until NUM_EQUAL_RESULTANTS consecutive modular resultants are equal.
+ LinkedList(rho, res)
satisfying res = rho*this + t*(x^n-1)
for some integer t
.
+ */
+ public Resultant resultantMultiThread()
+ {
+ int N = coeffs.length;
+
+ // upper bound for resultant(f, g) = ||f, 2||^deg(g) * ||g, 2||^deg(f) = squaresum(f)^(N/2) * 2^(deg(f)/2) because g(x)=x^N-1
+ // see http://jondalon.mathematik.uni-osnabrueck.de/staff/phpages/brunsw/CompAlg.pdf chapter 3
+ BigInteger max = squareSum().pow((N + 1) / 2);
+ max = max.multiply(BigInteger.valueOf(2).pow((degree() + 1) / 2));
+ BigInteger max2 = max.multiply(BigInteger.valueOf(2));
+
+ // compute resultants modulo prime numbers
+ BigInteger prime = BigInteger.valueOf(10000);
+ BigInteger pProd = Constants.BIGINT_ONE;
+ LinkedBlockingQueuex^n-1 mod p
.
+ *
+ * @return (rho, res)
satisfying res = rho*this + t*(x^n-1) mod p
for some integer t
.
+ */
+ public ModularResultant resultant(int p)
+ {
+ // Add a coefficient as the following operations involve polynomials of degree deg(f)+1
+ int[] fcoeffs = Arrays.copyOf(coeffs, coeffs.length + 1);
+ IntegerPolynomial f = new IntegerPolynomial(fcoeffs);
+ int N = fcoeffs.length;
+
+ IntegerPolynomial a = new IntegerPolynomial(N);
+ a.coeffs[0] = -1;
+ a.coeffs[N - 1] = 1;
+ IntegerPolynomial b = new IntegerPolynomial(f.coeffs);
+ IntegerPolynomial v1 = new IntegerPolynomial(N);
+ IntegerPolynomial v2 = new IntegerPolynomial(N);
+ v2.coeffs[0] = 1;
+ int da = N - 1;
+ int db = b.degree();
+ int ta = da;
+ int c = 0;
+ int r = 1;
+ while (db > 0)
+ {
+ c = Util.invert(b.coeffs[db], p);
+ c = (c * a.coeffs[da]) % p;
+ a.multShiftSub(b, c, da - db, p);
+ v1.multShiftSub(v2, c, da - db, p);
+
+ da = a.degree();
+ if (da < db)
+ {
+ r *= Util.pow(b.coeffs[db], ta - da, p);
+ r %= p;
+ if (ta % 2 == 1 && db % 2 == 1)
+ {
+ r = (-r) % p;
+ }
+ IntegerPolynomial temp = a;
+ a = b;
+ b = temp;
+ int tempdeg = da;
+ da = db;
+ temp = v1;
+ v1 = v2;
+ v2 = temp;
+ ta = db;
+ db = tempdeg;
+ }
+ }
+ r *= Util.pow(b.coeffs[0], da, p);
+ r %= p;
+ c = Util.invert(b.coeffs[0], p);
+ v2.mult(c);
+ v2.mod(p);
+ v2.mult(r);
+ v2.mod(p);
+
+ // drop the highest coefficient so #coeffs matches the original input
+ v2.coeffs = Arrays.copyOf(v2.coeffs, v2.coeffs.length - 1);
+ return new ModularResultant(new BigIntPolynomial(v2), BigInteger.valueOf(r), BigInteger.valueOf(p));
+ }
+
+ /**
+ * Computes this-b*c*(x^k) mod p
and stores the result in this polynomial.
+ * See steps 4a,4b in EESS algorithm 2.2.7.1.
+ *
+ * @param b
+ * @param c
+ * @param k
+ * @param p
+ */
+ private void multShiftSub(IntegerPolynomial b, int c, int k, int p)
+ {
+ int N = coeffs.length;
+ for (int i = k; i < N; i++)
+ {
+ coeffs[i] = (coeffs[i] - b.coeffs[i - k] * c) % p;
+ }
+ }
+
+ /**
+ * Adds the squares of all coefficients.
+ *
+ * @return the sum of squares
+ */
+ private BigInteger squareSum()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum = sum.add(BigInteger.valueOf(coeffs[i] * coeffs[i]));
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the degree of the polynomial
+ *
+ * @return the degree
+ */
+ int degree()
+ {
+ int degree = coeffs.length - 1;
+ while (degree > 0 && coeffs[degree] == 0)
+ {
+ degree--;
+ }
+ return degree;
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod modulus
.
+ *
+ * @param b another polynomial
+ */
+ public void add(IntegerPolynomial b, int modulus)
+ {
+ add(b);
+ mod(modulus);
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(IntegerPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] += b.coeffs[i];
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod modulus
.
+ *
+ * @param b another polynomial
+ */
+ public void sub(IntegerPolynomial b, int modulus)
+ {
+ sub(b);
+ mod(modulus);
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void sub(IntegerPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] -= b.coeffs[i];
+ }
+ }
+
+ /**
+ * Subtracts a int
from each coefficient. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param b
+ */
+ void sub(int b)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] -= b;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a int
. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ public void mult(int factor)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= factor;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a 2 and applies a modulus. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param modulus a modulus
+ */
+ private void mult2(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= 2;
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a 2 and applies a modulus. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param modulus a modulus
+ */
+ public void mult3(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= 3;
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Divides each coefficient by k
and rounds to the nearest integer. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param k the divisor
+ */
+ public void div(int k)
+ {
+ int k2 = (k + 1) / 2;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] += coeffs[i] > 0 ? k2 : -k2;
+ coeffs[i] /= k;
+ }
+ }
+
+ /**
+ * Takes each coefficient modulo 3 such that all coefficients are ternary.
+ */
+ public void mod3()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] %= 3;
+ if (coeffs[i] > 1)
+ {
+ coeffs[i] -= 3;
+ }
+ if (coeffs[i] < -1)
+ {
+ coeffs[i] += 3;
+ }
+ }
+ }
+
+ /**
+ * Ensures all coefficients are between 0 and modulus-1
+ *
+ * @param modulus a modulus
+ */
+ public void modPositive(int modulus)
+ {
+ mod(modulus);
+ ensurePositive(modulus);
+ }
+
+ /**
+ * Reduces all coefficients to the interval [-modulus/2, modulus/2)
+ */
+ void modCenter(int modulus)
+ {
+ mod(modulus);
+ for (int j = 0; j < coeffs.length; j++)
+ {
+ while (coeffs[j] < modulus / 2)
+ {
+ coeffs[j] += modulus;
+ }
+ while (coeffs[j] >= modulus / 2)
+ {
+ coeffs[j] -= modulus;
+ }
+ }
+ }
+
+ /**
+ * Takes each coefficient modulo modulus
.
+ */
+ public void mod(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Adds modulus
until all coefficients are above 0.
+ *
+ * @param modulus a modulus
+ */
+ public void ensurePositive(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ while (coeffs[i] < 0)
+ {
+ coeffs[i] += modulus;
+ }
+ }
+ }
+
+ /**
+ * Computes the centered euclidean norm of the polynomial.
+ *
+ * @param q a modulus
+ * @return the centered norm
+ */
+ public long centeredNormSq(int q)
+ {
+ int N = coeffs.length;
+ IntegerPolynomial p = (IntegerPolynomial)clone();
+ p.shiftGap(q);
+
+ long sum = 0;
+ long sqSum = 0;
+ for (int i = 0; i != p.coeffs.length; i++)
+ {
+ int c = p.coeffs[i];
+ sum += c;
+ sqSum += c * c;
+ }
+
+ long centeredNormSq = sqSum - sum * sum / N;
+ return centeredNormSq;
+ }
+
+ /**
+ * Shifts all coefficients so the largest gap is centered around -q/2
.
+ *
+ * @param q a modulus
+ */
+ void shiftGap(int q)
+ {
+ modCenter(q);
+
+ int[] sorted = Arrays.clone(coeffs);
+
+ sort(sorted);
+
+ int maxrange = 0;
+ int maxrangeStart = 0;
+ for (int i = 0; i < sorted.length - 1; i++)
+ {
+ int range = sorted[i + 1] - sorted[i];
+ if (range > maxrange)
+ {
+ maxrange = range;
+ maxrangeStart = sorted[i];
+ }
+ }
+
+ int pmin = sorted[0];
+ int pmax = sorted[sorted.length - 1];
+
+ int j = q - pmax + pmin;
+ int shift;
+ if (j > maxrange)
+ {
+ shift = (pmax + pmin) / 2;
+ }
+ else
+ {
+ shift = maxrangeStart + maxrange / 2 + q / 2;
+ }
+
+ sub(shift);
+ }
+
+ private void sort(int[] ints)
+ {
+ boolean swap = true;
+
+ while (swap)
+ {
+ swap = false;
+ for (int i = 0; i != ints.length - 1; i++)
+ {
+ if (ints[i] > ints[i+1])
+ {
+ int tmp = ints[i];
+ ints[i] = ints[i+1];
+ ints[i+1] = tmp;
+ swap = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Shifts the values of all coefficients to the interval [-q/2, q/2]
.
+ *
+ * @param q a modulus
+ */
+ public void center0(int q)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ while (coeffs[i] < -q / 2)
+ {
+ coeffs[i] += q;
+ }
+ while (coeffs[i] > q / 2)
+ {
+ coeffs[i] -= q;
+ }
+ }
+ }
+
+ /**
+ * Returns the sum of all coefficients, i.e. evaluates the polynomial at 0.
+ *
+ * @return the sum of all coefficients
+ */
+ public int sumCoeffs()
+ {
+ int sum = 0;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum += coeffs[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Tests if p(x) = 0
.
+ *
+ * @return true iff all coefficients are zeros
+ */
+ private boolean equalsZero()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if p(x) = 1
.
+ *
+ * @return true iff all coefficients are equal to zero, except for the lowest coefficient which must equal 1
+ */
+ public boolean equalsOne()
+ {
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return coeffs[0] == 1;
+ }
+
+ /**
+ * Tests if |p(x)| = 1
.
+ *
+ * @return true iff all coefficients are equal to zero, except for the lowest coefficient which must equal 1 or -1
+ */
+ private boolean equalsAbsOne()
+ {
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return Math.abs(coeffs[0]) == 1;
+ }
+
+ /**
+ * Counts the number of coefficients equal to an integer
+ *
+ * @param value an integer
+ * @return the number of coefficients equal to value
+ */
+ public int count(int value)
+ {
+ int count = 0;
+ for (int i = 0; i != coeffs.length; i++)
+ {
+ if (coeffs[i] == value)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Multiplication by X
in Z[X]/Z[X^n-1]
.
+ */
+ public void rotate1()
+ {
+ int clast = coeffs[coeffs.length - 1];
+ for (int i = coeffs.length - 1; i > 0; i--)
+ {
+ coeffs[i] = coeffs[i - 1];
+ }
+ coeffs[0] = clast;
+ }
+
+ public void clear()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = 0;
+ }
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ return (IntegerPolynomial)clone();
+ }
+
+ public Object clone()
+ {
+ return new IntegerPolynomial(coeffs.clone());
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof IntegerPolynomial)
+ {
+ return Arrays.areEqual(coeffs, ((IntegerPolynomial)obj).coeffs);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Calls {@link IntegerPolynomial#resultant(int)
+ */
+ private class ModResultantTask
+ implements Callablelong
value for
+ * faster multiplication in 64 bit environments.
+ * Coefficients can be between 0 and 2047 and are stored in pairs in the bits 0..10 and 24..34 of a long
number.
+ */
+public class LongPolynomial2
+{
+ private long[] coeffs; // each representing two coefficients in the original IntegerPolynomial
+ private int numCoeffs;
+
+ /**
+ * Constructs a LongPolynomial2
from a IntegerPolynomial
. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial. Coefficients must be between 0 and 2047.
+ */
+ public LongPolynomial2(IntegerPolynomial p)
+ {
+ numCoeffs = p.coeffs.length;
+ coeffs = new long[(numCoeffs + 1) / 2];
+ int idx = 0;
+ for (int pIdx = 0; pIdx < numCoeffs; )
+ {
+ int c0 = p.coeffs[pIdx++];
+ while (c0 < 0)
+ {
+ c0 += 2048;
+ }
+ long c1 = pIdx < numCoeffs ? p.coeffs[pIdx++] : 0;
+ while (c1 < 0)
+ {
+ c1 += 2048;
+ }
+ coeffs[idx] = c0 + (c1 << 24);
+ idx++;
+ }
+ }
+
+ private LongPolynomial2(long[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ private LongPolynomial2(int N)
+ {
+ coeffs = new long[N];
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the indices mod N and the values mod 2048.
+ */
+ public LongPolynomial2 mult(LongPolynomial2 poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N || numCoeffs != poly2.numCoeffs)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ LongPolynomial2 c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ if (numCoeffs % 2 == 0)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = (c.coeffs[k - N] + c.coeffs[k]) & 0x7FF0007FFL;
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ else
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N] + (c.coeffs[k - 1] >> 24);
+ c.coeffs[k - N] = c.coeffs[k - N] + ((c.coeffs[k] & 2047) << 24);
+ c.coeffs[k - N] &= 0x7FF0007FFL;
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ c.coeffs[c.coeffs.length - 1] &= 2047;
+ }
+ }
+
+ c = new LongPolynomial2(c.coeffs);
+ c.numCoeffs = numCoeffs;
+ return c;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] intCoeffs = new int[numCoeffs];
+ int uIdx = 0;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ intCoeffs[uIdx++] = (int)(coeffs[i] & 2047);
+ if (uIdx < numCoeffs)
+ {
+ intCoeffs[uIdx++] = (int)((coeffs[i] >> 24) & 2047);
+ }
+ }
+ return new IntegerPolynomial(intCoeffs);
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private LongPolynomial2 multRecursive(LongPolynomial2 poly2)
+ {
+ long[] a = coeffs;
+ long[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 32)
+ {
+ int cn = 2 * n;
+ LongPolynomial2 c = new LongPolynomial2(new long[cn]);
+ for (int k = 0; k < cn; k++)
+ {
+ for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
+ {
+ long c0 = a[k - i] * b[i];
+ long cu = c0 & 0x7FF000000L + (c0 & 2047);
+ long co = (c0 >>> 48) & 2047;
+
+ c.coeffs[k] = (c.coeffs[k] + cu) & 0x7FF0007FFL;
+ c.coeffs[k + 1] = (c.coeffs[k + 1] + co) & 0x7FF0007FFL;
+ }
+ }
+ return c;
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ LongPolynomial2 a1 = new LongPolynomial2(Arrays.copyOf(a, n1));
+ LongPolynomial2 a2 = new LongPolynomial2(Arrays.copyOfRange(a, n1, n));
+ LongPolynomial2 b1 = new LongPolynomial2(Arrays.copyOf(b, n1));
+ LongPolynomial2 b2 = new LongPolynomial2(Arrays.copyOfRange(b, n1, n));
+
+ LongPolynomial2 A = (LongPolynomial2)a1.clone();
+ A.add(a2);
+ LongPolynomial2 B = (LongPolynomial2)b1.clone();
+ B.add(b2);
+
+ LongPolynomial2 c1 = a1.multRecursive(b1);
+ LongPolynomial2 c2 = a2.multRecursive(b2);
+ LongPolynomial2 c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ LongPolynomial2 c = new LongPolynomial2(2 * n);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i] & 0x7FF0007FFL;
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = (c.coeffs[n1 + i] + c3.coeffs[i]) & 0x7FF0007FFL;
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = (c.coeffs[2 * n1 + i] + c2.coeffs[i]) & 0x7FF0007FFL;
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ private void add(LongPolynomial2 b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (coeffs[i] + b.coeffs[i]) & 0x7FF0007FFL;
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ private void sub(LongPolynomial2 b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & 0x7FF0007FFL;
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which must have the same number of coefficients,
+ * and applies an AND mask to the upper and lower halves of each coefficients.
+ *
+ * @param b another polynomial
+ * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
+ */
+ public void subAnd(LongPolynomial2 b, int mask)
+ {
+ long longMask = (((long)mask) << 24) + mask;
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & longMask;
+ }
+ }
+
+ /**
+ * Multiplies this polynomial by 2 and applies an AND mask to the upper and
+ * lower halves of each coefficients.
+ *
+ * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
+ */
+ public void mult2And(int mask)
+ {
+ long longMask = (((long)mask) << 24) + mask;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = (coeffs[i] << 1) & longMask;
+ }
+ }
+
+ public Object clone()
+ {
+ LongPolynomial2 p = new LongPolynomial2(coeffs.clone());
+ p.numCoeffs = numCoeffs;
+ return p;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof LongPolynomial2)
+ {
+ return Arrays.areEqual(coeffs, ((LongPolynomial2)obj).coeffs);
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java
new file mode 100644
index 000000000..6524f9ff3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java
@@ -0,0 +1,149 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial class that combines five coefficients into one long
value for
+ * faster multiplication by a ternary polynomial.
+ * Coefficients can be between 0 and 2047 and are stored in bits 0..11, 12..23, ..., 48..59 of a long
number.
+ */
+public class LongPolynomial5
+{
+ private long[] coeffs; // groups of 5 coefficients
+ private int numCoeffs;
+
+ /**
+ * Constructs a LongPolynomial5
from a IntegerPolynomial
. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial. Coefficients must be between 0 and 2047.
+ */
+ public LongPolynomial5(IntegerPolynomial p)
+ {
+ numCoeffs = p.coeffs.length;
+
+ coeffs = new long[(numCoeffs + 4) / 5];
+ int cIdx = 0;
+ int shift = 0;
+ for (int i = 0; i < numCoeffs; i++)
+ {
+ coeffs[cIdx] |= ((long)p.coeffs[i]) << shift;
+ shift += 12;
+ if (shift >= 60)
+ {
+ shift = 0;
+ cIdx++;
+ }
+ }
+ }
+
+ private LongPolynomial5(long[] coeffs, int numCoeffs)
+ {
+ this.coeffs = coeffs;
+ this.numCoeffs = numCoeffs;
+ }
+
+ /**
+ * Multiplies the polynomial with a TernaryPolynomial
, taking the indices mod N and the values mod 2048.
+ */
+ public LongPolynomial5 mult(TernaryPolynomial poly2)
+ {
+ long[][] prod = new long[5][coeffs.length + (poly2.size() + 4) / 5 - 1]; // intermediate results, the subarrays are shifted by 0,...,4 coefficients
+
+ // multiply ones
+ int[] ones = poly2.getOnes();
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int pIdx = ones[idx];
+ int cIdx = pIdx / 5;
+ int m = pIdx - cIdx * 5; // m = pIdx % 5
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ prod[m][cIdx] = (prod[m][cIdx] + coeffs[i]) & 0x7FF7FF7FF7FF7FFL;
+ cIdx++;
+ }
+ }
+
+ // multiply negative ones
+ int[] negOnes = poly2.getNegOnes();
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int pIdx = negOnes[idx];
+ int cIdx = pIdx / 5;
+ int m = pIdx - cIdx * 5; // m = pIdx % 5
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ prod[m][cIdx] = (0x800800800800800L + prod[m][cIdx] - coeffs[i]) & 0x7FF7FF7FF7FF7FFL;
+ cIdx++;
+ }
+ }
+
+ // combine shifted coefficients (5 arrays) into a single array of length prod[*].length+1
+ long[] cCoeffs = Arrays.copyOf(prod[0], prod[0].length + 1);
+ for (int m = 1; m <= 4; m++)
+ {
+ int shift = m * 12;
+ int shift60 = 60 - shift;
+ long mask = (1L << shift60) - 1;
+ int pLen = prod[m].length;
+ for (int i = 0; i < pLen; i++)
+ {
+ long upper, lower;
+ upper = prod[m][i] >> shift60;
+ lower = prod[m][i] & mask;
+
+ cCoeffs[i] = (cCoeffs[i] + (lower << shift)) & 0x7FF7FF7FF7FF7FFL;
+ int nextIdx = i + 1;
+ cCoeffs[nextIdx] = (cCoeffs[nextIdx] + upper) & 0x7FF7FF7FF7FF7FFL;
+ }
+ }
+
+ // reduce indices of cCoeffs modulo numCoeffs
+ int shift = 12 * (numCoeffs % 5);
+ for (int cIdx = coeffs.length - 1; cIdx < cCoeffs.length; cIdx++)
+ {
+ long iCoeff; // coefficient to shift into the [0..numCoeffs-1] range
+ int newIdx;
+ if (cIdx == coeffs.length - 1)
+ {
+ iCoeff = numCoeffs == 5 ? 0 : cCoeffs[cIdx] >> shift;
+ newIdx = 0;
+ }
+ else
+ {
+ iCoeff = cCoeffs[cIdx];
+ newIdx = cIdx * 5 - numCoeffs;
+ }
+
+ int base = newIdx / 5;
+ int m = newIdx - base * 5; // m = newIdx % 5
+ long lower = iCoeff << (12 * m);
+ long upper = iCoeff >> (12 * (5 - m));
+ cCoeffs[base] = (cCoeffs[base] + lower) & 0x7FF7FF7FF7FF7FFL;
+ int base1 = base + 1;
+ if (base1 < coeffs.length)
+ {
+ cCoeffs[base1] = (cCoeffs[base1] + upper) & 0x7FF7FF7FF7FF7FFL;
+ }
+ }
+
+ return new LongPolynomial5(cCoeffs, numCoeffs);
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] intCoeffs = new int[numCoeffs];
+ int cIdx = 0;
+ int shift = 0;
+ for (int i = 0; i < numCoeffs; i++)
+ {
+ intCoeffs[i] = (int)((coeffs[cIdx] >> shift) & 2047);
+ shift += 12;
+ if (shift >= 60)
+ {
+ shift = 0;
+ cIdx++;
+ }
+ }
+ return new IntegerPolynomial(intCoeffs);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java
new file mode 100644
index 000000000..7e8031fe6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java
@@ -0,0 +1,46 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigInteger;
+
+import org.spongycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+
+/**
+ * A resultant modulo a BigInteger
+ */
+public class ModularResultant
+ extends Resultant
+{
+ BigInteger modulus;
+
+ ModularResultant(BigIntPolynomial rho, BigInteger res, BigInteger modulus)
+ {
+ super(rho, res);
+ this.modulus = modulus;
+ }
+
+ /**
+ * Calculates a rho
modulo m1*m2
from
+ * two resultants whose rho
s are modulo m1
and m2
.
+ * null
.
+ *
+ * @param modRes1
+ * @param modRes2
+ * @return rho
modulo modRes1.modulus * modRes2.modulus
, and null
for res.
+ */
+ static ModularResultant combineRho(ModularResultant modRes1, ModularResultant modRes2)
+ {
+ BigInteger mod1 = modRes1.modulus;
+ BigInteger mod2 = modRes2.modulus;
+ BigInteger prod = mod1.multiply(mod2);
+ BigIntEuclidean er = BigIntEuclidean.calculate(mod2, mod1);
+
+ BigIntPolynomial rho1 = (BigIntPolynomial)modRes1.rho.clone();
+ rho1.mult(er.x.multiply(mod2));
+ BigIntPolynomial rho2 = (BigIntPolynomial)modRes2.rho.clone();
+ rho2.mult(er.y.multiply(mod1));
+ rho1.add(rho2);
+ rho1.mod(prod);
+
+ return new ModularResultant(rho1, null, prod);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java
new file mode 100644
index 000000000..588163414
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java
@@ -0,0 +1,42 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+public interface Polynomial
+{
+
+ /**
+ * Multiplies the polynomial by an IntegerPolynomial
,
+ * taking the indices mod N
.
+ *
+ * @param poly2 a polynomial
+ * @return the product of the two polynomials
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2);
+
+ /**
+ * Multiplies the polynomial by an IntegerPolynomial
,
+ * taking the coefficient values mod modulus
and the indices mod N
.
+ *
+ * @param poly2 a polynomial
+ * @param modulus a modulus to apply
+ * @return the product of the two polynomials
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2, int modulus);
+
+ /**
+ * Returns a polynomial that is equal to this polynomial (in the sense that {@link #mult(IntegerPolynomial, int)}
+ * returns equal IntegerPolynomial
s). The new polynomial is guaranteed to be independent of the original.
+ *
+ * @return a new IntegerPolynomial
.
+ */
+ IntegerPolynomial toIntegerPolynomial();
+
+ /**
+ * Multiplies the polynomial by a BigIntPolynomial
, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.
+ * Both polynomials must have the same number of coefficients.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ BigIntPolynomial mult(BigIntPolynomial poly2);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java
new file mode 100644
index 000000000..e60ce8b87
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java
@@ -0,0 +1,153 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial of the form f1*f2+f3
, where
+ * f1,f2,f3
are very sparsely populated ternary polynomials.
+ */
+public class ProductFormPolynomial
+ implements Polynomial
+{
+ private SparseTernaryPolynomial f1, f2, f3;
+
+ public ProductFormPolynomial(SparseTernaryPolynomial f1, SparseTernaryPolynomial f2, SparseTernaryPolynomial f3)
+ {
+ this.f1 = f1;
+ this.f2 = f2;
+ this.f3 = f3;
+ }
+
+ public static ProductFormPolynomial generateRandom(int N, int df1, int df2, int df3Ones, int df3NegOnes, SecureRandom random)
+ {
+ SparseTernaryPolynomial f1 = SparseTernaryPolynomial.generateRandom(N, df1, df1, random);
+ SparseTernaryPolynomial f2 = SparseTernaryPolynomial.generateRandom(N, df2, df2, random);
+ SparseTernaryPolynomial f3 = SparseTernaryPolynomial.generateRandom(N, df3Ones, df3NegOnes, random);
+ return new ProductFormPolynomial(f1, f2, f3);
+ }
+
+ public static ProductFormPolynomial fromBinary(byte[] data, int N, int df1, int df2, int df3Ones, int df3NegOnes)
+ throws IOException
+ {
+ return fromBinary(new ByteArrayInputStream(data), N, df1, df2, df3Ones, df3NegOnes);
+ }
+
+ public static ProductFormPolynomial fromBinary(InputStream is, int N, int df1, int df2, int df3Ones, int df3NegOnes)
+ throws IOException
+ {
+ SparseTernaryPolynomial f1;
+
+ f1 = SparseTernaryPolynomial.fromBinary(is, N, df1, df1);
+ SparseTernaryPolynomial f2 = SparseTernaryPolynomial.fromBinary(is, N, df2, df2);
+ SparseTernaryPolynomial f3 = SparseTernaryPolynomial.fromBinary(is, N, df3Ones, df3NegOnes);
+ return new ProductFormPolynomial(f1, f2, f3);
+ }
+
+ public byte[] toBinary()
+ {
+ byte[] f1Bin = f1.toBinary();
+ byte[] f2Bin = f2.toBinary();
+ byte[] f3Bin = f3.toBinary();
+
+ byte[] all = Arrays.copyOf(f1Bin, f1Bin.length + f2Bin.length + f3Bin.length);
+ System.arraycopy(f2Bin, 0, all, f1Bin.length, f2Bin.length);
+ System.arraycopy(f3Bin, 0, all, f1Bin.length + f2Bin.length, f3Bin.length);
+ return all;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial b)
+ {
+ IntegerPolynomial c = f1.mult(b);
+ c = f2.mult(c);
+ c.add(f3.mult(b));
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial b)
+ {
+ BigIntPolynomial c = f1.mult(b);
+ c = f2.mult(c);
+ c.add(f3.mult(b));
+ return c;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ IntegerPolynomial i = f1.mult(f2.toIntegerPolynomial());
+ i.add(f3.toIntegerPolynomial());
+ return i;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f1 == null) ? 0 : f1.hashCode());
+ result = prime * result + ((f2 == null) ? 0 : f2.hashCode());
+ result = prime * result + ((f3 == null) ? 0 : f3.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ ProductFormPolynomial other = (ProductFormPolynomial)obj;
+ if (f1 == null)
+ {
+ if (other.f1 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f1.equals(other.f1))
+ {
+ return false;
+ }
+ if (f2 == null)
+ {
+ if (other.f2 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f2.equals(other.f2))
+ {
+ return false;
+ }
+ if (f3 == null)
+ {
+ if (other.f3 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f3.equals(other.f3))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java
new file mode 100644
index 000000000..8fa133286
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java
@@ -0,0 +1,28 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigInteger;
+
+/**
+ * Contains a resultant and a polynomial rho
such that
+ * res = rho*this + t*(x^n-1) for some integer t
.
+ *
+ * @see IntegerPolynomial#resultant()
+ * @see IntegerPolynomial#resultant(int)
+ */
+public class Resultant
+{
+ /**
+ * A polynomial such that res = rho*this + t*(x^n-1) for some integer t
+ */
+ public BigIntPolynomial rho;
+ /**
+ * Resultant of a polynomial with x^n-1
+ */
+ public BigInteger res;
+
+ Resultant(BigIntPolynomial rho, BigInteger res)
+ {
+ this.rho = rho;
+ this.res = res;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java
new file mode 100644
index 000000000..2fbd70a48
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java
@@ -0,0 +1,320 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.spongycastle.pqc.math.ntru.util.Util;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A TernaryPolynomial
with a "low" number of nonzero coefficients.
+ */
+public class SparseTernaryPolynomial
+ implements TernaryPolynomial
+{
+ /**
+ * Number of bits to use for each coefficient. Determines the upper bound for N
.
+ */
+ private static final int BITS_PER_INDEX = 11;
+
+ private int N;
+ private int[] ones;
+ private int[] negOnes;
+
+ /**
+ * Constructs a new polynomial.
+ *
+ * @param N total number of coefficients including zeros
+ * @param ones indices of coefficients equal to 1
+ * @param negOnes indices of coefficients equal to -1
+ */
+ SparseTernaryPolynomial(int N, int[] ones, int[] negOnes)
+ {
+ this.N = N;
+ this.ones = ones;
+ this.negOnes = negOnes;
+ }
+
+ /**
+ * Constructs a DenseTernaryPolynomial
from a IntegerPolynomial
. The two polynomials are
+ * independent of each other.
+ *
+ * @param intPoly the original polynomial
+ */
+ public SparseTernaryPolynomial(IntegerPolynomial intPoly)
+ {
+ this(intPoly.coeffs);
+ }
+
+ /**
+ * Constructs a new SparseTernaryPolynomial
with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public SparseTernaryPolynomial(int[] coeffs)
+ {
+ N = coeffs.length;
+ ones = new int[N];
+ negOnes = new int[N];
+ int onesIdx = 0;
+ int negOnesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ switch (c)
+ {
+ case 1:
+ ones[onesIdx++] = i;
+ break;
+ case -1:
+ negOnes[negOnesIdx++] = i;
+ break;
+ case 0:
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
+ }
+ }
+ ones = Arrays.copyOf(ones, onesIdx);
+ negOnes = Arrays.copyOf(negOnes, negOnesIdx);
+ }
+
+ /**
+ * Decodes a byte array encoded with {@link #toBinary()} to a ploynomial.
+ *
+ * @param is an input stream containing an encoded polynomial
+ * @param N number of coefficients including zeros
+ * @param numOnes number of coefficients equal to 1
+ * @param numNegOnes number of coefficients equal to -1
+ * @return the decoded polynomial
+ * @throws IOException
+ */
+ public static SparseTernaryPolynomial fromBinary(InputStream is, int N, int numOnes, int numNegOnes)
+ throws IOException
+ {
+ int maxIndex = 1 << BITS_PER_INDEX;
+ int bitsPerIndex = 32 - Integer.numberOfLeadingZeros(maxIndex - 1);
+
+ int data1Len = (numOnes * bitsPerIndex + 7) / 8;
+ byte[] data1 = Util.readFullLength(is, data1Len);
+ int[] ones = ArrayEncoder.decodeModQ(data1, numOnes, maxIndex);
+
+ int data2Len = (numNegOnes * bitsPerIndex + 7) / 8;
+ byte[] data2 = Util.readFullLength(is, data2Len);
+ int[] negOnes = ArrayEncoder.decodeModQ(data2, numNegOnes, maxIndex);
+
+ return new SparseTernaryPolynomial(N, ones, negOnes);
+ }
+
+ /**
+ * Generates a random polynomial with numOnes
coefficients equal to 1,
+ * numNegOnes
coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ */
+ public static SparseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
+ return new SparseTernaryPolynomial(coeffs);
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2)
+ {
+ int[] b = poly2.coeffs;
+ if (b.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ int[] c = new int[N];
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] += b[j];
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] -= b[j];
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ return new IntegerPolynomial(c);
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ BigInteger[] b = poly2.coeffs;
+ if (b.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigInteger[] c = new BigInteger[N];
+ for (int i = 0; i < N; i++)
+ {
+ c[i] = BigInteger.ZERO;
+ }
+
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] = c[k].add(b[j]);
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] = c[k].subtract(b[j]);
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ return new BigIntPolynomial(c);
+ }
+
+ public int[] getOnes()
+ {
+ return ones;
+ }
+
+ public int[] getNegOnes()
+ {
+ return negOnes;
+ }
+
+ /**
+ * Encodes the polynomial to a byte array writing BITS_PER_INDEX
bits for each coefficient.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary()
+ {
+ int maxIndex = 1 << BITS_PER_INDEX;
+ byte[] bin1 = ArrayEncoder.encodeModQ(ones, maxIndex);
+ byte[] bin2 = ArrayEncoder.encodeModQ(negOnes, maxIndex);
+
+ byte[] bin = Arrays.copyOf(bin1, bin1.length + bin2.length);
+ System.arraycopy(bin2, 0, bin, bin1.length, bin2.length);
+ return bin;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] coeffs = new int[N];
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ coeffs[i] = 1;
+ }
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ coeffs[i] = -1;
+ }
+ return new IntegerPolynomial(coeffs);
+ }
+
+ public int size()
+ {
+ return N;
+ }
+
+ public void clear()
+ {
+ for (int i = 0; i < ones.length; i++)
+ {
+ ones[i] = 0;
+ }
+ for (int i = 0; i < negOnes.length; i++)
+ {
+ negOnes[i] = 0;
+ }
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + Arrays.hashCode(negOnes);
+ result = prime * result + Arrays.hashCode(ones);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ SparseTernaryPolynomial other = (SparseTernaryPolynomial)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (!Arrays.areEqual(negOnes, other.negOnes))
+ {
+ return false;
+ }
+ if (!Arrays.areEqual(ones, other.ones))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java
new file mode 100644
index 000000000..6b9530b3b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+/**
+ * A polynomial whose coefficients are all equal to -1, 0, or 1
+ */
+public interface TernaryPolynomial
+ extends Polynomial
+{
+
+ /**
+ * Multiplies the polynomial by an IntegerPolynomial
, taking the indices mod N
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2);
+
+ int[] getOnes();
+
+ int[] getNegOnes();
+
+ /**
+ * Returns the maximum number of coefficients the polynomial can have
+ */
+ int size();
+
+ void clear();
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java
new file mode 100644
index 000000000..8c9e3e034
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java
@@ -0,0 +1,292 @@
+package org.spongycastle.pqc.math.ntru.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * Converts a coefficient array to a compact byte array and vice versa.
+ */
+public class ArrayEncoder
+{
+ /**
+ * Bit string to coefficient conversion table from P1363.1. Also found at
+ * {@link http://stackoverflow.com/questions/1562548/how-to-make-a-message-into-a-polynomial}
+ *
+ * Convert each three-bit quantity to two ternary coefficients as follows, and concatenate the resulting
+ * ternary quantities to obtain [the output].
+ *
+ *
+ * {0, 0, 0} -> {0, 0}
+ */
+ private static final int[] COEFF1_TABLE = {0, 0, 0, 1, 1, 1, -1, -1};
+ private static final int[] COEFF2_TABLE = {0, 1, -1, 0, 1, -1, 0, 1};
+ /**
+ * Coefficient to bit string conversion table from P1363.1. Also found at
+ * {@link http://stackoverflow.com/questions/1562548/how-to-make-a-message-into-a-polynomial}
+ *
+ * Convert each set of two ternary coefficients to three bits as follows, and concatenate the resulting bit
+ * quantities to obtain [the output]:
+ *
+ *
+ * {0, 0, 1} -> {0, 1}
+ * {0, 1, 0} -> {0, -1}
+ * {0, 1, 1} -> {1, 0}
+ * {1, 0, 0} -> {1, 1}
+ * {1, 0, 1} -> {1, -1}
+ * {1, 1, 0} -> {-1, 0}
+ * {1, 1, 1} -> {-1, 1}
+ *
+ * {-1, -1} -> set "fail" to 1 and set bit string to {1, 1, 1}
+ * {-1, 0} -> {1, 1, 0}
+ */
+ private static final int[] BIT1_TABLE = {1, 1, 1, 0, 0, 0, 1, 0, 1};
+ private static final int[] BIT2_TABLE = {1, 1, 1, 1, 0, 0, 0, 1, 0};
+ private static final int[] BIT3_TABLE = {1, 0, 1, 0, 0, 1, 1, 1, 0};
+
+ /**
+ * Encodes an int array whose elements are between 0 and
+ * {-1, 1} -> {1, 1, 1}
+ * {0, -1} -> {0, 1, 0}
+ * {0, 0} -> {0, 0, 0}
+ * {0, 1} -> {0, 0, 1}
+ * {1, -1} -> {1, 0, 1}
+ * {1, 0} -> {0, 1, 1}
+ * {1, 1} -> {1, 0, 0}
+ * q
,
+ * to a byte array leaving no gaps between bits.
+ * q
must be a power of 2.
+ *
+ * @param a the input array
+ * @param q the modulus
+ * @return the encoded array
+ */
+ public static byte[] encodeModQ(int[] a, int q)
+ {
+ int bitsPerCoeff = 31 - Integer.numberOfLeadingZeros(q);
+ int numBits = a.length * bitsPerCoeff;
+ int numBytes = (numBits + 7) / 8;
+ byte[] data = new byte[numBytes];
+ int bitIndex = 0;
+ int byteIndex = 0;
+ for (int i = 0; i < a.length; i++)
+ {
+ for (int j = 0; j < bitsPerCoeff; j++)
+ {
+ int currentBit = (a[i] >> j) & 1;
+ data[byteIndex] |= currentBit << bitIndex;
+ if (bitIndex == 7)
+ {
+ bitIndex = 0;
+ byteIndex++;
+ }
+ else
+ {
+ bitIndex++;
+ }
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Decodes a byte
array encoded with {@link #encodeModQ(int[], int)} back to an int
array.
+ * N
is the number of coefficients. q
must be a power of 2
.
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return an array containing N
coefficients between 0
and q-1
+ */
+ public static int[] decodeModQ(byte[] data, int N, int q)
+ {
+ int[] coeffs = new int[N];
+ int bitsPerCoeff = 31 - Integer.numberOfLeadingZeros(q);
+ int numBits = N * bitsPerCoeff;
+ int coeffIndex = 0;
+ for (int bitIndex = 0; bitIndex < numBits; bitIndex++)
+ {
+ if (bitIndex > 0 && bitIndex % bitsPerCoeff == 0)
+ {
+ coeffIndex++;
+ }
+ int bit = getBit(data, bitIndex);
+ coeffs[coeffIndex] += bit << (bitIndex % bitsPerCoeff);
+ }
+ return coeffs;
+ }
+
+ /**
+ * Decodes data encoded with {@link #encodeModQ(int[], int)} back to an int
array.
+ * N
is the number of coefficients. q
must be a power of 2
.
+ * Ignores any excess bytes.
+ *
+ * @param is an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static int[] decodeModQ(InputStream is, int N, int q)
+ throws IOException
+ {
+ int qBits = 31 - Integer.numberOfLeadingZeros(q);
+ int size = (N * qBits + 7) / 8;
+ byte[] arr = Util.readFullLength(is, size);
+ return decodeModQ(arr, N, q);
+ }
+
+ /**
+ * Decodes a byte
array encoded with {@link #encodeMod3Sves(int[])} back to an int
array
+ * with N
coefficients between -1
and 1
.
+ * Ignores any excess bytes.
+ * See P1363.1 section 9.2.2.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @return the decoded coefficients
+ */
+ public static int[] decodeMod3Sves(byte[] data, int N)
+ {
+ int[] coeffs = new int[N];
+ int coeffIndex = 0;
+ for (int bitIndex = 0; bitIndex < data.length * 8; )
+ {
+ int bit1 = getBit(data, bitIndex++);
+ int bit2 = getBit(data, bitIndex++);
+ int bit3 = getBit(data, bitIndex++);
+ int coeffTableIndex = bit1 * 4 + bit2 * 2 + bit3;
+ coeffs[coeffIndex++] = COEFF1_TABLE[coeffTableIndex];
+ coeffs[coeffIndex++] = COEFF2_TABLE[coeffTableIndex];
+ // ignore bytes that can't fit
+ if (coeffIndex > N - 2)
+ {
+ break;
+ }
+ }
+ return coeffs;
+ }
+
+ /**
+ * Encodes an int
array whose elements are between -1
and 1
, to a byte array.
+ * coeffs[2*i]
and coeffs[2*i+1]
must not both equal -1 for any integer i,
+ * so this method is only safe to use with arrays produced by {@link #decodeMod3Sves(byte[], int)}.
)
+ *
+ * @see #setCertStores(java.util.List)
+ **/
+ public List getCertStores()
+ {
+ return Collections.unmodifiableList(certStores);
+ }
+
+ /**
+ * Sets the RevocationEnabled flag. If this flag is true, the default
+ * revocation checking mechanism of the underlying PKIX service provider
+ * will be used. If this flag is false, the default revocation checking
+ * mechanism will be disabled (not used).
+ * See P1363.1 section 9.2.3.
+ *
+ * @param arr
+ * @return the encoded array
+ */
+ public static byte[] encodeMod3Sves(int[] arr)
+ {
+ int numBits = (arr.length * 3 + 1) / 2;
+ int numBytes = (numBits + 7) / 8;
+ byte[] data = new byte[numBytes];
+ int bitIndex = 0;
+ int byteIndex = 0;
+ for (int i = 0; i < arr.length / 2 * 2; )
+ { // if length is an odd number, throw away the highest coeff
+ int coeff1 = arr[i++] + 1;
+ int coeff2 = arr[i++] + 1;
+ if (coeff1 == 0 && coeff2 == 0)
+ {
+ throw new IllegalStateException("Illegal encoding!");
+ }
+ int bitTableIndex = coeff1 * 3 + coeff2;
+ int[] bits = new int[]{BIT1_TABLE[bitTableIndex], BIT2_TABLE[bitTableIndex], BIT3_TABLE[bitTableIndex]};
+ for (int j = 0; j < 3; j++)
+ {
+ data[byteIndex] |= bits[j] << bitIndex;
+ if (bitIndex == 7)
+ {
+ bitIndex = 0;
+ byteIndex++;
+ }
+ else
+ {
+ bitIndex++;
+ }
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Encodes an int
array whose elements are between -1
and 1
, to a byte array.
+ *
+ * @return the encoded array
+ */
+ public static byte[] encodeMod3Tight(int[] intArray)
+ {
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = intArray.length - 1; i >= 0; i--)
+ {
+ sum = sum.multiply(BigInteger.valueOf(3));
+ sum = sum.add(BigInteger.valueOf(intArray[i] + 1));
+ }
+
+ int size = (BigInteger.valueOf(3).pow(intArray.length).bitLength() + 7) / 8;
+ byte[] arr = sum.toByteArray();
+
+ if (arr.length < size)
+ {
+ // pad with leading zeros so arr.length==size
+ byte[] arr2 = new byte[size];
+ System.arraycopy(arr, 0, arr2, size - arr.length, arr.length);
+ return arr2;
+ }
+
+ if (arr.length > size)
+ // drop sign bit
+ {
+ arr = Arrays.copyOfRange(arr, 1, arr.length);
+ }
+ return arr;
+ }
+
+ /**
+ * Converts a byte array produced by {@link #encodeMod3Tight(int[])} back to an int
array.
+ *
+ * @param b a byte array
+ * @param N number of coefficients
+ * @return the decoded array
+ */
+ public static int[] decodeMod3Tight(byte[] b, int N)
+ {
+ BigInteger sum = new BigInteger(1, b);
+ int[] coeffs = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = sum.mod(BigInteger.valueOf(3)).intValue() - 1;
+ if (coeffs[i] > 1)
+ {
+ coeffs[i] -= 3;
+ }
+ sum = sum.divide(BigInteger.valueOf(3));
+ }
+ return coeffs;
+ }
+
+ /**
+ * Converts data produced by {@link #encodeMod3Tight(int[])} back to an int
array.
+ *
+ * @param is an input stream containing the data to decode
+ * @param N number of coefficients
+ * @return the decoded array
+ */
+ public static int[] decodeMod3Tight(InputStream is, int N)
+ throws IOException
+ {
+ int size = (int)Math.ceil(N * Math.log(3) / Math.log(2) / 8);
+ byte[] arr = Util.readFullLength(is, size);
+ return decodeMod3Tight(arr, N);
+ }
+
+ private static int getBit(byte[] arr, int bitIndex)
+ {
+ int byteIndex = bitIndex / 8;
+ int arrElem = arr[byteIndex] & 0xFF;
+ return (arrElem >> (bitIndex % 8)) & 1;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java
new file mode 100644
index 000000000..b07e1cb88
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java
@@ -0,0 +1,158 @@
+package org.spongycastle.pqc.math.ntru.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.spongycastle.pqc.math.ntru.euclid.IntEuclidean;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.TernaryPolynomial;
+import org.spongycastle.util.Integers;
+
+public class Util
+{
+ private static volatile boolean IS_64_BITNESS_KNOWN;
+ private static volatile boolean IS_64_BIT_JVM;
+
+ /**
+ * Calculates the inverse of n mod modulus
+ */
+ public static int invert(int n, int modulus)
+ {
+ n %= modulus;
+ if (n < 0)
+ {
+ n += modulus;
+ }
+ return IntEuclidean.calculate(n, modulus).x;
+ }
+
+ /**
+ * Calculates a^b mod modulus
+ */
+ public static int pow(int a, int b, int modulus)
+ {
+ int p = 1;
+ for (int i = 0; i < b; i++)
+ {
+ p = (p * a) % modulus;
+ }
+ return p;
+ }
+
+ /**
+ * Calculates a^b mod modulus
+ */
+ public static long pow(long a, int b, long modulus)
+ {
+ long p = 1;
+ for (int i = 0; i < b; i++)
+ {
+ p = (p * a) % modulus;
+ }
+ return p;
+ }
+
+ /**
+ * Generates a "sparse" or "dense" polynomial containing numOnes ints equal to 1,
+ * numNegOnes int equal to -1, and the rest equal to 0.
+ *
+ * @param N
+ * @param numOnes
+ * @param numNegOnes
+ * @param sparse whether to create a {@link SparseTernaryPolynomial} or {@link DenseTernaryPolynomial}
+ * @return a ternary polynomial
+ */
+ public static TernaryPolynomial generateRandomTernary(int N, int numOnes, int numNegOnes, boolean sparse, SecureRandom random)
+ {
+ if (sparse)
+ {
+ return SparseTernaryPolynomial.generateRandom(N, numOnes, numNegOnes, random);
+ }
+ else
+ {
+ return DenseTernaryPolynomial.generateRandom(N, numOnes, numNegOnes, random);
+ }
+ }
+
+ /**
+ * Generates an array containing numOnes ints equal to 1,
+ * numNegOnes int equal to -1, and the rest equal to 0.
+ *
+ * @param N
+ * @param numOnes
+ * @param numNegOnes
+ * @return an array of integers
+ */
+ public static int[] generateRandomTernary(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ Integer one = Integers.valueOf(1);
+ Integer minusOne = Integers.valueOf(-1);
+ Integer zero = Integers.valueOf(0);
+
+ List list = new ArrayList();
+ for (int i = 0; i < numOnes; i++)
+ {
+ list.add(one);
+ }
+ for (int i = 0; i < numNegOnes; i++)
+ {
+ list.add(minusOne);
+ }
+ while (list.size() < N)
+ {
+ list.add(zero);
+ }
+
+ Collections.shuffle(list, random);
+
+ int[] arr = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ arr[i] = ((Integer)list.get(i)).intValue();
+ }
+ return arr;
+ }
+
+ /**
+ * Takes an educated guess as to whether 64 bits are supported by the JVM.
+ *
+ * @return true
if 64-bit support detected, false
otherwise
+ */
+ public static boolean is64BitJVM()
+ {
+ if (!IS_64_BITNESS_KNOWN)
+ {
+ String arch = System.getProperty("os.arch");
+ String sunModel = System.getProperty("sun.arch.data.model");
+ IS_64_BIT_JVM = "amd64".equals(arch) || "x86_64".equals(arch) || "ppc64".equals(arch) || "64".equals(sunModel);
+ IS_64_BITNESS_KNOWN = true;
+ }
+ return IS_64_BIT_JVM;
+ }
+
+ /**
+ * Reads a given number of bytes from an InputStream
.
+ * If there are not enough bytes in the stream, an IOException
+ * is thrown.
+ *
+ * @param is
+ * @param length
+ * @return an array of length length
+ * @throws IOException
+ */
+ public static byte[] readFullLength(InputStream is, int length)
+ throws IOException
+ {
+ byte[] arr = new byte[length];
+ if (is.read(arr) != arr.length)
+ {
+ throw new IOException("Not enough bytes to read.");
+ }
+ return arr;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Arrays.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Arrays.java
new file mode 100644
index 000000000..05bb3e12e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Arrays.java
@@ -0,0 +1,856 @@
+package org.spongycastle.util;
+
+import java.math.BigInteger;
+
+/**
+ * General array utilities.
+ */
+public final class Arrays
+{
+ private Arrays()
+ {
+ // static class, hide constructor
+ }
+
+ public static boolean areEqual(
+ boolean[] a,
+ boolean[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ char[] a,
+ char[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ byte[] a,
+ byte[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * A constant time equals comparison - does not terminate early if
+ * test will fail.
+ *
+ * @param a first array
+ * @param b second array
+ * @return true if arrays equal, false otherwise.
+ */
+ public static boolean constantTimeAreEqual(
+ byte[] a,
+ byte[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ int nonEqual = 0;
+
+ for (int i = 0; i != a.length; i++)
+ {
+ nonEqual |= (a[i] ^ b[i]);
+ }
+
+ return nonEqual == 0;
+ }
+
+ public static boolean areEqual(
+ int[] a,
+ int[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ long[] a,
+ long[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(Object[] a, Object[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+ if (a == null || b == null)
+ {
+ return false;
+ }
+ if (a.length != b.length)
+ {
+ return false;
+ }
+ for (int i = 0; i != a.length; i++)
+ {
+ Object objA = a[i], objB = b[i];
+ if (objA == null)
+ {
+ if (objB != null)
+ {
+ return false;
+ }
+ }
+ else if (!objA.equals(objB))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean contains(short[] a, short n)
+ {
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] == n)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(int[] a, int n)
+ {
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] == n)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void fill(
+ byte[] array,
+ byte value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ char[] array,
+ char value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ long[] array,
+ long value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ short[] array,
+ short value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ int[] array,
+ int value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static int hashCode(byte[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i];
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(char[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i];
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(int[][] ints)
+ {
+ int hc = 0;
+
+ for (int i = 0; i != ints.length; i++)
+ {
+ hc = hc * 257 + hashCode(ints[i]);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(int[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i];
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(short[][][] shorts)
+ {
+ int hc = 0;
+
+ for (int i = 0; i != shorts.length; i++)
+ {
+ hc = hc * 257 + hashCode(shorts[i]);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(short[][] shorts)
+ {
+ int hc = 0;
+
+ for (int i = 0; i != shorts.length; i++)
+ {
+ hc = hc * 257 + hashCode(shorts[i]);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(short[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= (data[i] & 0xff);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(Object[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i].hashCode();
+ }
+
+ return hc;
+ }
+
+ public static byte[] clone(byte[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[] copy = new byte[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static byte[] clone(byte[] data, byte[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ return clone(data);
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
+ public static byte[][] clone(byte[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ byte[][] copy = new byte[data.length][];
+
+ for (int i = 0; i != copy.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ public static byte[][][] clone(byte[][][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ byte[][][] copy = new byte[data.length][][];
+
+ for (int i = 0; i != copy.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ public static int[] clone(int[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ int[] copy = new int[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static long[] clone(long[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ long[] copy = new long[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static long[] clone(long[] data, long[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ return clone(data);
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
+ public static short[] clone(short[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ short[] copy = new short[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static BigInteger[] clone(BigInteger[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ BigInteger[] copy = new BigInteger[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static byte[] copyOf(byte[] data, int newLength)
+ {
+ byte[] tmp = new byte[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static char[] copyOf(char[] data, int newLength)
+ {
+ char[] tmp = new char[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static int[] copyOf(int[] data, int newLength)
+ {
+ int[] tmp = new int[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static long[] copyOf(long[] data, int newLength)
+ {
+ long[] tmp = new long[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static BigInteger[] copyOf(BigInteger[] data, int newLength)
+ {
+ BigInteger[] tmp = new BigInteger[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ /**
+ * Make a copy of a range of bytes from the passed in data array. The range can
+ * extend beyond the end of the input array, in which case the return array will
+ * be padded with zeroes.
+ *
+ * @param data the array from which the data is to be copied.
+ * @param from the start index at which the copying should take place.
+ * @param to the final index of the range (exclusive).
+ *
+ * @return a new byte array containing the range given.
+ */
+ public static byte[] copyOfRange(byte[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ byte[] tmp = new byte[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ public static int[] copyOfRange(int[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ int[] tmp = new int[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ public static long[] copyOfRange(long[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ long[] tmp = new long[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ public static BigInteger[] copyOfRange(BigInteger[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ BigInteger[] tmp = new BigInteger[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ private static int getLength(int from, int to)
+ {
+ int newLength = to - from;
+ if (newLength < 0)
+ {
+ StringBuffer sb = new StringBuffer(from);
+ sb.append(" > ").append(to);
+ throw new IllegalArgumentException(sb.toString());
+ }
+ return newLength;
+ }
+
+ public static byte[] append(byte[] a, byte b)
+ {
+ if (a == null)
+ {
+ return new byte[]{ b };
+ }
+
+ int length = a.length;
+ byte[] result = new byte[length + 1];
+ System.arraycopy(a, 0, result, 0, length);
+ result[length] = b;
+ return result;
+ }
+
+ public static int[] append(int[] a, int b)
+ {
+ if (a == null)
+ {
+ return new int[]{ b };
+ }
+
+ int length = a.length;
+ int[] result = new int[length + 1];
+ System.arraycopy(a, 0, result, 0, length);
+ result[length] = b;
+ return result;
+ }
+
+ public static byte[] concatenate(byte[] a, byte[] b)
+ {
+ if (a != null && b != null)
+ {
+ byte[] rv = new byte[a.length + b.length];
+
+ System.arraycopy(a, 0, rv, 0, a.length);
+ System.arraycopy(b, 0, rv, a.length, b.length);
+
+ return rv;
+ }
+ else if (b != null)
+ {
+ return clone(b);
+ }
+ else
+ {
+ return clone(a);
+ }
+ }
+
+ public static byte[] concatenate(byte[] a, byte[] b, byte[] c)
+ {
+ if (a != null && b != null && c != null)
+ {
+ byte[] rv = new byte[a.length + b.length + c.length];
+
+ System.arraycopy(a, 0, rv, 0, a.length);
+ System.arraycopy(b, 0, rv, a.length, b.length);
+ System.arraycopy(c, 0, rv, a.length + b.length, c.length);
+
+ return rv;
+ }
+ else if (b == null)
+ {
+ return concatenate(a, c);
+ }
+ else
+ {
+ return concatenate(a, b);
+ }
+ }
+
+ public static byte[] concatenate(byte[] a, byte[] b, byte[] c, byte[] d)
+ {
+ if (a != null && b != null && c != null && d != null)
+ {
+ byte[] rv = new byte[a.length + b.length + c.length + d.length];
+
+ System.arraycopy(a, 0, rv, 0, a.length);
+ System.arraycopy(b, 0, rv, a.length, b.length);
+ System.arraycopy(c, 0, rv, a.length + b.length, c.length);
+ System.arraycopy(d, 0, rv, a.length + b.length + c.length, d.length);
+
+ return rv;
+ }
+ else if (d == null)
+ {
+ return concatenate(a, b, c);
+ }
+ else if (c == null)
+ {
+ return concatenate(a, b, d);
+ }
+ else if (b == null)
+ {
+ return concatenate(a, c, d);
+ }
+ else
+ {
+ return concatenate(b, c, d);
+ }
+ }
+
+ public static byte[] prepend(byte[] a, byte b)
+ {
+ if (a == null)
+ {
+ return new byte[]{ b };
+ }
+
+ int length = a.length;
+ byte[] result = new byte[length + 1];
+ System.arraycopy(a, 0, result, 1, length);
+ result[0] = b;
+ return result;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/BigIntegers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/BigIntegers.java
new file mode 100644
index 000000000..93c113476
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/BigIntegers.java
@@ -0,0 +1,121 @@
+package org.spongycastle.util;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * BigInteger utilities.
+ */
+public final class BigIntegers
+{
+ private static final int MAX_ITERATIONS = 1000;
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ /**
+ * Return the passed in value as an unsigned byte array.
+ *
+ * @param value value to be converted.
+ * @return a byte array without a leading zero byte if present in the signed encoding.
+ */
+ public static byte[] asUnsignedByteArray(
+ BigInteger value)
+ {
+ byte[] bytes = value.toByteArray();
+
+ if (bytes[0] == 0)
+ {
+ byte[] tmp = new byte[bytes.length - 1];
+
+ System.arraycopy(bytes, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Return the passed in value as an unsigned byte array.
+ *
+ * @param value value to be converted.
+ * @return a byte array without a leading zero byte if present in the signed encoding.
+ */
+ public static byte[] asUnsignedByteArray(int length, BigInteger value)
+ {
+ byte[] bytes = value.toByteArray();
+ if (bytes.length == length)
+ {
+ return bytes;
+ }
+
+ int start = bytes[0] == 0 ? 1 : 0;
+ int count = bytes.length - start;
+
+ if (count > length)
+ {
+ throw new IllegalArgumentException("standard length exceeded for value");
+ }
+
+ byte[] tmp = new byte[length];
+ System.arraycopy(bytes, start, tmp, tmp.length - count, count);
+ return tmp;
+ }
+
+ /**
+ * Return a random BigInteger not less than 'min' and not greater than 'max'
+ *
+ * @param min the least value that may be generated
+ * @param max the greatest value that may be generated
+ * @param random the source of randomness
+ * @return a random BigInteger value in the range [min,max]
+ */
+ public static BigInteger createRandomInRange(
+ BigInteger min,
+ BigInteger max,
+ SecureRandom random)
+ {
+ int cmp = min.compareTo(max);
+ if (cmp >= 0)
+ {
+ if (cmp > 0)
+ {
+ throw new IllegalArgumentException("'min' may not be greater than 'max'");
+ }
+
+ return min;
+ }
+
+ if (min.bitLength() > max.bitLength() / 2)
+ {
+ return createRandomInRange(ZERO, max.subtract(min), random).add(min);
+ }
+
+ for (int i = 0; i < MAX_ITERATIONS; ++i)
+ {
+ BigInteger x = new BigInteger(max.bitLength(), random);
+ if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0)
+ {
+ return x;
+ }
+ }
+
+ // fall back to a faster (restricted) method
+ return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
+ }
+
+ public static BigInteger fromUnsignedByteArray(byte[] buf)
+ {
+ return new BigInteger(1, buf);
+ }
+
+ public static BigInteger fromUnsignedByteArray(byte[] buf, int off, int length)
+ {
+ byte[] mag = buf;
+ if (off != 0 || length != buf.length)
+ {
+ mag = new byte[length];
+ System.arraycopy(buf, off, mag, 0, length);
+ }
+ return new BigInteger(1, mag);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/CollectionStore.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/CollectionStore.java
new file mode 100644
index 000000000..fa4c9fe56
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/CollectionStore.java
@@ -0,0 +1,57 @@
+package org.spongycastle.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A simple collection backed store.
+ */
+public class CollectionStore
+ implements Store
+{
+ private Collection _local;
+
+ /**
+ * Basic constructor.
+ *
+ * @param collection - initial contents for the store, this is copied.
+ */
+ public CollectionStore(
+ Collection collection)
+ {
+ _local = new ArrayList(collection);
+ }
+
+ /**
+ * Return the matches in the collection for the passed in selector.
+ *
+ * @param selector the selector to match against.
+ * @return a possibly empty collection of matching objects.
+ */
+ public Collection getMatches(Selector selector)
+ {
+ if (selector == null)
+ {
+ return new ArrayList(_local);
+ }
+ else
+ {
+ List col = new ArrayList();
+ Iterator iter = _local.iterator();
+
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if (selector.match(obj))
+ {
+ col.add(obj);
+ }
+ }
+
+ return col;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/IPAddress.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/IPAddress.java
new file mode 100644
index 000000000..14c49cad5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/IPAddress.java
@@ -0,0 +1,188 @@
+package org.spongycastle.util;
+
+public class IPAddress
+{
+ /**
+ * Validate the given IPv4 or IPv6 address.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid address, false otherwise
+ */
+ public static boolean isValid(
+ String address)
+ {
+ return isValidIPv4(address) || isValidIPv6(address);
+ }
+
+ /**
+ * Validate the given IPv4 or IPv6 address and netmask.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid address with netmask, false otherwise
+ */
+ public static boolean isValidWithNetMask(
+ String address)
+ {
+ return isValidIPv4WithNetmask(address) || isValidIPv6WithNetmask(address);
+ }
+
+ /**
+ * Validate the given IPv4 address.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid IPv4 address, false otherwise
+ */
+ public static boolean isValidIPv4(
+ String address)
+ {
+ if (address.length() == 0)
+ {
+ return false;
+ }
+
+ int octet;
+ int octets = 0;
+
+ String temp = address+".";
+
+ int pos;
+ int start = 0;
+ while (start < temp.length()
+ && (pos = temp.indexOf('.', start)) > start)
+ {
+ if (octets == 4)
+ {
+ return false;
+ }
+ try
+ {
+ octet = Integer.parseInt(temp.substring(start, pos));
+ }
+ catch (NumberFormatException ex)
+ {
+ return false;
+ }
+ if (octet < 0 || octet > 255)
+ {
+ return false;
+ }
+ start = pos + 1;
+ octets++;
+ }
+
+ return octets == 4;
+ }
+
+ public static boolean isValidIPv4WithNetmask(
+ String address)
+ {
+ int index = address.indexOf("/");
+ String mask = address.substring(index + 1);
+
+ return (index > 0) && isValidIPv4(address.substring(0, index))
+ && (isValidIPv4(mask) || isMaskValue(mask, 32));
+ }
+
+ public static boolean isValidIPv6WithNetmask(
+ String address)
+ {
+ int index = address.indexOf("/");
+ String mask = address.substring(index + 1);
+
+ return (index > 0) && (isValidIPv6(address.substring(0, index))
+ && (isValidIPv6(mask) || isMaskValue(mask, 128)));
+ }
+
+ private static boolean isMaskValue(String component, int size)
+ {
+ try
+ {
+ int value = Integer.parseInt(component);
+
+ return value >= 0 && value <= size;
+ }
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Validate the given IPv6 address.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid IPv4 address, false otherwise
+ */
+ public static boolean isValidIPv6(
+ String address)
+ {
+ if (address.length() == 0)
+ {
+ return false;
+ }
+
+ int octet;
+ int octets = 0;
+
+ String temp = address + ":";
+ boolean doubleColonFound = false;
+ int pos;
+ int start = 0;
+ while (start < temp.length()
+ && (pos = temp.indexOf(':', start)) >= start)
+ {
+ if (octets == 8)
+ {
+ return false;
+ }
+
+ if (start != pos)
+ {
+ String value = temp.substring(start, pos);
+
+ if (pos == (temp.length() - 1) && value.indexOf('.') > 0)
+ {
+ if (!isValidIPv4(value))
+ {
+ return false;
+ }
+
+ octets++; // add an extra one as address covers 2 words.
+ }
+ else
+ {
+ try
+ {
+ octet = Integer.parseInt(temp.substring(start, pos), 16);
+ }
+ catch (NumberFormatException ex)
+ {
+ return false;
+ }
+ if (octet < 0 || octet > 0xffff)
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (pos != 1 && pos != temp.length() - 1 && doubleColonFound)
+ {
+ return false;
+ }
+ doubleColonFound = true;
+ }
+ start = pos + 1;
+ octets++;
+ }
+
+ return octets == 8 || doubleColonFound;
+ }
+}
+
+
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Integers.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Integers.java
new file mode 100644
index 000000000..adfa69501
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Integers.java
@@ -0,0 +1,9 @@
+package org.spongycastle.util;
+
+public class Integers
+{
+ public static Integer valueOf(int value)
+ {
+ return Integer.valueOf(value);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Memoable.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Memoable.java
new file mode 100644
index 000000000..75280e5a2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/Memoable.java
@@ -0,0 +1,23 @@
+package org.spongycastle.util;
+
+public interface Memoable
+{
+ /**
+ * Produce a copy of this object with its configuration and in its current state.
+ *
+ * The returned object may be used simply to store the state, or may be used as a similar object
+ * starting from the copied state.
+ */
+ public Memoable copy();
+
+ /**
+ * Restore a copied object state into this object.
+ *
+ * Implementations of this method should try to avoid or minimise memory allocation to perform the reset.
+ *
+ * @param other an object originally {@link #copy() copied} from an object of the same type as this instance.
+ * @throws ClassCastException if the provided object is not of the correct type.
+ * @throws MemoableResetException if the other parameter is in some other way invalid.
+ */
+ public void reset(Memoable other);
+}
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/util/MemoableResetException.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/MemoableResetException.java
new file mode 100644
index 000000000..6d579ed53
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/util/MemoableResetException.java
@@ -0,0 +1,22 @@
+package org.spongycastle.util;
+
+/**
+ * Exception to be thrown on a failure to reset an object implementing Memoable.
+ * CRL
s.
+ * Classes that implement this interface are often used to specify
+ * which CRL
s should be retrieved from a CertStore
.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this interface are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CRL
+ * @see CertStore
+ * @see CertStore#getCRLs
+ **/
+public interface CRLSelector extends Cloneable
+{
+ /**
+ * Decides whether a CRL
should be selected.
+ *
+ * @param crl the CRL
to be checked
+ *
+ * @return true
if the CRL
should be selected,
+ * false
otherwise
+ */
+ public boolean match(CRL crl);
+
+ /**
+ * Makes a copy of this CRLSelector
. Changes to the
+ * copy will not affect the original and vice versa.
+ *
+ * @return a copy of this CRLSelector
+ */
+ public Object clone();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPath.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPath.java
new file mode 100644
index 000000000..ceb5cd189
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPath.java
@@ -0,0 +1,283 @@
+package java.security.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.NotSerializableException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * An immutable sequence of certificates (a certification path).
+ *
+ * This is an abstract class that defines the methods common to all
+ * CertPaths. Subclasses can handle different kinds of certificates
+ * (X.509, PGP, etc.).
+ *
+ * All CertPath objects have a type, a list of Certificates, and one
+ * or more supported encodings. Because the CertPath class is
+ * immutable, a CertPath cannot change in any externally visible way
+ * after being constructed. This stipulation applies to all public
+ * fields and methods of this class and any added or overridden by
+ * subclasses.
+ *
+ * The type is a String that identifies the type of Certificates in
+ * the certification path. For each certificate cert in a
+ * certification path certPath,
+ * cert.getType().equals(certPath.getType()) must be true.
+ *
+ * The list of Certificates is an ordered List of zero or more
+ * Certificates. This List and all of the Certificates contained in it
+ * must be immutable.
+ *
+ * Each CertPath object must support one or more encodings so that the
+ * object can be translated into a byte array for storage or
+ * transmission to other parties. Preferably, these encodings should
+ * be well-documented standards (such as PKCS#7). One of the encodings
+ * supported by a CertPath is considered the default encoding. This
+ * encoding is used if no encoding is explicitly requested (for the
+ * {@link #getEncoded()} method, for instance).
+ *
+ * All CertPath objects are also Serializable. CertPath objects are
+ * resolved into an alternate {@link CertPathRep} object during
+ * serialization. This allows a CertPath object to be serialized into
+ * an equivalent representation regardless of its underlying
+ * implementation.
+ *
+ * CertPath objects can be created with a CertificateFactory or they
+ * can be returned by other classes, such as a CertPathBuilder.
+ *
+ * By convention, X.509 CertPaths (consisting of X509Certificates),
+ * are ordered starting with the target certificate and ending with a
+ * certificate issued by the trust anchor. That is, the issuer of one
+ * certificate is the subject of the following one. The certificate
+ * representing the {@link TrustAnchor TrustAnchor} should not be included in the
+ * certification path. Unvalidated X.509 CertPaths may not follow
+ * these conventions. PKIX CertPathValidators will detect any
+ * departure from these conventions that cause the certification path
+ * to be invalid and throw a CertPathValidatorException.
+ *
+ * Concurrent Access
+ *
+ * All CertPath objects must be thread-safe. That is, multiple threads
+ * may concurrently invoke the methods defined in this class on a
+ * single CertPath object (or more than one) with no ill effects. This
+ * is also true for the List returned by CertPath.getCertificates.
+ *
+ * Requiring CertPath objects to be immutable and thread-safe allows
+ * them to be passed around to various pieces of code without worrying
+ * about coordinating access. Providing this thread-safety is
+ * generally not difficult, since the CertPath and List objects in
+ * question are immutable.
+ *
+ * @see CertificateFactory
+ * @see CertPathBuilder
+ */
+public abstract class CertPath extends Object implements Serializable
+{
+ private String type;
+
+ /**
+ * Alternate CertPath
class for serialization.
+ **/
+ protected static class CertPathRep
+ implements Serializable
+ {
+ private String type;
+ private byte[] data;
+
+ /**
+ * Creates a CertPathRep
with the specified
+ * type and encoded form of a certification path.
+ *
+ * @param type the standard name of a CertPath
+ * @param typedata the encoded form of the certification
+ * path
+ **/
+ protected CertPathRep(String type, byte[] data)
+ {
+ this.type = type;
+ this.data = data;
+ }
+
+ /**
+ * Returns a CertPath constructed from the type and data.
+ *
+ * @return the resolved CertPath object
+ * @exception ObjectStreamException if a CertPath could not be constructed
+ **/
+ protected Object readResolve()
+ throws ObjectStreamException
+ {
+ try {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ CertificateFactory cf = CertificateFactory.getInstance(type);
+ return cf.generateCertPath(inStream);
+ } catch ( CertificateException ce ) {
+ throw new NotSerializableException(" java.security.cert.CertPath: " + type);
+ }
+ }
+ }
+
+ /**
+ * Creates a CertPath of the specified type.
+ * This constructor is protected because most users should use
+ * a CertificateFactory to create CertPaths.
+ * @param type the standard name of the type of Certificatesin this path
+ **/
+ protected CertPath(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns the type of Certificates in this certification
+ * path. This is the same string that would be returned by
+ * {@link Certificate#getType() cert.getType()} for all
+ * Certificates in the certification path.
+ *
+ * @return the type of Certificates in this certification path (never null)
+ **/
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns an iteration of the encodings supported by this
+ * certification path, with the default encoding
+ * first. Attempts to modify the returned Iterator via its
+ * remove method result in an UnsupportedOperationException.
+ *
+ * @return an Iterator over the names of the supported encodings (as Strings)
+ **/
+ public abstract Iterator getEncodings();
+
+ /**
+ * Compares this certification path for equality with the
+ * specified object. Two CertPaths are equal if and only if
+ * their types are equal and their certificate Lists (and by
+ * implication the Certificates in those Lists) are equal. A
+ * CertPath is never equal to an object that is not a
+ * CertPath.
+ *
+ * This algorithm is implemented by this method. If it is
+ * overridden, the behavior specified here must be maintained.
+ *
+ * @param other the object to test for equality with this
+ * certification path
+ *
+ * @return true if the specified object is equal to this
+ * certification path, false otherwise
+ *
+ * @see Object#hashCode() Object.hashCode()
+ **/
+ public boolean equals(Object other)
+ {
+ if (!( other instanceof CertPath ) )
+ return false;
+
+ CertPath otherCertPath = (CertPath)other;
+ if ( ! getType().equals(otherCertPath.getType()) )
+ return false;
+ return getCertificates().equals(otherCertPath.getCertificates());
+ }
+
+ /**
+ * Returns the hashcode for this certification path. The hash
+ * code of a certification path is defined to be the result of
+ * the following calculation:
+ *
+ * hashCode = path.getType().hashCode();
+ * hashCode = 31 * hashCode + path.getCertificates().hashCode();
+ *
+ * This ensures that path1.equals(path2) implies that
+ * path1.hashCode()==path2.hashCode() for any two
+ * certification paths, path1 and path2, as required by the
+ * general contract of Object.hashCode.
+ *
+ * @return The hashcode value for this certification path
+ *
+ * @see #equals(Object)
+ **/
+ public int hashCode()
+ {
+ return getType().hashCode() * 31 + getCertificates().hashCode();
+ }
+
+ /**
+ * Returns a string representation of this certification
+ * path. This calls the toString method on each of the
+ * Certificates in the path.
+ *
+ * @return a string representation of this certification path
+ **/
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ List certs = getCertificates();
+ ListIterator iter = certs.listIterator();
+ s.append('\n').append(getType()).append(" Cert Path: length = ").append(certs.size()).append("\n[\n");
+ while ( iter.hasNext() ) {
+ s.append("=========================================================Certificate ").append(iter.nextIndex()).append('\n');
+ s.append(iter.next()).append('\n');
+ s.append("========================================================Certificate end\n\n\n");
+ }
+ s.append("\n]");
+ return s.toString();
+ }
+
+ /**
+ * Returns the encoded form of this certification path, using
+ * the default encoding.
+ *
+ * @return the encoded bytes
+ *
+ * @exception CertificateEncodingException if an encoding error occurs
+ **/
+ public abstract byte[] getEncoded()
+ throws CertificateEncodingException;
+
+ /**
+ * Returns the encoded form of this certification path, using
+ * the specified encoding.
+ *
+ * @param encoding the name of the encoding to use
+ *
+ * @return the encoded bytes
+ *
+ * @exception CertificateEncodingException if an encoding error
+ * occurs or the encoding requested is not supported
+ **/
+ public abstract byte[] getEncoded(String encoding)
+ throws CertificateEncodingException;
+
+ /**
+ * Returns the list of certificates in this certification
+ * path. The List returned must be immutable and thread-safe.
+ *
+ * @return an immutable List of Certificates (may be empty, but not null)
+ **/
+ public abstract List getCertificates();
+
+ /**
+ * Replaces the CertPath to be serialized with a CertPathRep
+ * object.
+ *
+ * @return the CertPathRep to be serialized
+ *
+ * @exception ObjectStreamException if a CertPathRep object
+ * representing this certification path could not be created
+ **/
+ protected Object writeReplace()
+ throws ObjectStreamException
+ {
+ try {
+ return new CertPathRep( getType(), getEncoded() );
+ } catch ( CertificateException ce ) {
+ throw new NotSerializableException( " java.security.cert.CertPath: " + getType() );
+ }
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilder.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilder.java
new file mode 100644
index 000000000..b3adbf15f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilder.java
@@ -0,0 +1,243 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+
+/**
+ * A class for building certification paths (also known as certificate
+ * chains).
+ *
+ * This class uses a provider-based architecture, as described in the
+ * Java Cryptography Architecture. To create a
+ * CertPathBuilder
, call one of the static
+ * getInstance
methods, passing in the algorithm name of
+ * the CertPathBuilder desired and optionally the name of the provider
+ * desired.
+ *
+ * Once a CertPathBuilder
object has been created,
+ * certification paths can be constructed by calling the
+ * {@link #build build} method and passing it an algorithm-specific set
+ * of parameters. If successful, the result (including the CertPath
+ * that was built) is returned in an object that implements the
+ * CertPathBuilderResult
interface.
+ *
+ * Concurrent Access
+ *
+ * The static methods of this class are guaranteed to be
+ * thread-safe. Multiple threads may concurrently invoke the static
+ * methods defined in this class with no ill effects.
+ *
+ * However, this is not true for the non-static methods defined by
+ * this class. Unless otherwise documented by a specific provider,
+ * threads that need to access a single CertPathBuilder
+ * instance concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating a
+ * different CertPathBuilder
instance need not
+ * synchronize.
+ *
+ * Uses {@link CertUtil CertUtil} to actualiy load the SPI classes.
+ *
+ * @see CertUtil
+ **/
+public class CertPathBuilder extends Object
+{
+ private CertPathBuilderSpi builderSpi;
+ private Provider provider;
+ private String algorithm;
+
+ /**
+ * Creates a CertPathBuilder object of the given algorithm, and
+ * encapsulates the given provider implementation (SPI object)
+ * in it.
+ *
+ * @param builderSpi the provider implementation
+ * @param provider the provider
+ * @param algorithm the algorithm name
+ **/
+ protected CertPathBuilder(CertPathBuilderSpi builderSpi,
+ Provider provider,
+ String algorithm)
+ {
+ this.builderSpi = builderSpi;
+ this.provider = provider;
+ this.algorithm = algorithm;
+ }
+
+ /**
+ * Returns a CertPathBuilder object that implements the
+ * specified algorithm.
+ *
+ * If the default provider package provides an implementation
+ * of the specified CertPathBuilder algorithm, an instance of
+ * CertPathBuilder containing that implementation is
+ * returned. If the requested algorithm is not available in
+ * the default package, other packages are searched.
+ *
+ * @param algorithm the name of the requested CertPathBuilder algorithm
+ *
+ * @return a CertPathBuilder object that implements the
+ * specified algorithm
+ *
+ * @exception NoSuchAlgorithmException if the requested
+ * algorithm is not available in the default provider package
+ * or any of the other provider packages that were searched
+ **/
+ public static CertPathBuilder getInstance(String algorithm)
+ throws NoSuchAlgorithmException
+ {
+ try {
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation("CertPathBuilder", algorithm, (String)null);
+ if (imp != null)
+ {
+ return new CertPathBuilder((CertPathBuilderSpi)imp.getEngine(),
+ imp.getProvider(), algorithm);
+ }
+ } catch ( NoSuchProviderException ex ) {}
+ throw new NoSuchAlgorithmException("can't find type " + algorithm);
+ }
+
+ /**
+ * Returns a CertPathBuilder object that implements the
+ * specified algorithm, as supplied by the specified provider.
+ *
+ * @param algorithm the name of the requested CertPathBuilder
+ * algorithm
+ * @param provider the name of the provider
+ *
+ * @return a CertPathBuilder object that implements the
+ * specified algorithm, as supplied by the specified provider
+ *
+ * @exception NoSuchAlgorithmException if the requested algorithm
+ * is not available from the specified provider
+ * @exception NoSuchProviderException if the provider has not
+ * been configured
+ * @exception IllegalArgumentException if the provider is null
+ **/
+ public static CertPathBuilder getInstance(String algorithm,
+ String provider)
+ throws NoSuchAlgorithmException,
+ NoSuchProviderException
+ {
+ if ( provider == null )
+ throw new IllegalArgumentException("provider must be non-null");
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation("CertPathBuilder", algorithm, provider);
+
+ if (imp != null)
+ {
+ return new CertPathBuilder((CertPathBuilderSpi)imp.getEngine(),
+ imp.getProvider(), algorithm);
+ }
+ throw new NoSuchAlgorithmException("can't find type " + algorithm);
+ }
+
+ /**
+ * Returns a CertPathBuilder object that implements the
+ * specified algorithm, as supplied by the specified
+ * provider. Note: the provider doesn't have to be registered.
+ *
+ * @param algorithm the name of the requested CertPathBuilder
+ * algorithm
+ * @param provider the provider
+ * @return a CertPathBuilder object that implements the
+ * specified algorithm, as supplied by the specified provider
+ *
+ * @exception NoSuchAlgorithmException if the requested algorithm
+ * is not available from the specified provider
+ * @exception IllegalArgumentException if the provider is null.
+ **/
+ public static CertPathBuilder getInstance(String algorithm,
+ Provider provider)
+ throws NoSuchAlgorithmException
+ {
+ if ( provider == null )
+ throw new IllegalArgumentException("provider must be non-null");
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation("CertPathBuilder", algorithm, provider);
+
+ if (imp != null)
+ {
+ return new CertPathBuilder((CertPathBuilderSpi)imp.getEngine(),
+ provider, algorithm);
+ }
+ throw new NoSuchAlgorithmException("can't find type " + algorithm);
+ }
+
+ /**
+ * Returns the provider of this CertPathBuilder
.
+ *
+ * @return the provider of this CertPathBuilder
+ **/
+ public final Provider getProvider()
+ {
+ return provider;
+ }
+
+ /**
+ * Returns the name of the algorithm of this
+ * CertPathBuilder
.
+ *
+ * @return the name of the algorithm of this CertPathBuilder
+ **/
+ public final String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ /**
+ * Attempts to build a certification path using the specified algorithm
+ * parameter set.
+ *
+ * @param params the algorithm parameters
+ *
+ * @return the result of the build algorithm
+ *
+ * @exception CertPathBuilderException if the builder is unable to construct
+ * a certification path that satisfies the specified parameters
+ * @exception InvalidAlgorithmParameterException if the specified parameters * are inappropriate for this CertPathBuilder
+ */
+ public final CertPathBuilderResult build(CertPathParameters params)
+ throws CertPathBuilderException,
+ InvalidAlgorithmParameterException
+ {
+ return builderSpi.engineBuild(params);
+ }
+
+
+ /**
+ * Returns the default CertPathBuilder
type as specified in
+ * the Java security properties file, or the string "PKIX"
+ * if no such property exists. The Java security properties file is
+ * located in the file named <JAVA_HOME>/lib/security/java.security,
+ * where <JAVA_HOME> refers to the directory where the SDK was
+ * installed.
+ *
+ * The default CertPathBuilder
type can be used by
+ * applications that do not want to use a hard-coded type when calling one
+ * of the getInstance
methods, and want to provide a default
+ * type in case a user does not specify its own.
+ *
+ * The default CertPathBuilder
type can be changed by
+ * setting the value of the "certpathbuilder.type" security property
+ * (in the Java security properties file) to the desired type.
+ *
+ * @return the default CertPathBuilder
type as specified
+ * in the Java security properties file, or the string "PKIX"
+ * if no such property exists.
+ */
+ public static final String getDefaultType()
+ {
+ String defaulttype = null;
+ defaulttype = Security.getProperty("certpathbuilder.type");
+
+ if ( defaulttype == null || defaulttype.length() <= 0 )
+ return "PKIX";
+ else
+ return defaulttype;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderException.java
new file mode 100644
index 000000000..13b60891e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderException.java
@@ -0,0 +1,182 @@
+package java.security.cert;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
+
+/**
+ * An exception indicating one of a variety of problems encountered
+ * when building a certification path with a
+ * CertPathBuilder
.
+ *
+ * A CertPathBuilderException
provides support for
+ * wrapping exceptions. The {@link #getCause() getCause} method
+ * returns the throwable, if any, that caused this exception to be
+ * thrown.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are
+ * not thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertPathBuilder
+ **/
+public class CertPathBuilderException extends GeneralSecurityException
+{
+ private Throwable cause;
+
+ /**
+ * Creates a CertPathBuilderException
with null
+ * as its detail message.
+ */
+ public CertPathBuilderException()
+ {
+ }
+
+ /**
+ * Creates a CertPathBuilderException
with the given detail
+ * message. The detail message is a String
that describes
+ * this particular exception in more detail.
+ *
+ * @param msg
+ * the detail message
+ */
+ public CertPathBuilderException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a CertPathBuilderException
that wraps the
+ * specified throwable. This allows any exception to be converted into a
+ * CertPathBuilderException
, while retaining information
+ * about the wrapped exception, which may be useful for debugging. The
+ * detail message is set to
+ * (cause==null ? null : cause.toString())
(which typically
+ * contains the class and detail message of cause).
+ *
+ * @param cause
+ * the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A null value is permitted, and
+ * indicates that the cause is nonexistent or unknown.)
+ */
+ public CertPathBuilderException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ /**
+ * Creates a CertPathBuilderException
with the specified
+ * detail message and cause.
+ *
+ * @param msg
+ * the detail message
+ * @param cause
+ * the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A null value is permitted, and
+ * indicates that the cause is nonexistent or unknown.)
+ */
+ public CertPathBuilderException(Throwable cause)
+ {
+ this.cause = cause;
+ }
+
+ /**
+ * Returns the internal (wrapped) cause, or null if the cause is nonexistent
+ * or unknown.
+ *
+ * @return the cause of this throwable or null
if the cause
+ * is nonexistent or unknown.
+ */
+ public Throwable getCause()
+ {
+ return cause;
+ }
+
+ /**
+ * Returns the detail message for this CertPathBuilderException.
+ *
+ * @return the detail message, or null
if neither the message
+ * nor internal cause were specified
+ */
+ public String getMessage()
+ {
+ String message = super.getMessage();
+
+ if (message == null && cause == null)
+ {
+ return null;
+ }
+
+ if (cause != null)
+ {
+ return cause.getMessage();
+ }
+
+ return message;
+ }
+
+ /**
+ * Returns a string describing this exception, including a description of
+ * the internal (wrapped) cause if there is one.
+ *
+ * @return a string representation of this
+ * CertPathBuilderException
+ */
+ public String toString()
+ {
+ String message = getMessage();
+ if (message == null)
+ {
+ return "";
+ }
+
+ return message;
+ }
+
+ /**
+ * Prints a stack trace to System.err
, including the
+ * backtrace of the cause, if any.
+ */
+ public void printStackTrace()
+ {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Prints a stack trace to a PrintStream
, including the
+ * backtrace of the cause, if any.
+ *
+ * @param ps
+ * the PrintStream
to use for output
+ */
+ public void printStackTrace(PrintStream ps)
+ {
+ super.printStackTrace(ps);
+ if (getCause() != null)
+ {
+ getCause().printStackTrace(ps);
+ }
+ }
+
+ /**
+ * Prints a stack trace to a PrintWriter
, including the
+ * backtrace of the cause, if any.
+ *
+ * @param ps
+ * the PrintWriter
to use for output
+ */
+ public void printStackTrace(PrintWriter pw)
+ {
+ super.printStackTrace(pw);
+ if (getCause() != null)
+ {
+ getCause().printStackTrace(pw);
+ }
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderResult.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderResult.java
new file mode 100644
index 000000000..c0482bc4e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderResult.java
@@ -0,0 +1,38 @@
+package java.security.cert;
+
+/**
+ * A specification of the result of a certification path builder algorithm.
+ * All results returned by the {@link CertPathBuilder#build CertPathBuilder.build} method
+ * must implement this interface.
+ *
+ * At a minimum, a CertPathBuilderResult contains the CertPath built by the
+ * CertPathBuilder instance. Implementations of this interface may add methods
+ * to return implementation or algorithm specific information, such as
+ * debugging information or certification path validation results.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this interface are not
+ * thread-safe. Multiple threads that need to access a single object
+ * concurrently should synchronize amongst themselves and provide the
+ * necessary locking. Multiple threads each manipulating separate objects
+ * need not synchronize.
+ **/
+public interface CertPathBuilderResult extends Cloneable
+{
+ /**
+ * Returns the built certification path.
+ *
+ * @return the certification path (never null
)
+ */
+ public CertPath getCertPath();
+
+ /**
+ * Makes a copy of this CertPathBuilderResult
.
+ * Changes to the copy will not affect the original and vice
+ * versa.
+ *
+ * @return a copy of this CertPathBuilderResult
+ */
+ public Object clone();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderSpi.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderSpi.java
new file mode 100644
index 000000000..be044fa30
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathBuilderSpi.java
@@ -0,0 +1,50 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+
+/**
+ * The Service Provider Interface (SPI) for the CertPathBuilder
+ * class. All CertPathBuilder implementations must include a class
+ * (the SPI class) that extends this class (CertPathBuilderSpi) and
+ * implements all of its methods. In general, instances of this class
+ * should only be accessed through the CertPathBuilder class. For
+ * details, see the Java Cryptography Architecture.
+ *
+ * Concurrent Access
+ *
+ * Instances of this class need not be protected against concurrent
+ * access from multiple threads. Threads that need to access a single
+ * CertPathBuilderSpi instance concurrently should synchronize amongst
+ * themselves and provide the necessary locking before calling the
+ * wrapping CertPathBuilder object.
+ *
+ * However, implementations of CertPathBuilderSpi may still encounter
+ * concurrency issues, since multiple threads each manipulating a
+ * different CertPathBuilderSpi instance need not synchronize.
+ **/
+public abstract class CertPathBuilderSpi
+ extends Object
+{
+ /**
+ * The default constructor.
+ */
+ public CertPathBuilderSpi() {}
+
+ /**
+ * Attempts to build a certification path using the specified
+ * algorithm parameter set.
+ *
+ * @param params the algorithm parameters
+ *
+ * @return the result of the build algorithm
+ *
+ * @exception CertPathBuilderException if the builder is unable
+ * to construct a certification path that satisfies the
+ * specified
+ * @exception parametersInvalidAlgorithmParameterException if the
+ * specified parameters are inappropriate for this CertPathBuilder
+ */
+ public abstract CertPathBuilderResult engineBuild( CertPathParameters params )
+ throws CertPathBuilderException,
+ InvalidAlgorithmParameterException;
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathParameters.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathParameters.java
new file mode 100644
index 000000000..caff291a8
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathParameters.java
@@ -0,0 +1,18 @@
+package java.security.cert;
+
+/**
+ * A specification of certification path algorithm parameters. The purpose
+ * of this interface is to group (and provide type safety for) all CertPath
+ * parameter specifications. All CertPath
parameter specifications must
+ * implement this interface.
+ **/
+public interface CertPathParameters extends Cloneable
+{
+ /**
+ * Makes a copy of this CertPathParameters
. Changes to the
+ * copy will not affect the original and vice versa.
+ *
+ * @return a copy of this CertPathParameters
+ **/
+ public Object clone();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidator.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidator.java
new file mode 100644
index 000000000..aaddbf0e5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidator.java
@@ -0,0 +1,250 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+
+/**
+ * A class for validating certification paths (also known as certificate
+ * chains).
+ *
+ * This class uses a provider-based architecture, as described in the Java
+ * Cryptography Architecture. To create a CertPathValidator
,
+ * call one of the static getInstance
methods, passing in the
+ * algorithm name of the CertPathValidator
desired and
+ * optionally the name of the provider desired.
+ *
+ * Once a CertPathValidator
object has been created, it can
+ * be used to validate certification paths by calling the {@link #validate
+ * validate} method and passing it the CertPath
to be validated
+ * and an algorithm-specific set of parameters. If successful, the result is
+ * returned in an object that implements the
+ * CertPathValidatorResult
interface.
+ *
+ * Concurrent Access
+ *
+ * The static methods of this class are guaranteed to be thread-safe.
+ * Multiple threads may concurrently invoke the static methods defined in
+ * this class with no ill effects.
+ *
+ * However, this is not true for the non-static methods defined by this class.
+ * Unless otherwise documented by a specific provider, threads that need to
+ * access a single CertPathValidator
instance concurrently should
+ * synchronize amongst themselves and provide the necessary locking. Multiple
+ * threads each manipulating a different CertPathValidator
+ * instance need not synchronize.
+ *
+ * Uses {@link CertUtil CertUtil} to actualiy load the SPI classes.
+ *
+ * @see CertPath
+ * @see CertUtil
+ **/
+public class CertPathValidator extends Object
+{
+ private CertPathValidatorSpi validatorSpi;
+ private Provider provider;
+ private String algorithm;
+
+ /**
+ * Creates a CertPathValidator
object of the given algorithm,
+ * and encapsulates the given provider implementation (SPI object) in it.
+ *
+ * @param validatorSpi the provider implementation
+ * @param provider the provider
+ * @param algorithm the algorithm name
+ */
+ protected CertPathValidator( CertPathValidatorSpi validatorSpi,
+ Provider provider,
+ String algorithm)
+ {
+ this.validatorSpi = validatorSpi;
+ this.provider = provider;
+ this.algorithm = algorithm;
+ }
+
+ /**
+ * Returns a CertPathValidator
object that implements the
+ * specified algorithm.
+ *
+ * If the default provider package provides an implementation of the
+ * specified CertPathValidator
algorithm, an instance of
+ * CertPathValidator
containing that implementation is
+ * returned. If the requested algorithm is not available in the default
+ * package, other packages are searched.
+ *
+ * @param algorithm the name of the requested CertPathValidator
+ * algorithm
+ *
+ * @return a CertPathValidator
object that implements the
+ * specified algorithm
+ *
+ * @exception NoSuchAlgorithmException if the requested algorithm
+ * is not available in the default provider package or any of the other
+ * provider packages that were searched
+ */
+ public static CertPathValidator getInstance(String algorithm)
+ throws NoSuchAlgorithmException
+ {
+ try {
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation("CertPathValidator", algorithm, (String)null );
+ if (imp != null)
+ {
+ return new CertPathValidator((CertPathValidatorSpi)imp.getEngine(), imp.getProvider(), algorithm);
+ }
+ } catch (NoSuchProviderException ex ) {}
+ throw new NoSuchAlgorithmException("can't find algorithm " + algorithm);
+ }
+
+ /**
+ * Returns a CertPathValidator
object that implements the
+ * specified algorithm, as supplied by the specified provider.
+ *
+ * @param algorithm the name of the requested CertPathValidator
+ * algorithm
+ * @param provider the name of the provider
+ *
+ * @return a CertPathValidator
object that implements the
+ * specified algorithm, as supplied by the specified provider
+ *
+ * @exception NoSuchAlgorithmException if the requested algorithm
+ * is not available from the specified provider
+ * @exception NoSuchProviderException if the provider has not been
+ * configured
+ * @exception IllegalArgumentException if the provider
is
+ * null
+ */
+ public static CertPathValidator getInstance(String algorithm,
+ String provider)
+ throws NoSuchAlgorithmException,
+ NoSuchProviderException
+ {
+ if ( provider == null )
+ throw new IllegalArgumentException("provider must be non-null");
+
+ CertUtil.Implementation imp = CertUtil.getImplementation("CertPathValidator", algorithm, provider );
+ if (imp != null)
+ {
+ return new CertPathValidator((CertPathValidatorSpi)imp.getEngine(), imp.getProvider(), algorithm);
+ }
+ throw new NoSuchAlgorithmException("can't find algorithm " + algorithm);
+ }
+
+ /**
+ * Returns a CertPathValidator
object that implements the
+ * specified algorithm, as supplied by the specified provider.
+ * Note: the provider
doesn't have to be registered.
+ *
+ * @param algorithm the name of the requested
+ * CertPathValidator
algorithm
+ * @param provider the provider
+ *
+ * @return a CertPathValidator
object that implements the
+ * specified algorithm, as supplied by the specified provider
+ *
+ * @exception NoSuchAlgorithmException if the requested algorithm
+ * is not available from the specified provider
+ * @exception IllegalArgumentException if the provider
is
+ * null
+ */
+ public static CertPathValidator getInstance(String algorithm,
+ Provider provider)
+ throws NoSuchAlgorithmException
+ {
+ if ( provider == null )
+ throw new IllegalArgumentException("provider must be non-null");
+
+ CertUtil.Implementation imp = CertUtil.getImplementation("CertPathValidator", algorithm, provider );
+ if (imp != null)
+ {
+ return new CertPathValidator((CertPathValidatorSpi)imp.getEngine(), provider, algorithm);
+ }
+ throw new NoSuchAlgorithmException("can't find algorithm " + algorithm);
+ }
+
+ /**
+ * Returns the Provider
of this
+ * CertPathValidator
.
+ *
+ * @return the Provider
of this CertPathValidator
+ */
+ public final Provider getProvider()
+ {
+ return provider;
+ }
+
+ /**
+ * Returns the algorithm name of this CertPathValidator
.
+ *
+ * @return the algorithm name of this CertPathValidator
+ */
+ public final String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ /**
+ * Validates the specified certification path using the specified
+ * algorithm parameter set.
+ *
+ * The CertPath
specified must be of a type that is
+ * supported by the validation algorithm, otherwise an
+ * InvalidAlgorithmParameterException
will be thrown. For
+ * example, a CertPathValidator
that implements the PKIX
+ * algorithm validates CertPath
objects of type X.509.
+ *
+ * @param certPath the CertPath
to be validated
+ * @param params the algorithm parameters
+ *
+ * @return the result of the validation algorithm
+ *
+ * @exception CertPathValidatorException if the CertPath
+ * does not validate
+ * @exception InvalidAlgorithmParameterException if the specified
+ * parameters or the type of the specified CertPath
are
+ * inappropriate for this CertPathValidator
+ */
+ public final CertPathValidatorResult validate( CertPath certPath,
+ CertPathParameters params)
+ throws CertPathValidatorException,
+ InvalidAlgorithmParameterException
+ {
+ return validatorSpi.engineValidate( certPath, params );
+ }
+
+
+ /**
+ * Returns the default CertPathValidator
type as specified in
+ * the Java security properties file, or the string "PKIX"
+ * if no such property exists. The Java security properties file is
+ * located in the file named <JAVA_HOME>/lib/security/java.security,
+ * where <JAVA_HOME> refers to the directory where the SDK was
+ * installed.
+ *
+ * The default CertPathValidator
type can be used by
+ * applications that do not want to use a hard-coded type when calling one
+ * of the getInstance
methods, and want to provide a default
+ * type in case a user does not specify its own.
+ *
+ * The default CertPathValidator
type can be changed by
+ * setting the value of the "certpathvalidator.type" security property
+ * (in the Java security properties file) to the desired type.
+ *
+ * @return the default CertPathValidator
type as specified
+ * in the Java security properties file, or the string "PKIX"
+ * if no such property exists.
+ */
+ public static final String getDefaultType()
+ {
+ String defaulttype = null;
+ defaulttype = Security.getProperty("certpathvalidator.type");
+
+ if ( defaulttype == null || defaulttype.length() <= 0 )
+ return "PKIX";
+ else
+ return defaulttype;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorException.java
new file mode 100644
index 000000000..2088ab1a4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorException.java
@@ -0,0 +1,248 @@
+package java.security.cert;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
+
+/**
+ * An exception indicating one of a variety of problems encountered when
+ * validating a certification path.
+ *
+ * A CertPathValidatorException
provides support for wrapping
+ * exceptions. The {@link #getCause getCause} method returns the throwable,
+ * if any, that caused this exception to be thrown.
+ *
+ * A CertPathValidatorException
may also include the
+ * certification path that was being validated when the exception was thrown
+ * and the index of the certificate in the certification path that caused the
+ * exception to be thrown. Use the {@link #getCertPath getCertPath} and
+ * {@link #getIndex getIndex} methods to retrieve this information.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertPathValidator
+ **/
+public class CertPathValidatorException extends GeneralSecurityException
+{
+ private Throwable cause;
+ private CertPath certPath;
+ private int index = -1;
+
+ /**
+ * Creates a CertPathValidatorException
with
+ * no detail message.
+ */
+ public CertPathValidatorException()
+ {
+ super();
+ }
+
+ /**
+ * Creates a CertPathValidatorException
with the given
+ * detail message. A detail message is a String
that
+ * describes this particular exception.
+ *
+ * @param messag the detail message
+ */
+ public CertPathValidatorException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a CertPathValidatorException
with the specified
+ * detail message and cause.
+ *
+ * @param msg the detail message
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause getCause()} method). (A null
value is
+ * permitted, and indicates that the cause is nonexistent or unknown.)
+ */
+ public CertPathValidatorException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ /**
+ * Creates a CertPathValidatorException
with the specified
+ * detail message, cause, certification path, and index.
+ *
+ * @param msg the detail message (or null
if none)
+ * @param cause the cause (or null
if none)
+ * @param certPath the certification path that was in the process of
+ * being validated when the error was encountered
+ * @param index the index of the certificate in the certification path
+ * that caused the error (or -1 if not applicable). Note that
+ * the list of certificates in a CertPath
is zero based.
+ *
+ * @exception IndexOutOfBoundsException if the index is out of range
+ * (index < -1 || (certPath != null && index >=
+ * certPath.getCertificates().size())
+ * @exception IllegalArgumentException if certPath
is
+ * null
and index
is not -1
+ */
+ public CertPathValidatorException(String message, Throwable cause, CertPath certPath, int index)
+ {
+ super( message );
+
+ if ( certPath == null && index != -1 )
+ throw new IllegalArgumentException( "certPath = null and index != -1" );
+ if ( index < -1 || ( certPath != null && index >= certPath.getCertificates().size() ) )
+ throw new IndexOutOfBoundsException( " index < -1 or out of bound of certPath.getCertificates()" );
+
+ this.cause = cause;
+ this.certPath = certPath;
+ this.index = index;
+ }
+
+ /**
+ * Creates a CertPathValidatorException
that wraps the
+ * specified throwable. This allows any exception to be converted into a
+ * CertPathValidatorException
, while retaining information
+ * about the wrapped exception, which may be useful for debugging. The
+ * detail message is set to (cause==null ? null : cause.toString()
+ *
) (which typically contains the class and detail message of
+ * cause).
+ *
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause getCause()} method). (A null
value is
+ * permitted, and indicates that the cause is nonexistent or unknown.)
+ */
+ public CertPathValidatorException(Throwable cause)
+ {
+ this.cause = cause;
+ }
+
+ /**
+ * Returns the detail message for this
+ * CertPathValidatorException
.
+ *
+ * @return the detail message, or null
if neither the message
+ * nor cause were specified
+ */
+ public String getMessage()
+ {
+ String message = super.getMessage();
+
+ if ( message == null && cause == null )
+ return null;
+
+ StringBuffer s = new StringBuffer();
+ if ( message != null )
+ {
+ s.append(message).append('\n');
+ }
+ if ( cause != null )
+ {
+ s.append("Cause:\n").append(cause.getMessage()).append('\n');
+ }
+ return s.toString();
+ }
+
+ /**
+ * Returns the certification path that was being validated when
+ * the exception was thrown.
+ *
+ * @return the CertPath
that was being validated when
+ * the exception was thrown (or null
if not specified)
+ */
+ public CertPath getCertPath()
+ {
+ return certPath;
+ }
+
+ /**
+ * Returns the index of the certificate in the certification path
+ * that caused the exception to be thrown. Note that the list of
+ * certificates in a CertPath
is zero based. If no
+ * index has been set, -1 is returned.
+ *
+ * @return the index that has been set, or -1 if none has been set
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * Returns the cause of this CertPathValidatorException
or
+ * null
if the cause is nonexistent or unknown.
+ *
+ * @return the cause of this throwable or null
if the cause
+ * is nonexistent or unknown.
+ */
+ public Throwable getCause()
+ {
+ return cause;
+ }
+
+ /**
+ * Returns a string describing this exception, including a description
+ * of the internal (wrapped) cause if there is one.
+ *
+ * @return a string representation of this
+ * CertPathValidatorException
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ String s = getMessage();
+ if ( s != null )
+ {
+ sb.append( s );
+ }
+ if ( getIndex() >= 0 )
+ {
+ sb.append("index in certpath: ").append(getIndex()).append('\n');
+ sb.append(getCertPath());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Prints a stack trace to System.err
, including the backtrace
+ * of the cause, if any.
+ */
+ public void printStackTrace()
+ {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Prints a stack trace to a PrintStream
, including the
+ * backtrace of the cause, if any.
+ *
+ * @param ps the PrintStream
to use for output
+ */
+ public void printStackTrace(PrintStream ps)
+ {
+ super.printStackTrace(ps);
+ if ( getCause() != null )
+ {
+ getCause().printStackTrace(ps);
+ }
+ }
+
+ /**
+ * Prints a stack trace to a PrintWriter
, including the
+ * backtrace of the cause, if any.
+ *
+ * @param pw the PrintWriter
to use for output
+ */
+ public void printStackTrace(PrintWriter pw)
+ {
+ super.printStackTrace(pw);
+ if ( getCause() != null )
+ {
+ getCause().printStackTrace(pw);
+ }
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorResult.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorResult.java
new file mode 100644
index 000000000..ec09641d5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorResult.java
@@ -0,0 +1,22 @@
+package java.security.cert;
+
+/**
+ * A specification of the result of a certification path validator algorithm.
+ *
+ * The purpose of this interface is to group (and provide type safety
+ * for) all certification path validator results. All results returned
+ * by the {@link CertPathValidator#validate CertPathValidator.validate}
+ * method must implement this interface.
+ *
+ * @see CertPathValidator
+ **/
+public interface CertPathValidatorResult extends Cloneable
+{
+ /**
+ * Makes a copy of this CertPathValidatorResult
. Changes to the
+ * copy will not affect the original and vice versa.
+ *
+ * @return a copy of this CertPathValidatorResult
+ */
+ public Object clone();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorSpi.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorSpi.java
new file mode 100644
index 000000000..c70bc47fc
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertPathValidatorSpi.java
@@ -0,0 +1,59 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+
+/**
+ *
+ * The Service Provider Interface (SPI)
+ * for the {@link CertPathValidator CertPathValidator} class. All
+ * CertPathValidator
implementations must include a class (the
+ * SPI class) that extends this class (CertPathValidatorSpi
)
+ * and implements all of its methods. In general, instances of this class
+ * should only be accessed through the CertPathValidator
class.
+ * For details, see the Java Cryptography Architecture.
+ *
+ * Concurrent Access
+ *
+ * Instances of this class need not be protected against concurrent
+ * access from multiple threads. Threads that need to access a single
+ * CertPathValidatorSpi
instance concurrently should synchronize
+ * amongst themselves and provide the necessary locking before calling the
+ * wrapping CertPathValidator
object.
+ *
+ * However, implementations of CertPathValidatorSpi
may still
+ * encounter concurrency issues, since multiple threads each
+ * manipulating a different CertPathValidatorSpi
instance need not
+ * synchronize.
+ **/
+public abstract class CertPathValidatorSpi extends Object
+{
+ /**
+ * The default constructor.
+ */
+ public CertPathValidatorSpi() {}
+
+ /**
+ * Validates the specified certification path using the specified
+ * algorithm parameter set.
+ *
+ * The CertPath
specified must be of a type that is
+ * supported by the validation algorithm, otherwise an
+ * InvalidAlgorithmParameterException
will be thrown. For
+ * example, a CertPathValidator
that implements the PKIX
+ * algorithm validates CertPath
objects of type X.509.
+ *
+ * @param certPath the CertPath
to be validated
+ * @param params the algorithm parameters
+ *
+ * @return the result of the validation algorithm
+ *
+ * @exception CertPathValidatorException if the CertPath
+ * does not validate
+ * @exception InvalidAlgorithmParameterException if the specified
+ * parameters or the type of the specified CertPath
are
+ * inappropriate for this CertPathValidator
+ */
+ public abstract CertPathValidatorResult engineValidate(CertPath certPath, CertPathParameters params)
+ throws CertPathValidatorException,
+ InvalidAlgorithmParameterException;
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertSelector.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertSelector.java
new file mode 100644
index 000000000..31bf97448
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertSelector.java
@@ -0,0 +1,39 @@
+package java.security.cert;
+
+/**
+ * A selector that defines a set of criteria for selecting
+ * Certificate
s. Classes that implement this interface
+ * are often used to specify which Certificate
s should
+ * be retrieved from a CertStore
.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this interface are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see Certificate
+ * @see CertStore
+ * @see CertStore#getCertificates
+ */
+public interface CertSelector extends Cloneable
+{
+ /**
+ * Decides whether a Certificate
should be selected.
+ *
+ * @param cert the Certificate
to be checked
+ * @return true
if the Certificate
+ * should be selected, false
otherwise
+ */
+ public boolean match(Certificate cert);
+
+ /**
+ * Makes a copy of this CertSelector
. Changes to the
+ * copy will not affect the original and vice versa.
+ *
+ * @return a copy of this CertSelector
+ */
+ public Object clone();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStore.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStore.java
new file mode 100644
index 000000000..0e2c6d2f7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStore.java
@@ -0,0 +1,352 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Collection;
+
+/**
+ * A class for retrieving Certificate
s and CRL
s
+ * from a repository.
+ *
+ * This class uses a provider-based architecture, as described in the
+ * Java Cryptography Architecture.
+ * To create a CertStore
, call one of the static
+ * getInstance
methods, passing in the type of
+ * CertStore
desired, any applicable initialization parameters
+ * and optionally the name of the provider desired.
+ *
+ * Once the CertStore
has been created, it can be used to
+ * retrieve Certificate
s and CRL
s by calling its
+ * {@link #getCertificates(CertSelector selector) getCertificates} and
+ * {@link #getCRLs(CRLSelector selector) getCRLs} methods.
+ *
+ * Unlike a {@link java.security.KeyStore KeyStore}, which provides access
+ * to a cache of private keys and trusted certificates, a
+ * CertStore
is designed to provide access to a potentially
+ * vast repository of untrusted certificates and CRLs. For example, an LDAP
+ * implementation of CertStore
provides access to certificates
+ * and CRLs stored in one or more directories using the LDAP protocol and the
+ * schema as defined in the RFC service attribute. See Appendix A in the
+ * Java Certification Path API Programmer's Guide for more information about
+ * standard CertStore
types.
+ *
+ * Concurrent Access
+ *
+ * All public methods of CertStore
objects must be thread-safe.
+ * That is, multiple threads may concurrently invoke these methods on a
+ * single CertStore
object (or more than one) with no
+ * ill effects. This allows a CertPathBuilder
to search for a
+ * CRL while simultaneously searching for further certificates, for instance.
+ *
+ * The static methods of this class are also guaranteed to be thread-safe.
+ * Multiple threads may concurrently invoke the static methods defined in
+ * this class with no ill effects.
+ *
+ * Uses {@link CertUtil CertUtil} to actualiy load the SPI classes.
+ *
+ * @see CertUtil
+ **/
+public class CertStore extends Object
+{
+ private CertStoreSpi storeSpi;
+ private Provider provider;
+ private String type;
+ private CertStoreParameters params;
+
+ /**
+ * Creates a CertStore
object of the given type, and
+ * encapsulates the given provider implementation (SPI object) in it.
+ *
+ * @param storeSpi the provider implementation
+ * @param provider the provider
+ * @param type the type
+ * @param params the initialization parameters (may be null
)
+ */
+ protected CertStore( CertStoreSpi storeSpi,
+ Provider provider,
+ String type,
+ CertStoreParameters params )
+ {
+ this.storeSpi = storeSpi;
+ this.provider = provider;
+ this.type = type;
+ this.params = params;
+ }
+
+ /**
+ * Returns a Collection
of Certificate
s that
+ * match the specified selector. If no Certificate
s
+ * match the selector, an empty Collection
will be returned.
+ *
+ * For some CertStore
types, the resulting
+ * Collection
may not contain all of the
+ * Certificate
s that match the selector. For instance,
+ * an LDAP CertStore
may not search all entries in the
+ * directory. Instead, it may just search entries that are likely to
+ * contain the Certificate
s it is looking for.
+ *
+ * Some CertStore
implementations (especially LDAP
+ * CertStore
s) may throw a CertStoreException
+ * unless a non-null CertSelector
is provided that
+ * includes specific criteria that can be used to find the certificates.
+ * Issuer and/or subject names are especially useful criteria.
+ *
+ * @param selector A CertSelector
used to select which
+ * Certificate
s should be returned. Specify null
+ * to return all Certificate
s (if supported).
+ *
+ * @return A Collection
of Certificate
s that
+ * match the specified selector (never null
)
+ * @exception CertStoreException if an exception occurs
+ */
+ public final Collection getCertificates( CertSelector selector )
+ throws CertStoreException
+ {
+ return storeSpi.engineGetCertificates( selector );
+ }
+
+ /**
+ * Returns a Collection
of CRL
s that
+ * match the specified selector. If no CRL
s
+ * match the selector, an empty Collection
will be returned.
+ *
+ * For some CertStore
types, the resulting
+ * Collection
may not contain all of the
+ * CRL
s that match the selector. For instance,
+ * an LDAP CertStore
may not search all entries in the
+ * directory. Instead, it may just search entries that are likely to
+ * contain the CRL
s it is looking for.
+ *
+ * Some CertStore
implementations (especially LDAP
+ * CertStore
s) may throw a CertStoreException
+ * unless a non-null CRLSelector
is provided that
+ * includes specific criteria that can be used to find the CRLs.
+ * Issuer names and/or the certificate to be checked are especially useful.
+ *
+ * @param selector A CRLSelector
used to select which
+ * CRL
s should be returned. Specify null
+ * to return all CRL
s (if supported).
+ *
+ * @return A Collection
of CRL
s that
+ * match the specified selector (never null
)
+ *
+ * @exception CertStoreException if an exception occurs
+ */
+ public final Collection getCRLs( CRLSelector selector )
+ throws CertStoreException
+ {
+ return storeSpi.engineGetCRLs( selector );
+ }
+
+ /**
+ * Returns a CertStore
object that implements the specified
+ * CertStore
type and is initialized with the specified
+ * parameters.
+ *
+ * If the default provider package provides an implementation
+ * of the specified CertStore
type, an instance of
+ * CertStore
containing that implementation is returned.
+ * If the requested type is not available in the default package, other
+ * packages are searched.
+ *
+ * The CertStore
that is returned is initialized with the
+ * specified CertStoreParameters
. The type of parameters
+ * needed may vary between different types of CertStore
s.
+ * Note that the specified CertStoreParameters
object is
+ * cloned.
+ *
+ * @param type the name of the requested CertStore
type
+ * @param params the initialization parameters (may be null
)
+ *
+ * @return a CertStore
object that implements the specified
+ * CertStore
type
+ *
+ * @exception NoSuchAlgorithmException if the requested type is not
+ * available in the default provider package or any of the other provider
+ * packages that were searched
+ * @exception InvalidAlgorithmParameterException if the specified
+ * initialization parameters are inappropriate for this
+ * CertStore
+ */
+ public static CertStore getInstance( String type,
+ CertStoreParameters params)
+ throws InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException
+ {
+ try {
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation( "CertStore", type, (String)null,
+ new Class[] { CertStoreParameters.class },
+ new Object[] { params } );
+ if (imp != null)
+ {
+ return new CertStore((CertStoreSpi)imp.getEngine(), imp.getProvider(), type, params );
+ }
+ } catch ( NoSuchProviderException ex ) {}
+ throw new NoSuchAlgorithmException("can't find type " + type);
+ }
+
+ /**
+ * Returns a CertStore
object that implements the specified
+ * CertStore
type, as supplied by the specified provider
+ * and initialized with the specified parameters.
+ *
+ * The CertStore
that is returned is initialized with the
+ * specified CertStoreParameters
. The type of parameters
+ * needed may vary between different types of CertStore
s.
+ * Note that the specified CertStoreParameters
object is
+ * cloned.
+ *
+ * @param type the requested CertStore
type
+ * @param params the initialization parameters (may be null
)
+ * @param provider the name of the provider
+ *
+ * @return a CertStore
object that implements the
+ * specified type, as supplied by the specified provider
+ *
+ * @exception NoSuchAlgorithmException if the requested type is not
+ * available from the specified provider
+ * @exception InvalidAlgorithmParameterException if the specified
+ * initialization parameters are inappropriate for this
+ * CertStore
+ * @exception NoSuchProviderException if the provider has not been configured
+ * @exception IllegalArgumentException if the provider
is
+ * null
+ */
+ public static CertStore getInstance( String type,
+ CertStoreParameters params,
+ String provider)
+ throws InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException,
+ NoSuchProviderException,
+ IllegalArgumentException
+ {
+ if ( provider == null )
+ throw new IllegalArgumentException( "provider must be non-null" );
+
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation( "CertStore", type, provider,
+ new Class[] { CertStoreParameters.class },
+ new Object[] { params } );
+ if (imp != null)
+ {
+ return new CertStore((CertStoreSpi)imp.getEngine(), imp.getProvider(), type, params );
+ }
+ throw new NoSuchAlgorithmException("can't find type " + type);
+ }
+
+ /**
+ * Returns a CertStore
object that implements the specified
+ * CertStore
type, as supplied by the specified provider and
+ * initialized with the specified parameters.
+ * Note: the provider
doesn't have to be registered.
+ *
+ * The CertStore
that is returned is initialized with the
+ * specified CertStoreParameters
. The type of parameters
+ * needed may vary between different types of CertStore
s.
+ * Note that the specified CertStoreParameters
object is
+ * cloned.
+ *
+ * @param type the requested CertStore
type
+ * @param params the initialization parameters (may be null
)
+ * @param provider the provider
+ *
+ * @return a CertStore
object that implements the
+ * specified type, as supplied by the specified provider
+ *
+ * @exception NoSuchAlgorithmException if the requested type is not
+ * available from the specified provider
+ * @exception InvalidAlgorithmParameterException if the specified
+ * initialization parameters are inappropriate for this
+ * CertStore
+ * @exception IllegalArgumentException if the provider
is
+ * null
+ */
+ public static CertStore getInstance( String type,
+ CertStoreParameters params,
+ Provider provider )
+ throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException,
+ IllegalArgumentException
+ {
+ if ( provider == null )
+ throw new IllegalArgumentException( "provider must be non-null" );
+ CertUtil.Implementation imp =
+ CertUtil.getImplementation( "CertStore", type, provider,
+ new Class[] { CertStoreParameters.class },
+ new Object[] { params } );
+ if (imp != null)
+ {
+ return new CertStore((CertStoreSpi)imp.getEngine(), provider, type, params );
+ }
+ throw new NoSuchAlgorithmException("can't find type " + type);
+ }
+
+ /**
+ * Returns the parameters used to initialize this CertStore
.
+ * Note that the CertStoreParameters
object is cloned before
+ * it is returned.
+ *
+ * @return the parameters used to initialize this CertStore
+ * (may be null
)
+ */
+ public final CertStoreParameters getCertStoreParameters()
+ {
+ return params;
+ }
+
+ /**
+ * Returns the type of this CertStore
.
+ *
+ * @return the type of this CertStore
+ */
+ public final String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns the provider of this CertStore
.
+ *
+ * @return the provider of this CertStore
+ */
+ public final Provider getProvider()
+ {
+ return provider;
+ }
+
+ /**
+ * Returns the default CertStore
type as specified in the
+ * Java security properties file, or the string "LDAP" if no
+ * such property exists. The Java security properties file is located in
+ * the file named <JAVA_HOME>/lib/security/java.security, where
+ * <JAVA_HOME> refers to the directory where the SDK was installed.
+ *
+ * The default CertStore
type can be used by applications
+ * that do not want to use a hard-coded type when calling one of the
+ * getInstance
methods, and want to provide a default
+ * CertStore
type in case a user does not specify its own.
+ *
+ * The default CertStore
type can be changed by setting
+ * the value of the "certstore.type" security property (in the Java
+ * security properties file) to the desired type.
+ *
+ * @return the default CertStore
type as specified in the
+ * Java security properties file, or the string "LDAP"
+ * if no such property exists.
+ */
+ public static final String getDefaultType()
+ {
+ String defaulttype = null;
+ defaulttype = Security.getProperty("certstore.type");
+
+ if ( defaulttype == null || defaulttype.length() <= 0 )
+ return "LDAP";
+ else
+ return defaulttype;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreException.java
new file mode 100644
index 000000000..a15bc3df6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreException.java
@@ -0,0 +1,172 @@
+package java.security.cert;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
+
+/**
+ * An exception indicating one of a variety of problems retrieving
+ * certificates and CRLs from a CertStore
.
+ *
+ * A CertStoreException
provides support for wrapping
+ * exceptions. The {@link #getCause getCause} method returns the throwable,
+ * if any, that caused this exception to be thrown.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertStore
+ **/
+public class CertStoreException extends GeneralSecurityException
+{
+ private Throwable cause;
+
+ /**
+ * Creates a CertStoreException
with null
as
+ * its detail message.
+ */
+ public CertStoreException()
+ {
+ super();
+ }
+
+ /**
+ * Creates a CertStoreException
with the given detail
+ * message. A detail message is a String
that describes this
+ * particular exception.
+ *
+ * @param messag the detail message
+ */
+ public CertStoreException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a CertStoreException
with the specified detail
+ * message and cause.
+ *
+ * @param messag the detail message
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause getCause()} method). (A null
value is
+ * permitted, and indicates that the cause is nonexistent or unknown.)
+ */
+ public CertStoreException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ /**
+ * Creates a CertStoreException
that wraps the specified
+ * throwable. This allows any exception to be converted into a
+ * CertStoreException
, while retaining information about the
+ * cause, which may be useful for debugging. The detail message is
+ * set to (cause==null ? null : cause.toString()
) (which
+ * typically contains the class and detail message of cause).
+ *
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause getCause()} method). (A null
value is
+ * permitted, and indicates that the cause is nonexistent or unknown.)
+ */
+ public CertStoreException(Throwable cause)
+ {
+ this.cause = cause;
+ }
+
+ /**
+ * Returns the detail message for this CertStoreException
.
+ *
+ * @return the detail message, or null
if neither the message
+ * nor cause were specified
+ */
+ public String getMessage()
+ {
+ String message = super.getMessage();
+
+ if ( message == null && cause == null )
+ return null;
+
+ StringBuffer s = new StringBuffer();
+ if ( message != null )
+ {
+ s.append(message).append('\n');
+ }
+ if ( cause != null )
+ {
+ s.append("Cause:\n").append(cause.getMessage());
+ }
+ return s.toString();
+ }
+
+ /**
+ * Returns the cause of this CertStoreException
or
+ * null
if the cause is nonexistent or unknown.
+ *
+ * @return the cause of this throwable or null
if the cause
+ * is nonexistent or unknown.
+ */
+ public Throwable getCause()
+ {
+ return cause;
+ }
+
+ /**
+ * Returns a string describing this exception, including a description
+ * of the internal (wrapped) cause if there is one.
+ *
+ * @return a string representation of this
+ * CertStoreException
+ */
+ public String toString()
+ {
+ String message = getMessage();
+ if ( message == null )
+ return "";
+
+ return message;
+ }
+
+ /**
+ * Prints a stack trace to System.err
, including the backtrace
+ * of the cause, if any.
+ */
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Prints a stack trace to a PrintStream
, including the
+ * backtrace of the cause, if any.
+ *
+ * @param ps the PrintStream
to use for output
+ */
+ public void printStackTrace(PrintStream ps) {
+ super.printStackTrace(ps);
+ if ( cause != null ) {
+ cause.printStackTrace(ps);
+ }
+ }
+
+ /**
+ * Prints a stack trace to a PrintWriter
, including the
+ * backtrace of the cause, if any.
+ *
+ * @param pw the PrintWriter
to use for output
+ */
+ public void printStackTrace(PrintWriter pw) {
+ if ( cause != null ) {
+ cause.printStackTrace(pw);
+ }
+ super.printStackTrace(pw);
+ if ( cause != null ) {
+ cause.printStackTrace(pw);
+ }
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreParameters.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreParameters.java
new file mode 100644
index 000000000..58a70b372
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreParameters.java
@@ -0,0 +1,52 @@
+package java.security.cert;
+
+/**
+ * A specification of CertStore
parameters.
+ *
+ * The purpose of this interface is to group (and provide type safety for)
+ * all CertStore
parameter specifications. All
+ * CertStore
parameter specifications must implement this
+ * interface.
+ *
+ * Typically, a CertStoreParameters
object is passed as a parameter
+ * to one of the {@link CertStore#getInstance CertStore.getInstance} methods.
+ * The getInstance
method returns a CertStore
that
+ * is used for retrieving Certificate
s and CRL
s. The
+ * CertStore
that is returned is initialized with the specified
+ * parameters. The type of parameters needed may vary between different types
+ * of CertStore
s.
+ *
+ * @see CertStore#getInstance
+ **/
+public interface CertStoreParameters extends Cloneable
+{
+ /**
+ * Makes a copy of this CertStoreParameters
.
+ *
+ * The precise meaning of "copy" may depend on the class of
+ * the CertStoreParameters
object. A typical implementation
+ * performs a "deep copy" of this object, but this is not an absolute
+ * requirement. Some implementations may perform a "shallow copy" of some
+ * or all of the fields of this object.
+ *
+ * Note that the CertStore.getInstance
methods make a copy
+ * of the specified CertStoreParameters
. A deep copy
+ * implementation of clone
is safer and more robust, as it
+ * prevents the caller from corrupting a shared CertStore
by
+ * subsequently modifying the contents of its initialization parameters.
+ * However, a shallow copy implementation of clone
is more
+ * appropriate for applications that need to hold a reference to a
+ * parameter contained in the CertStoreParameters
. For example,
+ * a shallow copy clone allows an application to release the resources of
+ * a particular CertStore
initialization parameter immediately,
+ * rather than waiting for the garbage collection mechanism. This should
+ * be done with the utmost care, since the CertStore
may still
+ * be in use by other threads.
+ *
+ * Each subclass should state the precise behavior of this method so
+ * that users and developers know what to expect.
+ *
+ * @return a copy of this CertStoreParameters
+ */
+ public Object clone();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreSpi.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreSpi.java
new file mode 100644
index 000000000..b92cf4aa5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertStoreSpi.java
@@ -0,0 +1,104 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.util.Collection;
+
+/**
+ * The Service Provider Interface (SPI)
+ * for the {@link CertStore CertStore} class. All CertStore
+ * implementations must include a class (the SPI class) that extends
+ * this class (CertStoreSpi
), provides a constructor with
+ * a single argument of type CertStoreParameters
, and implements
+ * all of its methods. In general, instances of this class should only be
+ * accessed through the CertStore
class.
+ * For details, see the Java Cryptography Architecture.
+ *
+ * Concurrent Access
+ *
+ * The public methods of all CertStoreSpi
objects must be
+ * thread-safe. That is, multiple threads may concurrently invoke these
+ * methods on a single CertStoreSpi
object (or more than one)
+ * with no ill effects. This allows a CertPathBuilder
to search
+ * for a CRL while simultaneously searching for further certificates, for
+ * instance.
+ *
+ * Simple CertStoreSpi
implementations will probably ensure
+ * thread safety by adding a synchronized
keyword to their
+ * engineGetCertificates
and engineGetCRLs
methods.
+ * More sophisticated ones may allow truly concurrent access.
+ **/
+public abstract class CertStoreSpi
+ extends Object
+{
+
+ /**
+ * The sole constructor.
+ *
+ * @param params the initialization parameters (may be null
)
+ * @exception InvalidAlgorithmParameterException if the initialization
+ * parameters are inappropriate for this CertStoreSpi
+ */
+ public CertStoreSpi( CertStoreParameters params )
+ throws InvalidAlgorithmParameterException {}
+
+ /**
+ * Returns a Collection
of Certificate
s that
+ * match the specified selector. If no Certificate
s
+ * match the selector, an empty Collection
will be returned.
+ *
+ * For some CertStore
types, the resulting
+ * Collection
may not contain all of the
+ * Certificate
s that match the selector. For instance,
+ * an LDAP CertStore
may not search all entries in the
+ * directory. Instead, it may just search entries that are likely to
+ * contain the Certificate
s it is looking for.
+ *
+ * Some CertStore
implementations (especially LDAP
+ * CertStore
s) may throw a CertStoreException
+ * unless a non-null CertSelector
is provided that includes
+ * specific criteria that can be used to find the certificates. Issuer
+ * and/or subject names are especially useful criteria.
+ *
+ * @param selector A CertSelector
used to select which
+ * Certificate
s should be returned. Specify null
+ * to return all Certificate
s (if supported).
+ *
+ * @return A Collection
of Certificate
s that
+ * match the specified selector (never null
)
+ *
+ * @exception CertStoreException if an exception occurs
+ */
+ public abstract Collection engineGetCertificates( CertSelector selector )
+ throws CertStoreException;
+
+ /**
+ * Returns a Collection
of CRL
s that
+ * match the specified selector. If no CRL
s
+ * match the selector, an empty Collection
will be returned.
+ *
+ * For some CertStore
types, the resulting
+ * Collection
may not contain all of the
+ * CRL
s that match the selector. For instance,
+ * an LDAP CertStore
may not search all entries in the
+ * directory. Instead, it may just search entries that are likely to
+ * contain the CRL
s it is looking for.
+ *
+ * Some CertStore
implementations (especially LDAP
+ * CertStore
s) may throw a CertStoreException
+ * unless a non-null CRLSelector
is provided that includes
+ * specific criteria that can be used to find the CRLs. Issuer names
+ * and/or the certificate to be checked are especially useful.
+ *
+ * @param selector A CRLSelector
used to select which
+ * CRL
s should be returned. Specify null
+ * to return all CRL
s (if supported).
+ *
+ * @return A Collection
of CRL
s that
+ * match the specified selector (never null
)
+ *
+ * @exception CertStoreException if an exception occurs
+ */
+ public abstract Collection engineGetCRLs( CRLSelector selector )
+ throws CertStoreException;
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertUtil.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertUtil.java
new file mode 100644
index 000000000..216a8d8e4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertUtil.java
@@ -0,0 +1,556 @@
+package java.security.cert;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.OIDTokenizer;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.util.Strings;
+
+class CertUtil
+{
+ static class Implementation
+ {
+ Object engine;
+ Provider provider;
+
+ Implementation(
+ Object engine,
+ Provider provider)
+ {
+ this.engine = engine;
+ this.provider = provider;
+ }
+
+ Object getEngine()
+ {
+ return engine;
+ }
+
+ Provider getProvider()
+ {
+ return provider;
+ }
+ }
+
+ /**
+ * see if we can find an algorithm (or its alias and what it represents) in
+ * the property table for the given provider.
+ *
+ * @return null if no algorithm found, an Implementation if it is.
+ */
+ static Implementation getImplementation(
+ String baseName,
+ String algorithm,
+ Provider prov)
+ {
+ if (prov == null)
+ {
+ Provider[] provider = Security.getProviders();
+
+ //
+ // search every provider looking for the algorithm we want.
+ //
+ for (int i = 0; i != provider.length; i++)
+ {
+ Implementation imp = getImplementation(baseName, algorithm, provider[i]);
+ if (imp != null)
+ {
+ return imp;
+ }
+ }
+
+ return null;
+ }
+
+ String alias;
+
+ while ((alias = prov.getProperty("Alg.Alias." + baseName + "." + algorithm)) != null)
+ {
+ algorithm = alias;
+ }
+
+ String className = prov.getProperty(baseName + "." + algorithm);
+
+ if (className != null)
+ {
+ try
+ {
+ return new Implementation(Class.forName(className).newInstance(), prov);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IllegalStateException(
+ "algorithm " + algorithm + " in provider " + prov.getName() + " but no class found!");
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(
+ "algorithm " + algorithm + " in provider " + prov.getName() + " but class inaccessible: " + e.toString());
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * return an implementation for a given algorithm/provider.
+ * If the provider is null, we grab the first avalaible who has the required algorithm.
+ *
+ * @return null if no algorithm found, an Implementation if it is.
+ * @exception NoSuchProviderException if a provider is specified and not found.
+ */
+ static Implementation getImplementation(
+ String baseName,
+ String algorithm,
+ String provider)
+ throws NoSuchProviderException
+ {
+ if (provider == null)
+ {
+ Provider[] prov = Security.getProviders();
+
+ //
+ // search every provider looking for the algorithm we want.
+ //
+ for (int i = 0; i != prov.length; i++)
+ {
+ Implementation imp = getImplementation(baseName, algorithm, prov[i]);
+ if (imp != null)
+ {
+ return imp;
+ }
+ }
+ }
+ else
+ {
+ Provider prov = Security.getProvider(provider);
+
+ if (prov == null)
+ {
+ throw new NoSuchProviderException("Provider " + provider + " not found");
+ }
+
+ return getImplementation(baseName, algorithm, prov);
+ }
+
+ return null;
+ }
+
+ /**
+ * see if we can find an algorithm (or its alias and what it represents) in
+ * the property table for the given provider.
+ *
+ * @return null if no algorithm found, an Implementation if it is.
+ */
+ static Implementation getImplementation(String baseName, String algorithm,
+ Provider prov, Class[] ctorparamtype, Object[] ctorparam)
+ throws InvalidAlgorithmParameterException
+ {
+ String alias;
+
+ while ((alias = prov.getProperty("Alg.Alias." + baseName + "."
+ + algorithm)) != null)
+ {
+ algorithm = alias;
+ }
+
+ String className = prov.getProperty(baseName + "." + algorithm);
+
+ if (className != null)
+ {
+ try
+ {
+ return new Implementation(Class.forName(className)
+ .getConstructor(ctorparamtype).newInstance(ctorparam),
+ prov);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IllegalStateException("algorithm " + algorithm
+ + " in provider " + prov.getName()
+ + " but no class found!");
+ }
+ catch (Exception e)
+ {
+ if (e instanceof InvalidAlgorithmParameterException)
+ {
+ throw (InvalidAlgorithmParameterException)e;
+ }
+
+ throw new IllegalStateException("algorithm " + algorithm
+ + " in provider " + prov.getName()
+ + " but class inaccessible!");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * return an implementation for a given algorithm/provider. If the provider
+ * is null, we grab the first avalaible who has the required algorithm.
+ *
+ * @return null if no algorithm found, an Implementation if it is.
+ *
+ * @exception NoSuchProviderException
+ * if a provider is specified and not found.
+ */
+ static Implementation getImplementation(String baseName, String algorithm,
+ String provider, Class[] ctorparamtype, Object[] ctorparam)
+ throws NoSuchProviderException, InvalidAlgorithmParameterException
+ {
+ if (provider == null)
+ {
+ Provider[] prov = Security.getProviders();
+
+ //
+ // search every provider looking for the algorithm we want.
+ //
+ for (int i = 0; i != prov.length; i++)
+ {
+ Implementation imp = getImplementation(baseName, algorithm,
+ prov[i], ctorparamtype, ctorparam);
+ if (imp != null)
+ {
+ return imp;
+ }
+ }
+ }
+ else
+ {
+ Provider prov = Security.getProvider(provider);
+
+ if (prov == null)
+ {
+ throw new NoSuchProviderException("Provider " + provider
+ + " not found");
+ }
+
+ return getImplementation(baseName, algorithm, prov, ctorparamtype,
+ ctorparam);
+ }
+
+ return null;
+ }
+
+ static byte[] parseGeneralName(int type, String data) throws IOException
+ {
+ byte[] encoded = null;
+
+ switch (type)
+ {
+ case 0:
+ throw new IOException(
+ "unable to parse OtherName String representation");
+ case 1:
+ encoded = parseRfc822(data.trim());
+ break;
+ case 2:
+ encoded = parseDNSName(data.trim());
+ break;
+ case 3:
+ throw new IOException(
+ "unable to parse ORAddress String representation");
+ case 4:
+ encoded = parseX509Name(data.trim());
+ break;
+ case 5:
+ throw new IOException(
+ "unable to parse EDIPartyName String representation");
+ case 6:
+ encoded = parseURI(data.trim());
+ break;
+ case 7:
+ encoded = parseIP(data.trim());
+ break;
+ case 8:
+ encoded = parseOID(data.trim());
+ break;
+ default:
+ throw new IOException(
+ "unable to parse unkown type String representation");
+ }
+ return encoded;
+ }
+
+ /**
+ * Check the format of an OID.
+ * Throw an IOException if the first component is not 0, 1 or 2 or the
+ * second component is greater than 39.
+ *
+ * User {@link org.spongycastle.asn1.OIDTokenizer OIDTokenizer}
+ *
+ * @param the
+ * OID to be checked.
+ *
+ * @exception IOException
+ * if the first component is not 0, 1 or 2 or the second
+ * component is greater than 39.
+ */
+ static byte[] parseOID(String oid) throws IOException
+ {
+ OIDTokenizer tokenizer = new OIDTokenizer(oid);
+ String token;
+ if (!tokenizer.hasMoreTokens())
+ {
+ throw new IOException("OID contains no tokens");
+ }
+ token = tokenizer.nextToken();
+ if (token == null)
+ {
+ throw new IOException("OID contains no tokens");
+ }
+ try
+ {
+ int test = (Integer.valueOf(token)).intValue();
+ if (test < 0 || test > 2)
+ {
+ throw new IOException("first token is not >= 0 and <=2");
+ }
+ if (!tokenizer.hasMoreTokens())
+ {
+ throw new IOException("OID contains only one token");
+ }
+ token = tokenizer.nextToken();
+ if (token == null)
+ {
+ throw new IOException("OID contains only one token");
+ }
+ test = (Integer.valueOf(token)).intValue();
+ if (test < 0 || test > 39)
+ {
+ throw new IOException("secon token is not >= 0 and <=39");
+ }
+ }
+ catch (NumberFormatException ex)
+ {
+ throw new IOException("token: " + token + ": " + ex.toString());
+ }
+ ASN1Object derData = new ASN1ObjectIdentifier(oid);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+ derOutStream.writeObject(derData);
+ derOutStream.close();
+ return outStream.toByteArray();
+ }
+
+ /**
+ * Parse the given IPv4 or IPv6 into DER encoded byte array representation.
+ *
+ * @param the
+ * IP in well known String format
+ *
+ * @return the IP as byte array
+ *
+ * @exception IOException
+ * if the String could not be parsed
+ */
+ private static byte[] parseIP(String data) throws IOException
+ {
+ byte[] encoded = parseIPv4(data);
+
+ if (encoded == null)
+ {
+ encoded = parseIPv6(data);
+ }
+
+ if (encoded == null)
+ {
+ throw new IOException(
+ "unable to parse IP to DER encoded byte array");
+ }
+
+ return encoded;
+ }
+
+ /**
+ * Parse the given IPv4 into DER encoded byte array representation.
+ *
+ * @param the
+ * IP in well known String format
+ *
+ * @return the IP as byte array or null
if not parseable
+ */
+ private static byte[] parseIPv4(String data)
+ {
+ if (data.length() == 0)
+ {
+ return null;
+ }
+
+ int octet;
+ int octets = 0;
+ byte[] dst = new byte[4];
+
+ int pos = 0;
+ int start = 0;
+ while (start < data.length()
+ && (pos = data.indexOf('.', start)) > start && pos - start > 3)
+ {
+ try
+ {
+ octet = (Integer.valueOf(data.substring(start, pos - start)))
+ .intValue();
+ }
+ catch (NumberFormatException ex)
+ {
+ return null;
+ }
+ if (octet < 0 || octet > 255)
+ {
+ return null;
+ }
+ dst[octets++] = (byte)(octet & 0xff);
+
+ start = pos + 1;
+ }
+
+ if (octets < 4)
+ {
+ return null;
+ }
+
+ return dst;
+ }
+
+ /**
+ * Parse the given IPv6 into DER encoded byte array representation.
+ *
+ * TODO: implement this
+ *
+ * @param the
+ * IP in well known String format
+ *
+ * @return the IP as byte array or null
if not parseable
+ */
+ private static byte[] parseIPv6(String data)
+ {
+ return null;
+ }
+
+ /**
+ * Parse the given URI into DER encoded byte array representation.
+ *
+ * @param the
+ * URI in well known String format
+ *
+ * @return the URI as byte array
+ *
+ * @exception IOException
+ * if the String could not be parsed
+ */
+ private static byte[] parseURI(String data) throws IOException
+ {
+ // TODO do parsing test
+ ASN1Object derData = new DERIA5String(data);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+ derOutStream.writeObject(derData);
+ derOutStream.close();
+ return outStream.toByteArray();
+ }
+
+ /**
+ * Parse the given rfc822 addr-spec into DER encoded byte array
+ * representation.
+ *
+ * @param the
+ * rfc822 addr-spec in well known String format
+ *
+ * @return the rfc822 addr-spec as byte array
+ *
+ * @exception IOException
+ * if the String could not be parsed
+ */
+ private static byte[] parseRfc822(String data) throws IOException
+ {
+ int tmpInt = data.indexOf('@');
+ if (tmpInt < 0 || tmpInt >= data.length() - 1)
+ {
+ throw new IOException("wrong format of rfc822Name:" + data);
+ }
+ // TODO more test for illegal charateers
+ ASN1Object derData = new DERIA5String(data);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+ derOutStream.writeObject(derData);
+ derOutStream.close();
+ return outStream.toByteArray();
+ }
+
+ /**
+ * Parse the given DNS name into DER encoded byte array representation. The
+ * String must be in den preffered name syntax as defined in RFC 1034.
+ *
+ * @param the
+ * DNS name in well known String format
+ *
+ * @return the DNS name as byte array
+ *
+ * @exception IOException
+ * if the String could not be parsed
+ */
+ private static byte[] parseDNSName(String data) throws IOException
+ {
+ // TODO more test for illegal charateers
+ ASN1Object derData = new DERIA5String(data);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+ derOutStream.writeObject(derData);
+ derOutStream.close();
+ return outStream.toByteArray();
+ }
+
+ /**
+ * Parse the given X.509 name into DER encoded byte array representation.
+ *
+ * @param the
+ * X.509 name in well known String format
+ *
+ * @return the X.509 name as byte array
+ *
+ * @exception IOException
+ * if the String could not be parsed
+ */
+ private static byte[] parseX509Name(String data) throws IOException
+ {
+ // TODO more test for illegal charateers
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+ derOutStream.writeObject(new X509Name(trimX509Name(data)));
+ derOutStream.close();
+ return outStream.toByteArray();
+ }
+
+ /**
+ * Returns the given name converted to upper case and all multi spaces squezed
+ * to one space.
+ **/
+ static String trimX509Name(String name)
+ {
+ String data = Strings.toUpperCase(name.trim());
+ int pos;
+ while ((pos = data.indexOf(" ")) >= 0)
+ {
+ data = data.substring(0, pos) + data.substring(pos + 1);
+ }
+ while ((pos = data.indexOf(" =")) >= 0)
+ {
+ data = data.substring(0, pos) + data.substring(pos + 1);
+ }
+ while ((pos = data.indexOf("= ")) >= 0)
+ {
+ data = data.substring(0, pos + 1) + data.substring(pos + 2);
+ }
+ return data;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/Certificate.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/Certificate.java
new file mode 100644
index 000000000..201e209a3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/Certificate.java
@@ -0,0 +1,80 @@
+
+package java.security.cert;
+
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+
+public abstract class Certificate extends Object
+{
+ private String type;
+
+ protected Certificate(String type)
+ {
+ this.type = type;
+ }
+
+ public boolean equals(Object other)
+ {
+ if ( !(other instanceof Certificate) )
+ return false;
+
+ if ( other == this )
+ return true;
+
+ try
+ {
+ byte[] enc1 = getEncoded();
+ byte[] enc2 = ((Certificate)other).getEncoded();
+
+ return MessageDigest.isEqual(enc1, enc2);
+ }
+ catch (CertificateEncodingException e)
+ {
+ return false;
+ }
+ }
+
+ public final String getType()
+ {
+ return type;
+ }
+
+ // XXX
+ public int hashCode()
+ {
+ try
+ {
+ byte[] enc1 = getEncoded();
+ int hc = 0;
+ for (int i = 0; i < enc1.length; i++)
+ {
+ hc += enc1[i];
+ }
+
+ return hc;
+ }
+ catch (CertificateEncodingException e)
+ {
+ return 0;
+ }
+ }
+
+ public abstract byte[] getEncoded()
+ throws CertificateEncodingException;
+
+ public abstract PublicKey getPublicKey();
+
+ public abstract String toString();
+
+ public abstract void verify(PublicKey key)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException;
+
+ public abstract void verify(PublicKey key, String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException;
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateEncodingException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateEncodingException.java
new file mode 100644
index 000000000..47545a5c0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateEncodingException.java
@@ -0,0 +1,14 @@
+
+package java.security.cert;
+
+public class CertificateEncodingException extends CertificateException
+{
+ public CertificateEncodingException()
+ {
+ }
+
+ public CertificateEncodingException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateException.java
new file mode 100644
index 000000000..644c6249f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateException.java
@@ -0,0 +1,16 @@
+
+package java.security.cert;
+
+import java.security.GeneralSecurityException;
+
+public class CertificateException extends GeneralSecurityException
+{
+ public CertificateException()
+ {
+ }
+
+ public CertificateException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateExpiredException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateExpiredException.java
new file mode 100644
index 000000000..1a9062aa2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateExpiredException.java
@@ -0,0 +1,14 @@
+
+package java.security.cert;
+
+public class CertificateExpiredException extends CertificateException
+{
+ public CertificateExpiredException()
+ {
+ }
+
+ public CertificateExpiredException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateFactory.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateFactory.java
new file mode 100644
index 000000000..e86cd3a03
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateFactory.java
@@ -0,0 +1,183 @@
+
+package java.security.cert;
+
+import java.io.InputStream;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Uses {@link CertUtil CertUtil} to actualiy load the SPI classes.
+ *
+ * @see CertUtil
+ **/
+public class CertificateFactory
+{
+ private CertificateFactorySpi certFacSpi;
+ private Provider provider;
+ private String type;
+
+ protected CertificateFactory(
+ CertificateFactorySpi certFacSpi,
+ Provider provider,
+ String type)
+ {
+ this.certFacSpi = certFacSpi;
+ this.provider = provider;
+ this.type = type;
+ }
+
+ public final CRL generateCRL(InputStream inStream)
+ throws CRLException
+ {
+ return certFacSpi.engineGenerateCRL(inStream);
+ }
+
+ public final Collection generateCRLs(InputStream inStream)
+ throws CRLException
+ {
+ return certFacSpi.engineGenerateCRLs(inStream);
+ }
+
+ public final Certificate generateCertificate(InputStream inStream)
+ throws CertificateException
+ {
+ return certFacSpi.engineGenerateCertificate(inStream);
+ }
+
+ public final /*Sk13 Vector*/ Collection generateCertificates(InputStream inStream)
+ throws CertificateException
+ {
+ return certFacSpi.engineGenerateCertificates(inStream);
+ }
+
+ /**
+ * Returns an iteration of the CertPath
encodings supported
+ * by this certificate factory, with the default encoding first. See
+ * Appendix A in the
+ * Java Certification Path API Programmer's Guide for information about
+ * standard encoding names and their formats.
+ *
+ * Attempts to modify the returned Iterator
via its
+ * remove
method result in an
+ * UnsupportedOperationException
.
+ *
+ * @return an Iterator
over the names of the supported
+ * CertPath
encodings (as String
s)
+ */
+ public final Iterator getCertPathEncodings()
+ {
+ return certFacSpi.engineGetCertPathEncodings();
+ }
+
+ /**
+ * Generates a CertPath
object and initializes it with
+ * the data read from the InputStream
inStream. The data
+ * is assumed to be in the default encoding. The name of the default
+ * encoding is the first element of the Iterator
returned by
+ * the {@link #getCertPathEncodings getCertPathEncodings} method.
+ *
+ * @param inStream an InputStream
containing the data
+ *
+ * @return a CertPath
initialized with the data from the
+ * InputStream
+ *
+ * @exception CertificateException if an exception occurs while decoding
+ */
+ public final CertPath generateCertPath(InputStream inStream)
+ throws CertificateException
+ {
+ return certFacSpi.engineGenerateCertPath(inStream);
+ }
+
+ /**
+ * Generates a CertPath
object and initializes it with
+ * the data read from the InputStream
inStream. The data
+ * is assumed to be in the specified encoding. See Appendix A in the
+ *
+ * Java Certification Path API Programmer's Guide
+ * for information about standard encoding names and their formats.
+ *
+ * @param inStream an InputStream
containing the data
+ * @param encoding the encoding used for the data
+ *
+ * @return a CertPath
initialized with the data from the
+ * InputStream
+ *
+ * @exception CertificateException if an exception occurs while decoding or
+ * the encoding requested is not supported
+ */
+ public final CertPath generateCertPath(InputStream inStream, String encoding)
+ throws CertificateException
+ {
+ return certFacSpi.engineGenerateCertPath(inStream, encoding);
+ }
+
+ /**
+ * Generates a CertPath
object and initializes it with
+ * a List
of Certificate
s.
+ *
+ * The certificates supplied must be of a type supported by the
+ * CertificateFactory
. They will be copied out of the supplied
+ * List
object.
+ *
+ * @param certificates a List
of Certificate
s
+ *
+ * @return a CertPath
initialized with the supplied list of
+ * certificates
+ *
+ * @exception CertificateException if an exception occurs
+ */
+ public final CertPath generateCertPath(List certificates)
+ throws CertificateException
+ {
+ return certFacSpi.engineGenerateCertPath( certificates );
+ }
+
+ public static final CertificateFactory getInstance(String type)
+ throws CertificateException
+ {
+ try
+ {
+ CertUtil.Implementation imp = CertUtil.getImplementation("CertificateFactory", type, (String)null);
+
+ if (imp != null)
+ {
+ return new CertificateFactory((CertificateFactorySpi)imp.getEngine(), imp.getProvider(), type);
+ }
+
+ throw new CertificateException("can't find type " + type);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new CertificateException(type + " not found");
+ }
+ }
+
+ public static final CertificateFactory getInstance(
+ String type,
+ String provider)
+ throws CertificateException, NoSuchProviderException
+ {
+ CertUtil.Implementation imp = CertUtil.getImplementation("CertificateFactory", type, provider);
+
+ if (imp != null)
+ {
+ return new CertificateFactory((CertificateFactorySpi)imp.getEngine(), imp.getProvider(), type);
+ }
+
+ throw new CertificateException("can't find type " + type);
+ }
+
+ public final Provider getProvider()
+ {
+ return provider;
+ }
+
+ public final String getType()
+ {
+ return type;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateFactorySpi.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateFactorySpi.java
new file mode 100644
index 000000000..8cc06fc2e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateFactorySpi.java
@@ -0,0 +1,111 @@
+
+package java.security.cert;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class CertificateFactorySpi
+{
+ public CertificateFactorySpi()
+ {
+ }
+
+ public abstract CRL engineGenerateCRL(InputStream inStream)
+ throws CRLException;
+
+ public abstract Collection engineGenerateCRLs(InputStream inStream)
+ throws CRLException;
+
+ public abstract Certificate engineGenerateCertificate(InputStream inStream)
+ throws CertificateException;
+
+ public abstract /*SK13 Vector*/ Collection engineGenerateCertificates(InputStream inStream)
+ throws CertificateException;
+
+ /**
+ * Returns an iteration of the CertPath
encodings supported
+ * by this certificate factory, with the default encoding first. See
+ * Appendix A in the
+ * Java Certification Path API Programmer's Guide
+ * for information about standard encoding names.
+ *
+ * Attempts to modify the returned Iterator
via its
+ * remove
method result in an
+ * UnsupportedOperationException
.
+ *
+ * This method was added to version 1.4 of the Java 2 Platform
+ * Standard Edition. In order to maintain backwards compatibility with
+ * existing service providers, this method cannot be abstract
+ * and by default throws an UnsupportedOperationException
.
+ *
+ * @return an Iterator
over the names of the supported
+ * CertPath
encodings (as String
s)
+ *
+ * @exception UnsupportedOperationException if the method is not supported
+ */
+ public abstract Iterator engineGetCertPathEncodings();
+
+ /**
+ * Generates a CertPath
object and initializes it with
+ * the data read from the InputStream
inStream. The data
+ * is assumed to be in the default encoding.
+ *
+ * @param inStream an InputStream
containing the data
+ *
+ * @return a CertPath
initialized with the data from the
+ * InputStream
+ *
+ * @exception CertificateException if an exception occurs while decoding
+ */
+ public abstract CertPath engineGenerateCertPath(InputStream inStream)
+ throws CertificateException;
+
+ /**
+ * Generates a CertPath
object and initializes it with
+ * the data read from the InputStream
inStream. The data
+ * is assumed to be in the specified encoding.
+ *
+ * This method was added to version 1.4 of the Java 2 Platform
+ * Standard Edition. In order to maintain backwards compatibility with
+ * existing service providers, this method cannot be abstract
+ * and by default throws an UnsupportedOperationException
.
+ *
+ * @param inStream an InputStream
containing the data
+ * @param encoding the encoding used for the data
+ *
+ * @return a CertPath
initialized with the data from the
+ * InputStream
+ *
+ * @exception CertificateException if an exception occurs while decoding or
+ * the encoding requested is not supported
+ * @exception UnsupportedOperationException if the method is not supported
+ */
+ public abstract CertPath engineGenerateCertPath(InputStream inStream, String encoding)
+ throws CertificateException;
+
+ /**
+ * Generates a CertPath
object and initializes it with
+ * a List
of Certificate
s.
+ *
+ * The certificates supplied must be of a type supported by the
+ * CertificateFactory
. They will be copied out of the supplied
+ * List
object.
+ *
+ * This method was added to version 1.4 of the Java 2 Platform
+ * Standard Edition. In order to maintain backwards compatibility with
+ * existing service providers, this method cannot be abstract
+ * and by default throws an UnsupportedOperationException
.
+ *
+ * @param certificates a List
of Certificate
s
+ *
+ * @return a CertPath
initialized with the supplied list of
+ * certificates
+ *
+ * @exception CertificateException if an exception occurs
+ * @exception UnsupportedOperationException if the method is not supported
+ */
+ public abstract CertPath engineGenerateCertPath(List certificates)
+ throws CertificateException;
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateNotYetValidException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateNotYetValidException.java
new file mode 100644
index 000000000..ec8d46a3e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateNotYetValidException.java
@@ -0,0 +1,14 @@
+
+package java.security.cert;
+
+public class CertificateNotYetValidException extends CertificateException
+{
+ public CertificateNotYetValidException()
+ {
+ }
+
+ public CertificateNotYetValidException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateParsingException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateParsingException.java
new file mode 100644
index 000000000..a9f18aae0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CertificateParsingException.java
@@ -0,0 +1,14 @@
+
+package java.security.cert;
+
+public class CertificateParsingException extends CertificateException
+{
+ public CertificateParsingException()
+ {
+ }
+
+ public CertificateParsingException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CollectionCertStoreParameters.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CollectionCertStoreParameters.java
new file mode 100644
index 000000000..7c31e7b51
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/CollectionCertStoreParameters.java
@@ -0,0 +1,117 @@
+package java.security.cert;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Parameters used as input for the Collection CertStore
+ * algorithm.
+ *
+ * This class is used to provide necessary configuration parameters
+ * to implementations of the Collection CertStore
+ * algorithm. The only parameter included in this class is the
+ * Collection
from which the CertStore
will
+ * retrieve certificates and CRLs.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see java.util.Collection
+ * @see CertStore
+ **/
+public class CollectionCertStoreParameters implements CertStoreParameters
+{
+ private Collection collection;
+
+ /**
+ * Creates an instance of CollectionCertStoreParameters
+ * which will allow certificates and CRLs to be retrieved from the
+ * specified Collection
. If the specified
+ * Collection
contains an object that is not a
+ * Certificate
or CRL
, that object will be
+ * ignored by the Collection CertStore
.
+ *
+ * The Collection
is not copied. Instead, a
+ * reference is used. This allows the caller to subsequently add or
+ * remove Certificates
or CRL
s from the
+ * Collection
, thus changing the set of
+ * Certificates
or CRL
s available to the
+ * Collection CertStore
. The Collection CertStore
+ * will not modify the contents of the Collection
.
+ *
+ * If the Collection
will be modified by one thread while
+ * another thread is calling a method of a Collection CertStore
+ * that has been initialized with this Collection
, the
+ * Collection
must have fail-fast iterators.
+ *
+ * @param collection a Collection
of
+ * Certificate
s and CRL
s
+ *
+ * @exception NullPointerException if collection
is
+ * null
+ */
+ public CollectionCertStoreParameters(Collection collection)
+ {
+ if ( collection == null )
+ throw new NullPointerException("collection must be non-null");
+ this.collection = collection;
+ }
+
+ /**
+ * Creates an instance of CollectionCertStoreParameters
with
+ * the an empty Collection.
+ */
+ public CollectionCertStoreParameters()
+ {
+ collection = new ArrayList();
+ }
+
+ /**
+ * Returns the Collection
from which Certificate
s
+ * and CRL
s are retrieved. This is not a copy of the
+ * Collection
, it is a reference. This allows the caller to
+ * subsequently add or remove Certificates
or
+ * CRL
s from the Collection
.
+ *
+ * @return the Collection
(never null)
+ */
+ public Collection getCollection()
+ {
+ return collection;
+ }
+
+ /**
+ * Returns a copy of this object. Note that only a reference to the
+ * Collection
is copied, and not the contents.
+ *
+ * @return the copy
+ */
+ public Object clone()
+ {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ /* Cannot happen */
+ throw new InternalError(e.toString());
+ }
+ }
+
+ /**
+ * Returns a formatted string describing the parameters.
+ *
+ * @return a formatted string describing the parameters
+ */
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append("CollectionCertStoreParameters: [\n collections:\n");
+ s.append( getCollection());
+ s.append("\n]" );
+ return s.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/LDAPCertStoreParameters.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/LDAPCertStoreParameters.java
new file mode 100644
index 000000000..2e4669975
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/LDAPCertStoreParameters.java
@@ -0,0 +1,130 @@
+package java.security.cert;
+
+/**
+ * Parameters used as input for the LDAP CertStore
algorithm.
+ *
+ * This class is used to provide necessary configuration parameters (server
+ * name and port number) to implementations of the LDAP CertStore
+ * algorithm.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertStore
+ **/
+public class LDAPCertStoreParameters implements CertStoreParameters
+{
+ private static final int LDAP_DEFAULT_PORT = 389;
+
+ /**
+ * the port number of the LDAP server
+ */
+ private String serverName;
+
+ /**
+ * the DNS name of the LDAP server
+ */
+ private int port;
+
+ /**
+ * Creates an instance of LDAPCertStoreParameters
with the
+ * default parameter values (server name "localhost", port 389).
+ */
+ public LDAPCertStoreParameters()
+ {
+ this("localhost", LDAP_DEFAULT_PORT);
+ }
+
+ /**
+ * Creates an instance of LDAPCertStoreParameters
with the
+ * specified server name and a default port of 389.
+ *
+ * @param serverName the DNS name of the LDAP server
+ *
+ * @exception NullPointerException if serverName
is
+ * null
+ */
+ public LDAPCertStoreParameters(String serverName)
+ {
+ this(serverName, LDAP_DEFAULT_PORT);
+ }
+
+ /**
+ * Creates an instance of LDAPCertStoreParameters
with the
+ * specified parameter values.
+ *
+ * @param serverName the DNS name of the LDAP server
+ * @param port the port number of the LDAP server
+ *
+ * @exception NullPointerException if serverName
is
+ * null
+ */
+ public LDAPCertStoreParameters(String serverName, int port)
+ {
+ if (serverName == null)
+ throw new NullPointerException("serverName must be non-null");
+ this.serverName = serverName;
+ this.port = port;
+ }
+
+ /**
+ * Returns the DNS name of the LDAP server.
+ *
+ * @return the name (not null
)
+ */
+ public String getServerName()
+ {
+ return serverName;
+ }
+
+ /**
+ * Returns the port number of the LDAP server.
+ *
+ * @return the port number
+ */
+ public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Returns a copy of this object. Changes to the copy will not affect
+ * the original and vice versa.
+ *
+ * Note: this method currently performs a shallow copy of the object
+ * (simply calls Object.clone()
). This may be changed in a
+ * future revision to perform a deep copy if new parameters are added
+ * that should not be shared.
+ *
+ * @return the copy
+ */
+ public Object clone()
+ {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ /* Cannot happen */
+ throw new InternalError(e.toString());
+ }
+ }
+
+ /**
+ * Returns a formatted string describing the parameters.
+ *
+ * @return a formatted string describing the parameters
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("LDAPCertStoreParameters: [\n");
+ sb.append(" serverName: ").append(serverName).append('\n');
+ sb.append(" port: ").append(port).append('\n');
+ sb.append(']');
+ return sb.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXBuilderParameters.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXBuilderParameters.java
new file mode 100644
index 000000000..b4f7aceb9
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXBuilderParameters.java
@@ -0,0 +1,179 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.Set;
+
+/**
+ * Parameters used as input for the PKIX CertPathBuilder
+ * algorithm.
+ *
+ * A PKIX CertPathBuilder
uses these parameters to {@link
+ * CertPathBuilder#build build} a CertPath
which has been
+ * validated according to the PKIX certification path validation algorithm.
+ *
+ * To instantiate a PKIXBuilderParameters
object, an
+ * application must specify one or more most-trusted CAs as defined by
+ * the PKIX certification path validation algorithm. The most-trusted CA
+ * can be specified using one of two constructors. An application
+ * can call {@link #PKIXBuilderParameters(Set, CertSelector)
+ * PKIXBuilderParameters(Set, CertSelector)}, specifying a
+ * Set
of TrustAnchor
objects, each of which
+ * identifies a most-trusted CA. Alternatively, an application can call
+ * {@link #PKIXBuilderParameters(KeyStore, CertSelector)
+ * PKIXBuilderParameters(KeyStore, CertSelector)}, specifying a
+ * KeyStore
instance containing trusted certificate entries, each
+ * of which will be considered as a most-trusted CA.
+ *
+ * In addition, an application must specify constraints on the target
+ * certificate that the CertPathBuilder
will attempt
+ * to build a path to. The constraints are specified as a
+ * CertSelector
object. These constraints should provide the
+ * CertPathBuilder
with enough search criteria to find the target
+ * certificate. Minimal criteria for an X509Certificate
usually
+ * include the subject name and/or one or more subject alternative names.
+ * If enough criteria is not specified, the CertPathBuilder
+ * may throw a CertPathBuilderException
.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertPathBuilder
+ **/
+public class PKIXBuilderParameters extends PKIXParameters
+{
+ private int maxPathLength = 5;
+
+ /**
+ * Creates an instance of PKIXBuilderParameters
with
+ * the specified Set
of most-trusted CAs.
+ * Each element of the set is a {@link TrustAnchor TrustAnchor}.
+ *
+ * Note that the Set
is copied to protect against
+ * subsequent modifications.
+ *
+ * @param trustAnchors a Set
of TrustAnchor
s
+ * @param targetConstraints a CertSelector
specifying the
+ * constraints on the target certificate
+ *
+ * @exception InvalidAlgorithmParameterException if trustAnchors
+ * is empty (trustAnchors.isEmpty() == true)
+ * @exception NullPointerException if trustAnchors
is
+ * null
+ * @exception ClassCastException if any of the elements of
+ * trustAnchors
are not of type
+ * java.security.cert.TrustAnchor
+ */
+ public PKIXBuilderParameters(
+ Set trustAnchors,
+ CertSelector targetConstraints)
+ throws InvalidAlgorithmParameterException
+ {
+ super( trustAnchors );
+ setTargetCertConstraints( targetConstraints );
+ }
+
+ /**
+ * Creates an instance of PKIXBuilderParameters
that
+ * populates the set of most-trusted CAs from the trusted
+ * certificate entries contained in the specified KeyStore
.
+ * Only keystore entries that contain trusted X509Certificate
s
+ * are considered; all other certificate types are ignored.
+ *
+ * @param keystore a KeyStore
from which the set of
+ * most-trusted CAs will be populated
+ * @param targetConstraints a CertSelector
specifying the
+ * constraints on the target certificate
+ *
+ * @exception KeyStoreException if keystore
has not been
+ * initialized
+ * @exception InvalidAlgorithmParameterException if keystore
does
+ * not contain at least one trusted certificate entry
+ * @exception NullPointerException if keystore
is
+ * null
+ */
+ public PKIXBuilderParameters(KeyStore keystore,
+ CertSelector targetConstraints)
+ throws KeyStoreException,
+ InvalidAlgorithmParameterException
+ {
+ super( keystore );
+ setTargetCertConstraints( targetConstraints );
+ }
+
+ /**
+ * Sets the value of the maximum number of non-self-issued intermediate
+ * certificates that may exist in a certification path. A certificate
+ * is self-issued if the DNs that appear in the subject and issuer
+ * fields are identical and are not empty. Note that the last certificate
+ * in a certification path is not an intermediate certificate, and is not
+ * included in this limit. Usually the last certificate is an end entity
+ * certificate, but it can be a CA certificate. A PKIX
+ * CertPathBuilder
instance must not build
+ * paths longer than the length specified.
+ *
+ * A value of 0 implies that the path can only contain
+ * a single certificate. A value of -1 implies that the
+ * path length is unconstrained (i.e. there is no maximum).
+ * The default maximum path length, if not specified, is 5.
+ * Setting a value less than -1 will cause an exception to be thrown.
+ *
+ * If any of the CA certificates contain the
+ * BasicConstraintsExtension
, the value of the
+ * pathLenConstraint
field of the extension overrides
+ * the maximum path length parameter whenever the result is a
+ * certification path of smaller length.
+ *
+ * @param maxPathLength the maximum number of non-self-issued intermediate
+ * certificates that may exist in a certification path
+ *
+ * @exception InvalidParameterException if maxPathLength
is set
+ * to a value less than -1
+ *
+ * @see #getMaxPathLength
+ */
+ public void setMaxPathLength(int maxPathLength)
+ {
+ if ( maxPathLength < -1 )
+ throw new InvalidParameterException("the maximum path length parameter can not be less than -1");
+ this.maxPathLength = maxPathLength;
+ }
+
+ /**
+ * Returns the value of the maximum number of intermediate non-self-issued
+ * certificates that may exist in a certification path. See
+ * the {@link #setMaxPathLength} method for more details.
+ *
+ * @return the maximum number of non-self-issued intermediate certificates
+ * that may exist in a certification path, or -1 if there is no limit
+ *
+ * @see #setMaxPathLength
+ */
+ public int getMaxPathLength()
+ {
+ return maxPathLength;
+ }
+
+ /**
+ * Returns a formatted string describing the parameters.
+ *
+ * @return a formatted string describing the parameters
+ */
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append( "PKIXBuilderParameters [\n" );
+ s.append( super.toString() );
+ s.append( " Maximum Path Length: " );
+ s.append( getMaxPathLength() );
+ s.append( "\n]\n" );
+ return s.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathBuilderResult.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathBuilderResult.java
new file mode 100644
index 000000000..2ac791826
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathBuilderResult.java
@@ -0,0 +1,93 @@
+package java.security.cert;
+
+import java.security.PublicKey;
+
+/**
+ * This class represents the successful result of the PKIX certification
+ * path builder algorithm. All certification paths that are built and
+ * returned using this algorithm are also validated according to the PKIX
+ * certification path validation algorithm.
+ *
+ * Instances of PKIXCertPathBuilderResult
are returned by
+ * the build
method of CertPathBuilder
+ * objects implementing the PKIX algorithm.
+ *
+ * All PKIXCertPathBuilderResult
objects contain the
+ * certification path constructed by the build algorithm, the
+ * valid policy tree and subject public key resulting from the build
+ * algorithm, and a TrustAnchor
describing the certification
+ * authority (CA) that served as a trust anchor for the certification path.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertPathBuilderResult
+ *
+ **/
+public class PKIXCertPathBuilderResult extends PKIXCertPathValidatorResult
+ implements CertPathBuilderResult
+{
+ private CertPath certPath;
+
+ /**
+ * Creates an instance of PKIXCertPathBuilderResult
+ * containing the specified parameters.
+ *
+ * @param certPath the validated CertPath
+ * @param trustAnchor a TrustAnchor
describing the CA that
+ * served as a trust anchor for the certification path
+ * @param policyTree the immutable valid policy tree, or null
+ * if there are no valid policies
+ * @param subjectPublicKey the public key of the subject
+ *
+ * @exception NullPointerException if the certPath
,
+ * trustAnchor
or subjectPublicKey
parameters
+ * are null
+ */
+ public PKIXCertPathBuilderResult(CertPath certPath, TrustAnchor trustAnchor,
+ PolicyNode policyTree, PublicKey subjectPublicKey)
+ {
+ super(trustAnchor, policyTree, subjectPublicKey);
+ if ( certPath == null )
+ throw new NullPointerException( "certPath must be non-null" );
+ this.certPath = certPath;
+ }
+
+ /**
+ * Returns the built and validated certification path. The
+ * CertPath
object does not include the trust anchor.
+ * Instead, use the {@link #getTrustAnchor() getTrustAnchor()} method to
+ * obtain the TrustAnchor
that served as the trust anchor
+ * for the certification path.
+ *
+ * @return the built and validated CertPath
(never
+ * null
)
+ */
+ public CertPath getCertPath()
+ {
+ return certPath;
+ }
+
+ /**
+ * Return a printable representation of this
+ * PKIXCertPathBuilderResult
.
+ *
+ * @return a String
describing the contents of this
+ * PKIXCertPathBuilderResult
+ */
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append( "PKIXCertPathBuilderResult: [\n" );
+ s.append( " Certification Path: ").append(getCertPath()).append('\n' );
+ s.append( " Trust Anchor: ").append(getTrustAnchor()).append('\n' );
+ s.append( " Policy Tree: ").append(getPolicyTree()).append('\n' );
+ s.append( " Subject Public Key: ").append(getPublicKey()).append("\n]");
+ return s.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathChecker.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathChecker.java
new file mode 100644
index 000000000..14dec8060
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathChecker.java
@@ -0,0 +1,155 @@
+package java.security.cert;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * An abstract class that performs one or more checks on an
+ * X509Certificate
.
+ *
+ * A concrete implementation of the PKIXCertPathChecker
class
+ * can be created to extend the PKIX certification path validation algorithm.
+ * For example, an implementation may check for and process a critical private
+ * extension of each certificate in a certification path.
+ *
+ * Instances of PKIXCertPathChecker
are passed as parameters
+ * using the {@link PKIXParameters#setCertPathCheckers setCertPathCheckers}
+ * or {@link PKIXParameters#addCertPathChecker addCertPathChecker} methods
+ * of the PKIXParameters
and PKIXBuilderParameters
+ * class. Each of the PKIXCertPathChecker
s {@link #check check}
+ * methods will be called, in turn, for each certificate processed by a PKIX
+ * CertPathValidator
or CertPathBuilder
+ * implementation.
+ *
+ * A PKIXCertPathChecker
may be called multiple times on
+ * successive certificates in a certification path. Concrete subclasses
+ * are expected to maintain any internal state that may be necessary to
+ * check successive certificates. The {@link #init init} method is used
+ * to initialize the internal state of the checker so that the certificates
+ * of a new certification path may be checked. A stateful implementation
+ * must override the {@link #clone clone} method if necessary in
+ * order to allow a PKIX CertPathBuilder
to efficiently
+ * backtrack and try other paths. In these situations, the
+ * CertPathBuilder
is able to restore prior path validation
+ * states by restoring the cloned PKIXCertPathChecker
s.
+ *
+ * The order in which the certificates are presented to the
+ * PKIXCertPathChecker
may be either in the forward direction
+ * (from target to most-trusted CA) or in the reverse direction (from
+ * most-trusted CA to target). A PKIXCertPathChecker
implementation
+ * must support reverse checking (the ability to perform its checks when
+ * it is presented with certificates in the reverse direction) and may
+ * support forward checking (the ability to perform its checks when it is
+ * presented with certificates in the forward direction). The
+ * {@link #isForwardCheckingSupported isForwardCheckingSupported} method
+ * indicates whether forward checking is supported.
+ *
+ * Additional input parameters required for executing the check may be
+ * specified through constructors of concrete implementations of this class.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see PKIXParameters
+ * @see PKIXBuilderParameters
+ **/
+public abstract class PKIXCertPathChecker implements Cloneable
+{
+
+ /**
+ * Default constructor.
+ */
+ protected PKIXCertPathChecker() {}
+
+ /**
+ * Initializes the internal state of this PKIXCertPathChecker
.
+ * forward
flag specifies the order that
+ * certificates will be passed to the {@link #check check} method
+ * (forward or reverse). A PKIXCertPathChecker
must
+ * support reverse checking and may support forward checking.
+ *
+ * @param forward the order that certificates are presented to
+ * the check
method. If true
, certificates
+ * are presented from target to most-trusted CA (forward); if
+ * false
, from most-trusted CA to target (reverse).
+ * @exception CertPathValidatorException if this
+ * PKIXCertPathChecker
is unable to check certificates in
+ * the specified order; it should never be thrown if the forward flag
+ * is false since reverse checking must be supported
+ */
+ public abstract void init(boolean forward)
+ throws CertPathValidatorException;
+
+ /**
+ * Indicates if forward checking is supported. Forward checking refers
+ * to the ability of the PKIXCertPathChecker
to perform
+ * its checks when certificates are presented to the check
+ * method in the forward direction (from target to most-trusted CA).
+ *
+ * @return true
if forward checking is supported,
+ * false
otherwise
+ */
+ public abstract boolean isForwardCheckingSupported();
+
+ /**
+ * Returns an immutable Set
of X.509 certificate extensions
+ * that this PKIXCertPathChecker
supports (i.e. recognizes, is
+ * able to process), or null
if no extensions are supported.
+ * String
representing the
+ * Object Identifier (OID) of the X.509 extension that is supported.
+ * The OID is represented by a set of nonnegative integers separated by
+ * periods.
+ * PKIXCertPathChecker
+ * might possibly be able to process should be included in the set.
+ *
+ * @return an immutable Set
of X.509 extension OIDs (in
+ * String
format) supported by this
+ * PKIXCertPathChecker
, or null
if no
+ * extensions are supported
+ */
+ public abstract Set getSupportedExtensions();
+
+ /**
+ * Performs the check(s) on the specified certificate using its internal
+ * state and removes any critical extensions that it processes from the
+ * specified collection of OID strings that represent the unresolved
+ * critical extensions. The certificates are presented in the order
+ * specified by the init
method.
+ *
+ * @param cert the Certificate
to be checked
+ * @param unresolvedCritExts a Collection
of OID strings
+ * representing the current set of unresolved critical extensions
+ * @exception CertPathValidatorException if the specified certificate does
+ * not pass the check
+ */
+ public abstract void check(
+ Certificate cert,
+ Collection unresolvedCritExts)
+ throws CertPathValidatorException;
+
+ /**
+ * Returns a clone of this object. Calls the Object.clone()
+ * method.
+ * All subclasses which maintain state must support and
+ * override this method, if necessary.
+ *
+ * @return a copy of this PKIXCertPathChecker
+ */
+ public Object clone()
+ {
+ try {
+ return super.clone();
+ } catch ( CloneNotSupportedException ex ) {
+ /* Cannot happen */
+ throw new InternalError( ex.toString() );
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathValidatorResult.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathValidatorResult.java
new file mode 100644
index 000000000..8ffa25555
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXCertPathValidatorResult.java
@@ -0,0 +1,136 @@
+package java.security.cert;
+
+import java.security.PublicKey;
+
+/**
+ * This class represents the successful result of the PKIX certification
+ * path validation algorithm.
+ *
+ * Instances of PKIXCertPathValidatorResult
are returned by the
+ * {@link CertPathValidator#validate validate} method of
+ * CertPathValidator
objects implementing the PKIX algorithm.
+ *
+ * All PKIXCertPathValidatorResult
objects contain the
+ * valid policy tree and subject public key resulting from the
+ * validation algorithm, as well as a TrustAnchor
describing
+ * the certification authority (CA) that served as a trust anchor for the
+ * certification path.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertPathValidatorResult
+ **/
+public class PKIXCertPathValidatorResult implements CertPathValidatorResult
+{
+ private TrustAnchor trustAnchor;
+ private PolicyNode policyTree;
+ private PublicKey subjectPublicKey;
+
+ /**
+ * Creates an instance of PKIXCertPathValidatorResult
+ * containing the specified parameters.
+ *
+ * @param trustAnchor a TrustAnchor
describing the CA that
+ * served as a trust anchor for the certification path
+ * @param policyTree the immutable valid policy tree, or null
+ * if there are no valid policies
+ * @param subjectPublicKey the public key of the subject
+ *
+ * @exception NullPointerException if the subjectPublicKey
or
+ * trustAnchor
parameters are null
+ */
+ public PKIXCertPathValidatorResult(TrustAnchor trustAnchor,
+ PolicyNode policyTree,
+ PublicKey subjectPublicKey)
+ {
+ if ( subjectPublicKey == null )
+ throw new NullPointerException( "subjectPublicKey must be non-null" );
+ if ( trustAnchor == null )
+ throw new NullPointerException( "trustAnchor must be non-null" );
+
+ this.trustAnchor = trustAnchor;
+ this.policyTree = policyTree;
+ this.subjectPublicKey = subjectPublicKey;
+ }
+
+ /**
+ * Returns the TrustAnchor
describing the CA that served
+ * as a trust anchor for the certification path.
+ *
+ * @return the TrustAnchor
(never null
)
+ */
+ public TrustAnchor getTrustAnchor()
+ {
+ return trustAnchor;
+ }
+
+ /**
+ * Returns the root node of the valid policy tree resulting from the
+ * PKIX certification path validation algorithm. The
+ * PolicyNode
object that is returned and any objects that
+ * it returns through public methods are immutable.
+ *
+ * Most applications will not need to examine the valid policy tree.
+ * They can achieve their policy processing goals by setting the
+ * policy-related parameters in PKIXParameters
. However, more
+ * sophisticated applications, especially those that process policy
+ * qualifiers, may need to traverse the valid policy tree using the
+ * {@link PolicyNode#getParent PolicyNode.getParent} and
+ * {@link PolicyNode#getChildren PolicyNode.getChildren} methods.
+ *
+ * @return the root node of the valid policy tree, or null
+ * if there are no valid policies
+ */
+ public PolicyNode getPolicyTree()
+ {
+ return policyTree;
+ }
+
+ /**
+ * Returns the public key of the subject (target) of the certification
+ * path, including any inherited public key parameters if applicable.
+ *
+ * @return the public key of the subject (never null
)
+ */
+ public PublicKey getPublicKey()
+ {
+ return subjectPublicKey;
+ }
+
+ /**
+ * Returns a copy of this object.
+ *
+ * @return the copy
+ */
+ public Object clone()
+ {
+ try {
+ return super.clone();
+ } catch ( CloneNotSupportedException ex ) {
+ throw new InternalError( ex.toString() );
+ }
+ }
+
+ /**
+ * Return a printable representation of this
+ * PKIXCertPathValidatorResult
.
+ *
+ * @return a String
describing the contents of this
+ * PKIXCertPathValidatorResult
+ */
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append( "PKIXCertPathValidatorResult: [ \n" );
+ s.append( " Trust Anchor: ").append(getTrustAnchor()).append('\n' );
+ s.append( " Policy Tree: ").append(getPolicyTree()).append('\n' );
+ s.append( " Subject Public Key: ").append(getPublicKey()).append("\n]" );
+ return s.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXParameters.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXParameters.java
new file mode 100644
index 000000000..3c55d7e49
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PKIXParameters.java
@@ -0,0 +1,770 @@
+package java.security.cert;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Parameters used as input for the PKIX CertPathValidator algorithm.
+ *
+ * A PKIX CertPathValidator
uses these parameters to validate a
+ * CertPath
according to the PKIX certification path validation
+ * algorithm.
+ *
+ * To instantiate a PKIXParameters
object, an application must specify
+ * one or more most-trusted CAs as defined by the PKIX certification
+ * path validation algorithm. The most-trusted CAs can be specified
+ * using one of two constructors. An application can call
+ * {@link #PKIXParameters(Set)}, specifying a Set of TrustAnchor
objects, each
+ * of which identify a most-trusted CA. Alternatively, an application
+ * can call {@link #PKIXParameters(KeyStore)}, specifying a KeyStore
instance
+ * containing trusted certificate entries, each of which will be
+ * considered as a most-trusted CA.
+ *
+ * Once a PKIXParameters
object has been created, other parameters can
+ * be specified (by calling {@link #setInitialPolicies} or {@link #setDate}, for
+ * instance) and then the PKIXParameters
is passed along with the
+ * CertPath
to be validated to {@link CertPathValidator#validate}.
+ *
+ * Any parameter that is not set (or is set to null) will be set to the
+ * default value for that parameter. The default value for the date
+ * parameter is null, which indicates the current time when the path is
+ * validated. The default for the remaining parameters is the least
+ * constrained.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are
+ * not thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * @see CertPathValidator
+ **/
+public class PKIXParameters implements CertPathParameters {
+ private Set trustAnchors;
+ private Set initialPolicies = new HashSet();
+ private List certStores = new ArrayList();
+ private CertSelector certSelector;
+ private List certPathCheckers = new ArrayList();
+ private boolean revocationEnabled = true;
+ private boolean explicitPolicyRequired = false;
+ private boolean policyMappingInhibited = false;
+ private boolean anyPolicyInhibited = false;
+ private boolean policyQualifiersRejected = true;
+ private Date date;
+ private String sigProvider;
+
+ /**
+ * Creates an instance of PKIXParameters with the specified
+ * Set of most-trusted CAs. Each element of the set is a
+ * TrustAnchor.
+ *
+ * Note that the Set is copied to protect against subsequent
+ * modifications.
+ *
+ * @param trustAnchors a Set of TrustAnchors
+ *
+ * @exception InvalidAlgorithmParameterException if the
+ * specified Set is empty (trustAnchors.isEmpty() == true)
+ * @exception NullPointerException if the specified Set is null
+ * @exception ClassCastException if any of the elements in the
+ * Set are not of type
+ * java.security.cert.TrustAnchor
+ **/
+ public PKIXParameters(Set trustAnchors)
+ throws InvalidAlgorithmParameterException
+ {
+ setTrustAnchors( trustAnchors );
+ }
+
+ /**
+ * Creates an instance of PKIXParameters that populates the
+ * set of most-trusted CAs from the trusted certificate
+ * entries contained in the specified KeyStore. Only keystore
+ * entries that contain trusted X509Certificates are
+ * considered; all other certificate types are ignored.
+ *
+ * @param keystore a KeyStore from which the set of
+ * most-trusted CAs will be populated
+ *
+ * @exception KeyStoreException if the keystore has not been
+ * initialized
+ * @exception InvalidAlgorithmParameterException if the keystore
+ * does not contain at least one trusted certificate entry
+ * @exception NullPointerException if the keystore is null
+ **/
+ public PKIXParameters(KeyStore keystore)
+ throws KeyStoreException,
+ InvalidAlgorithmParameterException
+ {
+ if ( keystore == null )
+ throw new NullPointerException( "the keystore parameter must be non-null" );
+
+ Set trustAnchors = new HashSet();
+ String alias;
+ Certificate cert;
+ Enumeration enum = keystore.aliases();
+ while ( enum.hasMoreElements() ) {
+ alias = (String)enum.nextElement();
+ if ( keystore.isCertificateEntry( alias ) ) {
+ cert = keystore.getCertificate( alias );
+ if ( cert instanceof X509Certificate )
+ trustAnchors.add( new TrustAnchor( (X509Certificate)cert, null ) );
+ }
+ }
+ setTrustAnchors( trustAnchors );
+ }
+
+ /**
+ * Returns an immutable Set of the most-trusted CAs.
+ *
+ * @return an immutable Set
of
+ * TrustAnchors
(never null
)
+ *
+ * @see #setTrustAnchors
+ **/
+ public Set getTrustAnchors()
+ {
+ return Collections.unmodifiableSet(trustAnchors);
+ }
+
+ /**
+ * Sets the Set of most-trusted CAs.
+ *
+ * Note that the Set is copied to protect against subsequent
+ * modifications.
+ *
+ * @param trustAnchors a Set of TrustAnchors
+ *
+ * @exception InvalidAlgorithmParameterException if the specified Set is empty (trustAnchors.isEmpty() == true)
+ * @exception NullPointerException if the specified Set is null
+ * @exception ClassCastException if any of the elements in
+ * the set are not of type java.security.cert.TrustAnchor
+ *
+ * @see #getTrustAnchors
+ **/
+ public void setTrustAnchors(Set trustAnchors)
+ throws InvalidAlgorithmParameterException
+ {
+ if ( trustAnchors == null )
+ throw new NullPointerException("the trustAnchors parameter must be non-null");
+ if ( trustAnchors.isEmpty() )
+ throw new InvalidAlgorithmParameterException("the trustAnchors parameter must be non-empty");
+
+ Iterator iter = trustAnchors.iterator();
+ TrustAnchor obj;
+ this.trustAnchors = new HashSet();
+ while( iter.hasNext() ) {
+ obj = (TrustAnchor)iter.next();
+ if ( obj != null ) {
+ this .trustAnchors.add( obj );
+ }
+ }
+ }
+
+ /**
+ * Returns an immutable Set of initial policy identifiers (OID
+ * strings), indicating that any one of these policies would
+ * be acceptable to the certificate user for the purposes of
+ * certification path processing. The default return value is
+ * an empty Set
, which is interpreted as meaning that any
+ * policy would be acceptable.
+ *
+ * @return an immutable Set
of initial policy
+ * OIDs in String format, or an empty Set
(implying any policy
+ * is acceptable). Never returns null
.
+ *
+ * @see #setInitialPolicies(java.util.Set)
+ **/
+ public Set getInitialPolicies()
+ {
+ Set returnSet = initialPolicies;
+ if ( initialPolicies == null )
+ returnSet = new HashSet();
+
+ return Collections.unmodifiableSet( returnSet );
+ }
+
+ /**
+ * Sets the Set
of initial policy identifiers (OID strings),
+ * indicating that any one of these policies would be
+ * acceptable to the certificate user for the purposes of
+ * certification path processing. By default, any policy is
+ * acceptable (i.e. all policies), so a user that wants to
+ * allow any policy as acceptable does not need to call this
+ * method, or can call it with an empty Set
(or null
).
+ *
+ * Note that the Set is copied to protect against subsequent
+ * modifications.
+ *
+ * @param initialPolicies a Set of initial policy OIDs in String format (or null
)
+ *
+ * @exception ClassCastException if any of the elements in the
+ * set are not of type String
+ *
+ * @see #getInitialPolicies()
+ **/
+ public void setInitialPolicies(Set initialPolicies)
+ {
+ if ( initialPolicies == null || initialPolicies.isEmpty() )
+ {
+ this.initialPolicies = null;
+ }
+ else
+ {
+ Iterator iter = initialPolicies.iterator();
+ this.initialPolicies = new HashSet();
+ String obj;
+ while ( iter.hasNext() )
+ {
+ obj = (String)iter.next();
+ if ( obj != null ) {
+ this.initialPolicies.add( obj );
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the list of CertStores to be used in finding
+ * certificates and CRLs. May be null, in which case no
+ * CertStores will be used. The first CertStores in the list
+ * may be preferred to those that appear later.
+ *
+ * Note that the List is copied to protect against subsequent
+ * modifications.
+ *
+ * @param stores a List of CertStores (or null
)
+ *
+ * @exception ClassCastException if any of the elements in the
+ * list are not of type java.security.cert.CertStore
+ *
+ * @see #getCertStores()
+ **/
+ public void setCertStores(List stores)
+ {
+ certStores = new ArrayList();
+ if ( stores != null && ! stores.isEmpty() )
+ {
+ Iterator iter = stores.iterator();
+ CertStore obj;
+ while ( iter.hasNext() )
+ {
+ obj = (CertStore)iter.next();
+ if ( obj != null )
+ {
+ certStores.add( obj );
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a CertStore to the end of the list of CertStores used
+ * in finding certificates and CRLs.
+ *
+ * @param store the CertStore
to add. If
+ * null
null
+ *
+ * When a PKIXParameters
object is created, this flag is set
+ * to true. This setting reflects the most common strategy for checking
+ * revocation, since each service provider must support revocation
+ * checking to be PKIX compliant. Sophisticated applications should set
+ * this flag to false when it is not practical to use a PKIX service
+ * provider's default revocation checking mechanism or when an alternative
+ * revocation checking mechanism is to be substituted (by also calling the
+ * {@link #addCertPathChecker addCertPathChecker} or {@link
+ * #setCertPathCheckers setCertPathCheckers} methods).
+ *
+ * @param val the new value of the RevocationEnabled flag
+ **/
+ public void setRevocationEnabled(boolean val)
+ {
+ revocationEnabled = val;
+ }
+
+ /**
+ * Checks the RevocationEnabled flag. If this flag is true,
+ * the default revocation checking mechanism of the underlying
+ * PKIX service provider will be used. If this flag is false,
+ * the default revocation checking mechanism will be disabled
+ * (not used). See the setRevocationEnabled method for more
+ * details on setting the value of this flag.
+ *
+ * @return the current value of the RevocationEnabled flag
+ **/
+ public boolean isRevocationEnabled()
+ {
+ return revocationEnabled;
+ }
+
+ /**
+ * Sets the ExplicitPolicyRequired flag. If this flag is true,
+ * an acceptable policy needs to be explicitly identified in
+ * every certificate. By default, the ExplicitPolicyRequired
+ * flag is false.
+ *
+ * @param val true if explicit policy is to be required, false
+ * otherwise
+ **/
+ public void setExplicitPolicyRequired(boolean val)
+ {
+ explicitPolicyRequired = val;
+ }
+
+ /**
+ * Checks if explicit policy is required. If this flag is
+ * true, an acceptable policy needs to be explicitly
+ * identified in every certificate. By default, the
+ * ExplicitPolicyRequired flag is false.
+ *
+ * @return true if explicit policy is required, false otherwise
+ **/
+ public boolean isExplicitPolicyRequired()
+ {
+ return explicitPolicyRequired;
+ }
+
+ /**
+ * Sets the PolicyMappingInhibited flag. If this flag is true,
+ * policy mapping is inhibited. By default, policy mapping is
+ * not inhibited (the flag is false).
+ *
+ * @param val true if policy mapping is to be inhibited, false otherwise
+ **/
+ public void setPolicyMappingInhibited(boolean val)
+ {
+ policyMappingInhibited = val;
+ }
+
+ /**
+ * Checks if policy mapping is inhibited. If this flag is
+ * true, policy mapping is inhibited. By default, policy
+ * mapping is not inhibited (the flag is false).
+ *
+ * @return true if policy mapping is inhibited, false otherwise
+ **/
+ public boolean isPolicyMappingInhibited()
+ {
+ return policyMappingInhibited;
+ }
+
+ /**
+ * Sets state to determine if the any policy OID should be
+ * processed if it is included in a certificate. By default,
+ * the any policy OID is not inhibited ({@link #isAnyPolicyInhibited()}
+ * returns false).
+ *
+ * @return val - true
if the any policy OID is to be inhibited, false
otherwise
+ **/
+ public void setAnyPolicyInhibited(boolean val)
+ {
+ anyPolicyInhibited = val;
+ }
+
+ /**
+ * Checks whether the any policy OID should be processed if it
+ * is included in a certificate.
+ *
+ * @return true
if the any policy OID is inhibited, false
otherwise
+ **/
+ public boolean isAnyPolicyInhibited()
+ {
+ return anyPolicyInhibited;
+ }
+
+ /**
+ * Sets the PolicyQualifiersRejected flag. If this flag is
+ * true, certificates that include policy qualifiers in a
+ * certificate policies extension that is marked critical are
+ * rejected. If the flag is false, certificates are not
+ * rejected on this basis.
+ *
+ * When a PKIXParameters
object is created, this flag is set
+ * to true. This setting reflects the most common (and
+ * simplest) strategy for processing policy
+ * qualifiers. Applications that want to use a more
+ * sophisticated policy must set this flag to false.
+ *
+ * Note that the PKIX certification path validation algorithm
+ * specifies that any policy qualifier in a certificate
+ * policies extension that is marked critical must be
+ * processed and validated. Otherwise the certification path
+ * must be rejected. If the policyQualifiersRejected flag is
+ * set to false, it is up to the application to validate all
+ * policy qualifiers in this manner in order to be PKIX
+ * compliant.
+ *
+ * @param qualifiersRejected the new value of the PolicyQualifiersRejected flag
+ *
+ * @see #getPolicyQualifiersRejected()
+ * @see PolicyQualifierInfo
+ **/
+ public void setPolicyQualifiersRejected(boolean qualifiersRejected)
+ {
+ policyQualifiersRejected = qualifiersRejected;
+ }
+
+ /**
+ * Gets the PolicyQualifiersRejected flag. If this flag is
+ * true, certificates that include policy qualifiers in a
+ * certificate policies extension that is marked critical are
+ * rejected. If the flag is false, certificates are not
+ * rejected on this basis.
+ *
+ * When a PKIXParameters object is created, this flag is set to
+ * true. This setting reflects the most common (and simplest)
+ * strategy for processing policy qualifiers. Applications that
+ * want to use a more sophisticated policy must set this flag
+ * to false.
+ *
+ * @return the current value of the PolicyQualifiersRejected flag
+ *
+ * @see #setPolicyQualifiersRejected(boolean)
+ **/
+ public boolean getPolicyQualifiersRejected()
+ {
+ return policyQualifiersRejected;
+ }
+
+ /**
+ * Returns the time for which the validity of the
+ * certification path should be determined. If null, the
+ * current time is used.
+ *
+ * Note that the Date returned is copied to protect against
+ * subsequent modifications.
+ *
+ * @return the Date, or null
if not set
+ *
+ * @see #setDate(java.util.Date)
+ **/
+ public Date getDate()
+ {
+ if ( date == null )
+ return null;
+
+ return new Date( date.getTime() );
+ }
+
+ /**
+ * Sets the time for which the validity of the certification
+ * path should be determined. If null, the current time is
+ * used.
+ *
+ * Note that the Date supplied here is copied to protect
+ * against subsequent modifications.
+ *
+ * @param date the Date, or null
for the current time
+ *
+ * @see #getDate()
+ **/
+ public void setDate(Date date)
+ {
+ if ( date == null )
+ this.date = null;
+ else
+ this.date = new Date( date.getTime() );
+ }
+
+ /**
+ * Sets a List
of additional certification path checkers. If
+ * the specified List contains an object that is not a
+ * PKIXCertPathChecker, it is ignored.
+ *
+ * Each PKIXCertPathChecker
specified implements additional
+ * checks on a certificate. Typically, these are checks to
+ * process and verify private extensions contained in
+ * certificates. Each PKIXCertPathChecker
should be
+ * instantiated with any initialization parameters needed to
+ * execute the check.
+ *
+ * This method allows sophisticated applications to extend a
+ * PKIX CertPathValidator
or CertPathBuilder
. Each of the
+ * specified PKIXCertPathCheckers will be called, in turn, by
+ * a PKIX CertPathValidator
or CertPathBuilder
for each
+ * certificate processed or validated.
+ *
+ * Regardless of whether these additional PKIXCertPathCheckers
+ * are set, a PKIX CertPathValidator
or CertPathBuilder
must
+ * perform all of the required PKIX checks on each
+ * certificate. The one exception to this rule is if the
+ * RevocationEnabled flag is set to false (see the
+ * {@link #setRevocationEnabled(boolean) setRevocationEnabled} method).
+ *
+ * Note that the List supplied here is copied and each
+ * PKIXCertPathChecker in the list is cloned to protect against
+ * subsequent modifications.
+ *
+ * @param checkers a List of PKIXCertPathCheckers. May be
+ * null, in which case no additional checkers will be used.
+ * @exception ClassCastException if any of the elements in the
+ * list are not of type
+ * java.security.cert.PKIXCertPathChecker
+ * @see #getCertPathCheckers()
+ **/
+ public void setCertPathCheckers(List checkers)
+ {
+ certPathCheckers = new ArrayList();
+ if ( checkers == null )
+ return;
+ Iterator iter = checkers.iterator();
+ while ( iter.hasNext() )
+ certPathCheckers.add( (PKIXCertPathChecker)((PKIXCertPathChecker)iter.next()).clone() );
+ }
+
+ /**
+ * Returns the List of certification path checkers. The
+ * returned List is immutable, and each PKIXCertPathChecker in
+ * the List is cloned to protect against subsequent
+ * modifications.
+ *
+ * @return an immutable List of PKIXCertPathCheckers (may be empty, but not null
)
+ *
+ * @see #setCertPathCheckers(java.util.List)
+ **/
+ public List getCertPathCheckers()
+ {
+ List checkers = new ArrayList();
+ Iterator iter = certPathCheckers.iterator();
+ while ( iter.hasNext() )
+ {
+ checkers.add( (PKIXCertPathChecker)((PKIXCertPathChecker)iter.next()).clone() );
+ }
+ return Collections.unmodifiableList(checkers);
+ }
+
+ /**
+ * Adds a PKIXCertPathChecker to the list of certification
+ * path checkers. See the {@link #setCertPathCheckers} method for more
+ * details.
+ *
+ * Note that the PKIXCertPathChecker
is cloned to protect
+ * against subsequent modifications.
+ *
+ * @param checker a PKIXCertPathChecker
to add
+ * to the list of checks. If null
, the checker is
+ * ignored (not added to list).
+ **/
+ public void addCertPathChecker( PKIXCertPathChecker checker )
+ {
+ if ( checker != null )
+ {
+ certPathCheckers.add( checker.clone() );
+ }
+ }
+
+ /**
+ * Returns the signature provider's name, or null
if not set.
+ *
+ * @return the signature provider's name (or null
)
+ *
+ * @see #setSigProvider(java.lang.String)
+ **/
+ public String getSigProvider()
+ {
+ return sigProvider;
+ }
+
+ /**
+ * Sets the signature provider's name. The specified provider
+ * will be preferred when creating Signature objects. If null
+ * or not set, the first provider found supporting the
+ * algorithm will be used.
+ *
+ * @param sigProvider the signature provider's name (or null
)
+ *
+ * @see #getSigProvider()
+ **/
+ public void setSigProvider(String sigProvider)
+ {
+ this.sigProvider = sigProvider;
+ }
+
+ /**
+ * Returns the required constraints on the target
+ * certificate. The constraints are returned as an instance of
+ * CertSelector. If null
, no constraints are defined.
+ *
+ * Note that the CertSelector returned is cloned to protect
+ * against subsequent modifications.
+ *
+ * @return a CertSelector specifying the constraints on the target certificate (or null
)
+ *
+ * @see #setTargetCertConstraints(java.security.cert.CertSelector)
+ **/
+ public CertSelector getTargetCertConstraints()
+ {
+ if ( certSelector == null )
+ return null;
+
+ return (CertSelector)certSelector.clone();
+ }
+
+ /**
+ * Sets the required constraints on the target
+ * certificate. The constraints are specified as an instance
+ * of CertSelector. If null, no constraints are defined.
+ *
+ * Note that the CertSelector specified is cloned to protect
+ * against subsequent modifications.
+ *
+ * @param selector a CertSelector specifying the constraints
+ * on the target certificate (or null
)
+ *
+ * @see #getTargetCertConstraints()
+ **/
+ public void setTargetCertConstraints(CertSelector selector)
+ {
+ if ( selector == null )
+ certSelector = null;
+ else
+ certSelector = (CertSelector)selector.clone();
+ }
+
+ /**
+ * Makes a copy of this PKIXParameters object. Changes to the
+ * copy will not affect the original and vice versa.
+ *
+ * @return a copy of this PKIXParameters
object
+ **/
+ public Object clone()
+ {
+ try {
+ PKIXParameters obj = (PKIXParameters)super.clone();
+ obj.certStores = new ArrayList( certStores );
+ Iterator iter = certPathCheckers.iterator();
+ obj.certPathCheckers = new ArrayList();
+ while ( iter.hasNext() )
+ {
+ obj.certPathCheckers.add( ((PKIXCertPathChecker)iter.next()).clone() );
+ }
+ if ( initialPolicies != null )
+ {
+ obj.initialPolicies = new HashSet( initialPolicies );
+ }
+ if ( trustAnchors != null )
+ {
+ obj.trustAnchors = new HashSet( trustAnchors );
+ }
+ if ( certSelector != null )
+ {
+ obj.certSelector = (CertSelector)certSelector.clone();
+ }
+ return obj;
+ } catch ( CloneNotSupportedException ex ) {
+ throw new InternalError();
+ }
+ }
+
+ /**
+ * Returns a formatted string describing the parameters.
+ *
+ * @return a formatted string describing the parameters.
+ **/
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append("[\n");
+ if ( trustAnchors != null )
+ {
+ s.append(" Trust Anchors: ").append(trustAnchors).append('\n');
+ }
+ if ( initialPolicies != null )
+ {
+ if ( initialPolicies.isEmpty() )
+ {
+ s.append(" Initial Policy OIDs: any\n" );
+ }
+ else
+ {
+ s.append(" Initial Policy OIDs: [").append(initialPolicies).append("]\n");
+ }
+ }
+ s.append(" Validity Date: ");
+ if ( date != null )
+ s.append(date);
+ else
+ s.append("null");
+ s.append('\n');
+
+ s.append(" Signature Provider: ");
+ if ( sigProvider != null )
+ s.append(sigProvider);
+ else
+ s.append("null");
+ s.append('\n');
+
+ s.append(" Default Revocation Enabled: ");
+ s.append(revocationEnabled);
+ s.append('\n' );
+
+ s.append(" Explicit Policy Required: ");
+ s.append(explicitPolicyRequired);
+ s.append('\n');
+
+ s.append(" Policy Mapping Inhibited: ");
+ s.append(policyMappingInhibited);
+ s.append('\n');
+
+ s.append(" Any Policy Inhibited: ");
+ s.append(anyPolicyInhibited);
+ s.append('\n');
+
+ s.append(" Policy Qualifiers Rejected: ");
+ s.append(policyQualifiersRejected);
+ s.append('\n');
+
+ s.append(" Target Cert Constraints: ");
+ s.append(certSelector);
+ s.append('\n');
+
+ s.append(" Certification Path Checkers: [");
+ s.append(certPathCheckers);
+ s.append( "}\n");
+
+ s.append(" CertStores: [");
+ s.append(certStores);
+ s.append("}\n");
+
+ s.append("]\n");
+
+ return s.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PolicyNode.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PolicyNode.java
new file mode 100644
index 000000000..cdae45205
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PolicyNode.java
@@ -0,0 +1,107 @@
+package java.security.cert;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * An immutable valid policy tree node as defined by the PKIX certification
+ * path validation algorithm.
+ *
+ * One of the outputs of the PKIX certification path validation
+ * algorithm is a valid policy tree, which includes the policies that
+ * were determined to be valid, how this determination was reached,
+ * and any policy qualifiers encountered. This tree is of depth
+ * n, where n is the length of the certification
+ * path that has been validated.
+ *
+ * Most applications will not need to examine the valid policy tree.
+ * They can achieve their policy processing goals by setting the
+ * policy-related parameters in PKIXParameters
. However,
+ * the valid policy tree is available for more sophisticated applications,
+ * especially those that process policy qualifiers.
+ *
+ * {@link PKIXCertPathValidatorResult#getPolicyTree()
+ * PKIXCertPathValidatorResult.getPolicyTree} returns the root node of the
+ * valid policy tree. The tree can be traversed using the
+ * {@link #getChildren getChildren} and {@link #getParent getParent} methods.
+ * Data about a particular node can be retrieved using other methods of
+ * PolicyNode
.
+ *
+ * Concurrent Access
+ *
+ * All PolicyNode
objects must be immutable and
+ * thread-safe. Multiple threads may concurrently invoke the methods defined
+ * in this class on a single PolicyNode
object (or more than one)
+ * with no ill effects. This stipulation applies to all public fields and
+ * methods of this class and any added or overridden by subclasses.
+ **/
+public interface PolicyNode
+{
+
+ /**
+ * Returns the parent of this node, or null
if this is the
+ * root node.
+ *
+ * @return the parent of this node, or null
if this is the
+ * root node
+ */
+ public PolicyNode getParent();
+
+ /**
+ * Returns an iterator over the children of this node. Any attempts to
+ * modify the children of this node through the
+ * Iterator
's remove method must throw an
+ * UnsupportedOperationException
.
+ *
+ * @return an iterator over the children of this node
+ */
+ public Iterator getChildren();
+
+ /**
+ * Returns the depth of this node in the valid policy tree.
+ *
+ * @return the depth of this node (0 for the root node, 1 for its
+ * children, and so on)
+ */
+ public int getDepth();
+
+ /**
+ * Returns the valid policy represented by this node.
+ *
+ * @return the String
OID of the valid policy
+ * represented by this node, or the special value "any-policy". For
+ * the root node, this method always returns the special value "any-policy".
+ */
+ public String getValidPolicy();
+
+ /**
+ * Returns the set of policy qualifiers associated with the
+ * valid policy represented by this node.
+ *
+ * @return an immutable Set
of
+ * PolicyQualifierInfo
s. For the root node, this
+ * is always an empty Set
.
+ */
+ public Set getPolicyQualifiers();
+
+ /**
+ * Returns the set of expected policies that would satisfy this
+ * node's valid policy in the next certificate to be processed.
+ *
+ * @return an immutable Set
of expected policy
+ * String
OIDs, or an immutable Set
with
+ * the single special value "any-policy". For the root node, this method
+ * always returns a Set
with the single value "any-policy".
+ */
+ public Set getExpectedPolicies();
+
+ /**
+ * Returns the criticality indicator of the certificate policy extension
+ * in the most recently processed certificate.
+ *
+ * @return true
if extension marked critical,
+ * false
otherwise. For the root node, false
+ * is always returned.
+ */
+ public boolean isCritical();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PolicyQualifierInfo.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PolicyQualifierInfo.java
new file mode 100644
index 000000000..a17f49bf4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/PolicyQualifierInfo.java
@@ -0,0 +1,196 @@
+package java.security.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.util.ASN1Dump;
+
+/**
+ * An immutable policy qualifier represented by the ASN.1 PolicyQualifierInfo
+ * structure.
+ *
+ * The ASN.1 definition is as follows:
+ *
+ *
+ *
+ * PolicyQualifierInfo ::= SEQUENCE {
+ * policyQualifierId PolicyQualifierId,
+ * qualifier ANY DEFINED BY policyQualifierId }
+ *
+ *
+ *
+ *
+ * A certificate policies extension, if present in an X.509 version 3
+ * certificate, contains a sequence of one or more policy information terms,
+ * each of which consists of an object identifier (OID) and optional qualifiers.
+ * In an end-entity certificate, these policy information terms indicate the
+ * policy under which the certificate has been issued and the purposes for which
+ * the certificate may be used. In a CA certificate, these policy information
+ * terms limit the set of policies for certification paths which include this
+ * certificate.
+ *
+ * A Set
of PolicyQualifierInfo
objects are
+ * returned by the
+ * {@link PolicyNode#getPolicyQualifiers PolicyNode.getPolicyQualifiers} method.
+ * This allows applications with specific policy requirements to process and
+ * validate each policy qualifier. Applications that need to process policy
+ * qualifiers should explicitly set the policyQualifiersRejected
+ * flag to false (by calling the
+ * {@link PKIXParameters#setPolicyQualifiersRejected
+ * PKIXParameters.setPolicyQualifiersRejected} method) before validating a
+ * certification path.
+ *
+ * Note that the PKIX certification path validation algorithm specifies that any
+ * policy qualifier in a certificate policies extension that is marked critical
+ * must be processed and validated. Otherwise the certification path must be
+ * rejected. If the policyQualifiersRejected
flag is set to
+ * false, it is up to the application to validate all policy qualifiers in this
+ * manner in order to be PKIX compliant.
+ *
+ * Concurrent Access
+ *
+ * All PolicyQualifierInfo
objects must be immutable and
+ * thread-safe. That is, multiple threads may concurrently invoke the methods
+ * defined in this class on a single PolicyQualifierInfo
object
+ * (or more than one) with no ill effects. Requiring
+ * PolicyQualifierInfo
objects to be immutable and thread-safe
+ * allows them to be passed around to various pieces of code without worrying
+ * about coordinating access.
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
+ * {@link org.spongycastle.asn1.DEROutputStream DEROutputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object}
+ */
+public final class PolicyQualifierInfo
+{
+ private String id;
+
+ private byte[] encoded;
+
+ private byte[] qualifier;
+
+ /**
+ * Creates an instance of PolicyQualifierInfo
from the
+ * encoded bytes. The encoded byte array is copied on construction.
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier} and
+ * {@link org.spongycastle.asn1.DEROutputStream DEROutputStream}
+ *
+ * @param encoded
+ * a byte array containing the qualifier in DER encoding
+ *
+ * @exception IOException
+ * thrown if the byte array does not represent a valid and
+ * parsable policy qualifier
+ */
+ public PolicyQualifierInfo(byte[] encoded) throws IOException
+ {
+ this.encoded = (byte[])encoded.clone();
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ this.encoded);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Sequence obj = (ASN1Sequence)derInStream.readObject();
+ id = ((ASN1ObjectIdentifier)obj.getObjectAt(0)).getId();
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+
+ derOutStream.writeObject(obj.getObjectAt(1));
+ derOutStream.close();
+
+ qualifier = outStream.toByteArray();
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("parsing exception : " + ex.toString());
+ }
+ }
+
+ /**
+ * Returns the policyQualifierId
field of this
+ * PolicyQualifierInfo
. The policyQualifierId
+ * is an Object Identifier (OID) represented by a set of nonnegative
+ * integers separated by periods.
+ *
+ * @return the OID (never null
)
+ */
+ public String getPolicyQualifierId()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the ASN.1 DER encoded form of this
+ * PolicyQualifierInfo
.
+ *
+ * @return the ASN.1 DER encoded bytes (never null
). Note
+ * that a copy is returned, so the data is cloned each time this
+ * method is called.
+ */
+ public byte[] getEncoded()
+ {
+ return (byte[])encoded.clone();
+ }
+
+ /**
+ * Returns the ASN.1 DER encoded form of the qualifier
field
+ * of this PolicyQualifierInfo
.
+ *
+ * @return the ASN.1 DER encoded bytes of the qualifier
+ * field. Note that a copy is returned, so the data is cloned each
+ * time this method is called.
+ */
+ public byte[] getPolicyQualifier()
+ {
+ if (qualifier == null)
+ {
+ return null;
+ }
+
+ return (byte[])qualifier.clone();
+ }
+
+ /**
+ * Return a printable representation of this
+ * PolicyQualifierInfo
.
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object}
+ *
+ * @return a String
describing the contents of this
+ * PolicyQualifierInfo
+ */
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append("PolicyQualifierInfo: [\n");
+ s.append("qualifierID: ").append(id).append('\n');
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(qualifier);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object derObject = derInStream.readObject();
+ s
+ .append(" qualifier:\n").append(ASN1Dump.dumpAsString(derObject))
+ .append('\n');
+ }
+ catch (IOException ex)
+ {
+ s.append(ex.getMessage());
+ }
+ s.append("qualifier: ").append(id).append('\n');
+ s.append(']');
+ return s.toString();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/TrustAnchor.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/TrustAnchor.java
new file mode 100644
index 000000000..f139a742c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/TrustAnchor.java
@@ -0,0 +1,293 @@
+package java.security.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Sequence;
+
+/**
+ * A trust anchor or most-trusted Certification Authority (CA).
+ *
+ * This class represents a "most-trusted CA", which is used as a trust anchor
+ * for validating X.509 certification paths. A most-trusted CA includes the
+ * public key of the CA, the CA's name, and any constraints upon the set of
+ * paths which may be validated using this key. These parameters can be
+ * specified in the form of a trusted X509Certificate or as individual
+ * parameters.
+ *
+ * Concurrent Access
+ *
+ * All TrustAnchor objects must be immutable and thread-safe. That is, multiple
+ * threads may concurrently invoke the methods defined in this class on a
+ * single TrustAnchor object (or more than one) with no ill effects. Requiring
+ * TrustAnchor objects to be immutable and thread-safe allows them to be passed
+ * around to various pieces of code without worrying about coordinating access.
+ * This stipulation applies to all public fields and methods of this class and
+ * any added or overridden by subclasses.
+ *
+ * TODO: implement better nameConstraints testing.
+ **/
+public class TrustAnchor
+{
+ private X509Certificate trustCert = null;
+
+ private PublicKey trustPublicKey = null;
+
+ private String trustName = null;
+
+ private byte[] nameConstraints = null;
+
+ /**
+ * Creates an instance of TrustAnchor with the specified X509Certificate and
+ * optional name constraints, which are intended to be used as additional
+ * constraints when validating an X.509 certification path.
+ *
+ * The name constraints are specified as a byte array. This byte array
+ * should contain the DER encoded form of the name constraints, as they
+ * would appear in the NameConstraints structure defined in RFC 2459 and
+ * X.509. The ASN.1 definition of this structure appears below.
+ *
+ *
+ *
+ * NameConstraints ::= SEQUENCE {
+ * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
+ * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
+ *
+ * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
+ *
+ * GeneralSubtree ::= SEQUENCE {
+ * base GeneralName,
+ * minimum [0] BaseDistance DEFAULT 0,
+ * maximum [1] BaseDistance OPTIONAL }
+ *
+ * BaseDistance ::= INTEGER (0..MAX)
+ *
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER}
+ *
+ *
+ *
+ *
+ * Note that the name constraints byte array supplied is cloned to protect
+ * against subsequent modifications.
+ *
+ * @param trustedCert
+ * a trusted X509Certificate
+ * @param nameConstraints
+ * a byte array containing the ASN.1 DER encoding of a
+ * NameConstraints extension to be used for checking name
+ * constraints. Only the value of the extension is included, not
+ * the OID or criticality flag. Specify null to omit the
+ * parameter.
+ *
+ * @exception IllegalArgumentException
+ * if the name constraints cannot be decoded
+ * @exception NullPointerException
+ * if the specified X509Certificate is null
+ */
+ public TrustAnchor(X509Certificate trustedCert, byte[] nameConstraints)
+ {
+ if (trustedCert == null)
+ {
+ throw new NullPointerException("trustedCert must be non-null");
+ }
+
+ this.trustCert = trustedCert;
+ if (nameConstraints != null)
+ {
+ this.nameConstraints = (byte[])nameConstraints.clone();
+ checkNameConstraints(this.nameConstraints);
+ }
+ }
+
+ /**
+ * Creates an instance of TrustAnchor
where the most-trusted
+ * CA is specified as a distinguished name and public key. Name constraints
+ * are an optional parameter, and are intended to be used as additional
+ * constraints when validating an X.509 certification path.
+ *
+ * The name constraints are specified as a byte array. This byte array
+ * contains the DER encoded form of the name constraints, as they would
+ * appear in the NameConstraints structure defined in RFC 2459 and X.509.
+ * The ASN.1 notation for this structure is supplied in the documentation
+ * for {@link #TrustAnchor(X509Certificate trustedCert, byte[]
+ * nameConstraints) TrustAnchor(X509Certificate trustedCert, byte[]
+ * nameConstraints) }.
+ *
+ * Note that the name constraints byte array supplied here is cloned to
+ * protect against subsequent modifications.
+ *
+ * @param caName
+ * the X.500 distinguished name of the most-trusted CA in RFC
+ * 2253 String format
+ * @param pubKey
+ * the public key of the most-trusted CA
+ * @param nameConstraints
+ * a byte array containing the ASN.1 DER encoding of a
+ * NameConstraints extension to be used for checking name
+ * constraints. Only the value of the extension is included, not
+ * the OID or criticality flag. Specify null to omit the
+ * parameter.
+ *
+ * @exception IllegalArgumentException
+ * if the specified caName parameter is empty (caName.length() == 0
)
+ * or incorrectly formatted or the name constraints cannot be
+ * decoded
+ * @exception NullPointerException
+ * if the specified caName or pubKey parameter is null
+ */
+ public TrustAnchor(String caName, PublicKey pubKey, byte[] nameConstraints)
+ {
+ if (caName == null)
+ {
+ throw new NullPointerException("caName must be non-null");
+ }
+ if (pubKey == null)
+ {
+ throw new NullPointerException("pubKey must be non-null");
+ }
+ if (caName.length() == 0)
+ {
+ throw new IllegalArgumentException(
+ "caName can not be an empty string");
+ }
+
+ this.trustName = caName;
+ this.trustPublicKey = pubKey;
+ if (nameConstraints != null)
+ {
+ this.nameConstraints = (byte[])nameConstraints.clone();
+ checkNameConstraints(this.nameConstraints);
+ }
+ }
+
+ /**
+ * Returns the most-trusted CA certificate.
+ *
+ * @return a trusted X509Certificate
or null
+ * if the trust anchor was not specified as a trusted certificate
+ */
+ public final X509Certificate getTrustedCert()
+ {
+ return trustCert;
+ }
+
+ /**
+ * Returns the name of the most-trusted CA in RFC 2253 String format.
+ *
+ * @return the X.500 distinguished name of the most-trusted CA, or
+ * null
if the trust anchor was not specified as a
+ * trusted public key and name pair
+ */
+ public final String getCAName()
+ {
+ return trustName;
+ }
+
+ /**
+ * Returns the public key of the most-trusted CA.
+ *
+ * @return the public key of the most-trusted CA, or null if the trust
+ * anchor was not specified as a trusted public key and name pair
+ */
+ public final PublicKey getCAPublicKey()
+ {
+ return trustPublicKey;
+ }
+
+ /**
+ * Returns the name constraints parameter. The specified name constraints
+ * are associated with this trust anchor and are intended to be used as
+ * additional constraints when validating an X.509 certification path.
+ *
+ * The name constraints are returned as a byte array. This byte array
+ * contains the DER encoded form of the name constraints, as they would
+ * appear in the NameConstraints structure defined in RFC 2459 and X.509.
+ * The ASN.1 notation for this structure is supplied in the documentation
+ * for TrustAnchor(X509Certificate trustedCert, byte[]
+ * nameConstraints)
.
+ *
+ * Note that the byte array returned is cloned to protect against subsequent
+ * modifications.
+ *
+ * @return a byte array containing the ASN.1 DER encoding of a
+ * NameConstraints extension used for checking name constraints, or
+ * null
if not set.
+ */
+ public final byte[] getNameConstraints()
+ {
+ return (byte[])nameConstraints.clone();
+ }
+
+ /**
+ * Returns a formatted string describing the TrustAnchor
.
+ *
+ * @return a formatted string describing the TrustAnchor
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[\n");
+ if (getCAPublicKey() != null)
+ {
+ sb.append(" Trusted CA Public Key: ").append(getCAPublicKey()).append('\n');
+ sb.append(" Trusted CA Issuer Name: ").append(getCAName()).append('\n');
+ }
+ else
+ {
+ sb.append(" Trusted CA cert: ").append(getTrustedCert()).append('\n');
+ }
+ if (nameConstraints != null)
+ {
+ sb.append(" Name Constraints: ").append(nameConstraints).append('\n');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Check given DER encoded nameConstraints for correct decoding. Currently
+ * only basic DER decoding test.
+ *
+ * TODO: implement more testing.
+ *
+ * @param data
+ * the DER encoded nameConstrains to be checked or
+ * null
+ * @exception IllegalArgumentException
+ * if the check failed.
+ */
+ private void checkNameConstraints(byte[] data)
+ {
+ if (data != null)
+ {
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object derObject = derInStream.readObject();
+ if (!(derObject instanceof ASN1Sequence))
+ {
+ throw new IllegalArgumentException(
+ "nameConstraints parameter decoding error");
+ }
+ }
+ catch (IOException ex)
+ {
+ throw new IllegalArgumentException(
+ "nameConstraints parameter decoding error: " + ex);
+ }
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRL.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRL.java
new file mode 100644
index 000000000..cf65ed0b6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRL.java
@@ -0,0 +1,77 @@
+
+package java.security.cert;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.util.Date;
+import java.util.Set;
+
+public abstract class X509CRL extends CRL implements X509Extension
+{
+ protected X509CRL()
+ {
+ super("X.509");
+ }
+
+ public boolean equals(Object other)
+ {
+ if ( this == other )
+ return true;
+
+ if ( !(other instanceof X509CRL) )
+ return false;
+
+ try
+ {
+ byte[] enc1 = getEncoded();
+ byte[] enc2 = ((X509CRL)other).getEncoded();
+
+ return MessageDigest.isEqual(enc1, enc2);
+ }
+ catch (CRLException e)
+ {
+ return false;
+ }
+ }
+
+ public int hashCode()
+ {
+ int hashcode = 0;
+
+ try
+ {
+ byte[] encoded = getEncoded();
+ for (int i = 1; i < encoded.length; i++)
+ {
+ hashcode += encoded[i] * i;
+ }
+ }
+ catch (CRLException ce)
+ {
+ return(hashcode);
+ }
+
+ return(hashcode);
+ }
+
+ public abstract byte[] getEncoded() throws CRLException;
+ public abstract Principal getIssuerDN();
+ public abstract Date getNextUpdate();
+ public abstract X509CRLEntry getRevokedCertificate(BigInteger serialNumber);
+ public abstract Set getRevokedCertificates();
+ public abstract String getSigAlgName();
+ public abstract String getSigAlgOID();
+ public abstract byte[] getSigAlgParams();
+ public abstract byte[] getSignature();
+ public abstract byte[] getTBSCertList() throws CRLException;
+ public abstract Date getThisUpdate();
+ public abstract int getVersion();
+ public abstract void verify(PublicKey key) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException;
+ public abstract void verify(PublicKey key, String sigProvider) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException;
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRLEntry.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRLEntry.java
new file mode 100644
index 000000000..bb0d78074
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRLEntry.java
@@ -0,0 +1,56 @@
+
+package java.security.cert;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.util.Date;
+
+public abstract class X509CRLEntry implements X509Extension
+{
+ public boolean equals(Object other)
+ {
+ if ( this == other )
+ return true;
+
+ if ( !(other instanceof X509CRLEntry) )
+ return false;
+
+ try
+ {
+ byte[] enc1 = getEncoded();
+ byte[] enc2 = ((X509CRLEntry)other).getEncoded();
+
+ return MessageDigest.isEqual(enc1, enc2);
+ }
+ catch (CRLException e)
+ {
+ return false;
+ }
+ }
+
+ public int hashCode()
+ {
+ int hashcode = 0;
+
+ try
+ {
+ byte[] encoded = getEncoded();
+ for (int i = 1; i < encoded.length; i++)
+ {
+ hashcode += encoded[i] * i;
+ }
+ }
+ catch (CRLException ce)
+ {
+ return(hashcode);
+ }
+
+ return(hashcode);
+ }
+
+ public abstract byte[] getEncoded() throws CRLException;
+ public abstract Date getRevocationDate();
+ public abstract BigInteger getSerialNumber();
+ public abstract boolean hasExtensions();
+ public abstract String toString();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRLSelector.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRLSelector.java
new file mode 100644
index 000000000..7971e7b61
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CRLSelector.java
@@ -0,0 +1,717 @@
+package java.security.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.CRL;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERInteger;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.jce.PrincipalUtil;
+
+/**
+ * A CRLSelector
that selects X509CRLs
that match
+ * all specified criteria. This class is particularly useful when selecting CRLs
+ * from a CertStore
to check revocation status of a particular
+ * certificate.
+ *
+ * When first constructed, an X509CRLSelector
has no criteria
+ * enabled and each of the get
methods return a default value (null
).
+ * Therefore, the {@link #match match} method would return true
+ * for any X509CRL
. Typically, several criteria are enabled (by
+ * calling {@link #setIssuerNames setIssuerNames} or
+ * {@link #setDateAndTime setDateAndTime}, for instance) and then the
+ * X509CRLSelector
is passed to
+ * {@link CertStore#getCRLs CertStore.getCRLs} or some similar method.
+ *
+ * Please refer to RFC 2459 for definitions of the X.509 CRL fields and
+ * extensions mentioned below.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are not
+ * thread-safe. Multiple threads that need to access a single object
+ * concurrently should synchronize amongst themselves and provide the necessary
+ * locking. Multiple threads each manipulating separate objects need not
+ * synchronize.
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
+ * {@link org.spongycastle.asn1.DEROutputStream DEROutputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name}
+ *
+ * @see CRLSelector
+ * @see X509CRL
+ */
+public class X509CRLSelector implements CRLSelector
+{
+ private Set issuerNames = null;
+
+ private Set issuerNamesX509 = null;
+
+ private BigInteger minCRL = null;
+
+ private BigInteger maxCRL = null;
+
+ private Date dateAndTime = null;
+
+ private X509Certificate certChecking = null;
+
+ /**
+ * Creates an X509CRLSelector
. Initially, no criteria are
+ * set so any X509CRL
will match.
+ */
+ public X509CRLSelector()
+ {
+ }
+
+ /**
+ * Sets the issuerNames criterion. The issuer distinguished name in the
+ * X509CRL
must match at least one of the specified
+ * distinguished names. If null
, any issuer distinguished
+ * name will do.
+ *
+ * This method allows the caller to specify, with a single method call, the
+ * complete set of issuer names which X509CRLs
may contain.
+ * The specified value replaces the previous value for the issuerNames
+ * criterion.
+ *
+ * The names
parameter (if not null
) is a
+ * Collection
of names. Each name is a String
+ * or a byte array representing a distinguished name (in RFC 2253 or ASN.1
+ * DER encoded form, respectively). If null
is supplied as
+ * the value for this argument, no issuerNames check will be performed.
+ *
+ * Note that the names
parameter can contain duplicate
+ * distinguished names, but they may be removed from the
+ * Collection
of names returned by the
+ * {@link #getIssuerNames getIssuerNames} method.
+ *
+ * If a name is specified as a byte array, it should contain a single DER
+ * encoded distinguished name, as defined in X.501. The ASN.1 notation for
+ * this structure is as follows.
+ *
+ *
+ *
+ *
+ * Name ::= CHOICE {
+ * RDNSequence }
+ *
+ * RDNSequence ::= SEQUENCE OF RDN
+ *
+ * RDN ::=
+ * SET SIZE (1 .. MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ *
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ * ....
+ * DirectoryString ::= CHOICE {
+ * teletexString TeletexString (SIZE (1..MAX)),
+ * printableString PrintableString (SIZE (1..MAX)),
+ * universalString UniversalString (SIZE (1..MAX)),
+ * utf8String UTF8String (SIZE (1.. MAX)),
+ * bmpString BMPString (SIZE (1..MAX)) }
+ *
+ *
+ * Note that a deep copy is performed on the Collection
to
+ * protect against subsequent modifications.
+ *
+ * @param names
+ * a Collection
of names (or null
)
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ *
+ * @see #getIssuerNames
+ */
+ public void setIssuerNames(Collection names) throws IOException
+ {
+ if (names == null || names.isEmpty())
+ {
+ issuerNames = null;
+ issuerNamesX509 = null;
+ }
+ else
+ {
+ Object item;
+ Iterator iter = names.iterator();
+ while (iter.hasNext())
+ {
+ item = iter.next();
+ if (item instanceof String)
+ {
+ addIssuerName((String)item);
+ }
+ else if (item instanceof byte[])
+ {
+ addIssuerName((byte[])item);
+ }
+ else
+ {
+ throw new IOException("name not byte[]or String: "
+ + item.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a name to the issuerNames criterion. The issuer distinguished name
+ * in the X509CRL
must match at least one of the specified
+ * distinguished names.
+ *
+ * This method allows the caller to add a name to the set of issuer names
+ * which X509CRLs
may contain. The specified name is added to
+ * any previous value for the issuerNames criterion. If the specified name
+ * is a duplicate, it may be ignored.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.X509Name X509Name} for parsing the
+ * name
+ *
+ * @param name
+ * the name in RFC 2253 form
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ */
+ public void addIssuerName(String name) throws IOException
+ {
+ if (issuerNames == null)
+ {
+ issuerNames = new HashSet();
+ issuerNamesX509 = new HashSet();
+ }
+ X509Name nameX509;
+ try
+ {
+ nameX509 = new X509Name(name);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ throw new IOException(ex.getMessage());
+ }
+ issuerNamesX509.add(nameX509);
+ issuerNames.add(name);
+ }
+
+ /**
+ * Adds a name to the issuerNames criterion. The issuer distinguished name
+ * in the X509CRL
must match at least one of the specified
+ * distinguished names.
+ *
+ * This method allows the caller to add a name to the set of issuer names
+ * which X509CRLs
may contain. The specified name is added to
+ * any previous value for the issuerNames criterion. If the specified name
+ * is a duplicate, it may be ignored. If a name is specified as a byte
+ * array, it should contain a single DER encoded distinguished name, as
+ * defined in X.501. The ASN.1 notation for this structure is as follows.
+ *
+ * The name is provided as a byte array. This byte array should contain a
+ * single DER encoded distinguished name, as defined in X.501. The ASN.1
+ * notation for this structure appears in the documentation for
+ * {@link #setIssuerNames setIssuerNames(Collection names)}.
+ *
+ * Note that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.X509Name X509Name} for parsing the
+ * name, {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object} and
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence}
+ *
+ * @param name
+ * a byte array containing the name in ASN.1 DER encoded form
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ */
+ public void addIssuerName(byte[] name) throws IOException
+ {
+ if (issuerNames == null)
+ {
+ issuerNames = new HashSet();
+ issuerNamesX509 = new HashSet();
+ }
+
+ ByteArrayInputStream inStream = new ByteArrayInputStream(name);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object obj = derInStream.readObject();
+ if (obj instanceof ASN1Sequence)
+ {
+ issuerNamesX509.add(new X509Name((ASN1Sequence)obj));
+ }
+ else
+ {
+ throw new IOException("parsing error");
+ }
+ issuerNames.add(name.clone());
+ }
+
+ /**
+ * Sets the minCRLNumber criterion. The X509CRL
must have a
+ * CRL number extension whose value is greater than or equal to the
+ * specified value. If null
, no minCRLNumber check will be
+ * done.
+ *
+ * @param minCRL
+ * the minimum CRL number accepted (or null
)
+ */
+ public void setMinCRLNumber(BigInteger minCRL)
+ {
+ this.minCRL = minCRL;
+ }
+
+ /**
+ * Sets the maxCRLNumber criterion. The X509CRL
must have a
+ * CRL number extension whose value is less than or equal to the specified
+ * value. If null
, no maxCRLNumber check will be done.
+ *
+ * @param maxCRL
+ * the maximum CRL number accepted (or null
)
+ */
+ public void setMaxCRLNumber(BigInteger maxCRL)
+ {
+ this.maxCRL = maxCRL;
+ }
+
+ /**
+ * Sets the dateAndTime criterion. The specified date must be equal to or
+ * later than the value of the thisUpdate component of the
+ * X509CRL
and earlier than the value of the nextUpdate
+ * component. There is no match if the X509CRL
does not
+ * contain a nextUpdate component. If null
, no dateAndTime
+ * check will be done.
+ *
+ * Note that the Date
supplied here is cloned to protect
+ * against subsequent modifications.
+ *
+ * @param dateAndTime
+ * the Date
to match against (or null
)
+ *
+ * @see #getDateAndTime
+ */
+ public void setDateAndTime(Date dateAndTime)
+ {
+ if (dateAndTime == null)
+ {
+ this.dateAndTime = null;
+ }
+ else
+ {
+ this.dateAndTime = new Date(dateAndTime.getTime());
+ }
+ }
+
+ /**
+ * Sets the certificate being checked. This is not a criterion. Rather, it
+ * is optional information that may help a CertStore
find
+ * CRLs that would be relevant when checking revocation for the specified
+ * certificate. If null
is specified, then no such optional
+ * information is provided.
+ *
+ * @param cert
+ * the X509Certificate
being checked (or
+ * null
)
+ *
+ * @see #getCertificateChecking
+ */
+ public void setCertificateChecking(X509Certificate cert)
+ {
+ certChecking = cert;
+ }
+
+ /**
+ * Returns a copy of the issuerNames criterion. The issuer distinguished
+ * name in the X509CRL
must match at least one of the
+ * specified distinguished names. If the value returned is null
,
+ * any issuer distinguished name will do.
+ *
+ * If the value returned is not null
, it is a
+ * Collection
of names. Each name is a String
+ * or a byte array representing a distinguished name (in RFC 2253 or ASN.1
+ * DER encoded form, respectively). Note that the Collection
+ * returned may contain duplicate names.
+ *
+ * If a name is specified as a byte array, it should contain a single DER
+ * encoded distinguished name, as defined in X.501. The ASN.1 notation for
+ * this structure is given in the documentation for
+ * {@link #setIssuerNames setIssuerNames(Collection names)}.
+ *
+ * Note that a deep copy is performed on the Collection
to
+ * protect against subsequent modifications.
+ *
+ * @return a Collection
of names (or null
)
+ * @see #setIssuerNames
+ */
+ public Collection getIssuerNames()
+ {
+ if (issuerNames == null)
+ {
+ return null;
+ }
+
+ Collection set = new HashSet();
+ Iterator iter = issuerNames.iterator();
+ Object item;
+ while (iter.hasNext())
+ {
+ item = iter.next();
+ if (item instanceof String)
+ {
+ set.add(new String((String)item));
+ }
+ else if (item instanceof byte[])
+ {
+ set.add(((byte[])item).clone());
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Returns the minCRLNumber criterion. The X509CRL
must have
+ * a CRL number extension whose value is greater than or equal to the
+ * specified value. If null
, no minCRLNumber check will be
+ * done.
+ *
+ * @return the minimum CRL number accepted (or null
)
+ */
+ public BigInteger getMinCRL()
+ {
+ return minCRL;
+ }
+
+ /**
+ * Returns the maxCRLNumber criterion. The X509CRL
must have
+ * a CRL number extension whose value is less than or equal to the specified
+ * value. If null
, no maxCRLNumber check will be done.
+ *
+ * @return the maximum CRL number accepted (or null
)
+ */
+ public BigInteger getMaxCRL()
+ {
+ return maxCRL;
+ }
+
+ /**
+ * Returns the dateAndTime criterion. The specified date must be equal to or
+ * later than the value of the thisUpdate component of the
+ * X509CRL
and earlier than the value of the nextUpdate
+ * component. There is no match if the X509CRL
does not
+ * contain a nextUpdate component. If null
, no dateAndTime
+ * check will be done.
+ *
+ * Note that the Date
returned is cloned to protect against
+ * subsequent modifications.
+ *
+ * @return the Date
to match against (or null
)
+ *
+ * @see #setDateAndTime
+ */
+ public Date getDateAndTime()
+ {
+ if (dateAndTime == null)
+ {
+ return null;
+ }
+
+ return new Date(dateAndTime.getTime());
+ }
+
+ /**
+ * Returns the certificate being checked. This is not a criterion. Rather,
+ * it is optional information that may help a CertStore
find
+ * CRLs that would be relevant when checking revocation for the specified
+ * certificate. If the value returned is null
, then no such
+ * optional information is provided.
+ *
+ * @return the certificate being checked (or null
)
+ *
+ * @see #setCertificateChecking
+ */
+ public X509Certificate getCertificateChecking()
+ {
+ return certChecking;
+ }
+
+ /**
+ * Returns a printable representation of the X509CRLSelector
.
+ *
+ * Uses
+ * {@link org.spongycastle.asn1.x509.X509Name#toString X509Name.toString} to
+ * format the output
+ *
+ * @return a String
describing the contents of the
+ * X509CRLSelector
.
+ */
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append("X509CRLSelector: [\n");
+ if (issuerNamesX509 != null)
+ {
+ s.append(" IssuerNames:\n");
+ Iterator iter = issuerNamesX509.iterator();
+ while (iter.hasNext())
+ {
+ s.append(" ").append(iter.next()).append('\n');
+ }
+ }
+ if (minCRL != null)
+ {
+ s.append(" minCRLNumber: ").append(minCRL).append('\n');
+ }
+ if (maxCRL != null)
+ {
+ s.append(" maxCRLNumber: ").append(maxCRL).append('\n');
+ }
+ if (dateAndTime != null)
+ {
+ s.append(" dateAndTime: ").append(dateAndTime).append('\n');
+ }
+ if (certChecking != null)
+ {
+ s.append(" Certificate being checked: ").append(certChecking).append('\n');
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ /**
+ * Decides whether a CRL
should be selected.
+ *
+ * Uses
+ * {@link org.spongycastle.asn1.x509.X509Name#toString X509Name.toString} to
+ * parse and to compare the crl parameter issuer and
+ * {@link org.spongycastle.asn1.x509.X509Extensions#CRLNumber CRLNumber} to
+ * access the CRL number extension.
+ *
+ * @param crl
+ * the CRL
to be checked
+ *
+ * @return true
if the CRL
should be selected,
+ * false
otherwise
+ */
+ public boolean match(CRL crl)
+ {
+ if (!(crl instanceof X509CRL))
+ {
+ return false;
+ }
+
+ X509CRL crlX509 = (X509CRL)crl;
+ boolean test;
+
+ if (issuerNamesX509 != null)
+ {
+ Iterator iter = issuerNamesX509.iterator();
+ test = false;
+ X509Name crlIssuer = null;
+ try
+ {
+ crlIssuer = PrincipalUtil.getIssuerX509Principal(crlX509);
+ }
+ catch (Exception ex)
+ {
+
+ return false;
+ }
+
+ while (iter.hasNext())
+ {
+ if (crlIssuer.equals(iter.next(), true))
+ {
+ test = true;
+ break;
+ }
+ }
+ if (!test)
+ {
+ return false;
+ }
+ }
+
+ byte[] data = crlX509.getExtensionValue(X509Extensions.CRLNumber
+ .getId());
+ if (data != null)
+ {
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ inStream = new ByteArrayInputStream(
+ ((ASN1OctetString)derInputStream.readObject())
+ .getOctets());
+ derInputStream = new ASN1InputStream(inStream);
+ BigInteger crlNumber = ((DERInteger)derInputStream.readObject())
+ .getPositiveValue();
+ if (minCRL != null && minCRL.compareTo(crlNumber) > 0)
+ {
+ return false;
+ }
+ if (maxCRL != null && maxCRL.compareTo(crlNumber) < 0)
+ {
+ return false;
+ }
+ }
+ catch (IOException ex)
+ {
+ return false;
+ }
+ }
+ else if (minCRL != null || maxCRL != null)
+ {
+ return false;
+ }
+
+ if (dateAndTime != null)
+ {
+ Date check = crlX509.getThisUpdate();
+ if (check == null)
+ {
+ return false;
+ }
+ else if (dateAndTime.before(check))
+ {
+ return false;
+ }
+
+ check = crlX509.getNextUpdate();
+ if (check == null)
+ {
+ return false;
+ }
+ else if (!dateAndTime.before(check))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a copy of this object.
+ *
+ * @return the copy
+ */
+ public Object clone()
+ {
+ try
+ {
+ X509CRLSelector copy = (X509CRLSelector)super.clone();
+ if (issuerNames != null)
+ {
+ copy.issuerNames = new HashSet();
+ Iterator iter = issuerNames.iterator();
+ Object obj;
+ while (iter.hasNext())
+ {
+ obj = iter.next();
+ if (obj instanceof byte[])
+ {
+ copy.issuerNames.add(((byte[])obj).clone());
+ }
+ else
+ {
+ copy.issuerNames.add(obj);
+ }
+ }
+ copy.issuerNamesX509 = new HashSet(issuerNamesX509);
+ }
+ return copy;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ /* Cannot happen */
+ throw new InternalError(e.toString());
+ }
+ }
+
+ /**
+ * Decides whether a CRL
should be selected.
+ *
+ * @param crl
+ * the CRL
to be checked
+ *
+ * @return true
if the CRL
should be selected,
+ * false
otherwise
+ */
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof X509CRLSelector))
+ {
+ return false;
+ }
+
+ X509CRLSelector equalsCRL = (X509CRLSelector)obj;
+
+ if (!equals(dateAndTime, equalsCRL.dateAndTime))
+ {
+ return false;
+ }
+
+ if (!equals(minCRL, equalsCRL.minCRL))
+ {
+ return false;
+ }
+
+ if (!equals(maxCRL, equalsCRL.maxCRL))
+ {
+ return false;
+ }
+
+ if (!equals(issuerNamesX509, equalsCRL.issuerNamesX509))
+ {
+ return false;
+ }
+
+ if (!equals(certChecking, equalsCRL.certChecking))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Return true
if two Objects are unequal.
+ * This means that one is null
and the other is
+ * not or obj1.equals(obj2)
returns
+ * false
.
+ **/
+ private boolean equals(Object obj1, Object obj2)
+ {
+ if (obj1 == null)
+ {
+ if (obj2 != null)
+ {
+ return true;
+ }
+ }
+ else if (!obj1.equals(obj2))
+ {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CertSelector.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CertSelector.java
new file mode 100644
index 000000000..89767cde5
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509CertSelector.java
@@ -0,0 +1,2461 @@
+package java.security.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.util.ASN1Dump;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.ExtendedKeyUsage;
+import org.spongycastle.asn1.x509.KeyPurposeId;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.jce.PrincipalUtil;
+import org.spongycastle.util.Integers;
+
+/**
+ * A CertSelector
that selects
+ * X509Certificates that match all
+ * specified criteria. This class is particularly useful when
+ * selecting certificates from a CertStore to build a PKIX-compliant
+ * certification path.
+ *
+ * When first constructed, an X509CertSelector
has no criteria enabled
+ * and each of the get methods return a default value (null
, or -1 for
+ * the {@link #getBasicConstraints} method). Therefore, the {@link #match} method would
+ * return true for any X509Certificate
. Typically, several criteria
+ * are enabled (by calling {@link #setIssuer} or {@link #setKeyUsage}, for instance) and
+ * then the X509CertSelector
is passed to {@link CertStore#getCertificates} or
+ * some similar method.
+ *
+ * Several criteria can be enabled (by calling {@link #setIssuer} and
+ * {@link #setSerialNumber}, for example) such that the match method usually
+ * uniquely matches a single X509Certificate
. We say usually, since it
+ * is possible for two issuing CAs to have the same distinguished name
+ * and each issue a certificate with the same serial number. Other
+ * unique combinations include the issuer, subject,
+ * subjectKeyIdentifier and/or the subjectPublicKey criteria.
+ *
+ * Please refer to RFC 2459 for definitions of the X.509 certificate
+ * extensions mentioned below.
+ *
+ * Concurrent Access
+ *
+ * Unless otherwise specified, the methods defined in this class are
+ * not thread-safe. Multiple threads that need to access a single
+ * object concurrently should synchronize amongst themselves and
+ * provide the necessary locking. Multiple threads each manipulating
+ * separate objects need not synchronize.
+ *
+ * TODO: implement name constraints
+ * TODO: implement match check for path to names
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
+ * {@link org.spongycastle.asn1.DEROutputStream DEROutputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object},
+ * {@link org.spongycastle.asn1.OIDTokenizer OIDTokenizer},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name},
+ * {@link org.spongycastle.asn1.x509.X509Extensions X509Extensions},
+ * {@link org.spongycastle.asn1.x509.ExtendedKeyUsage ExtendedKeyUsage},
+ * {@link org.spongycastle.asn1.x509.KeyPurposeId KeyPurposeId},
+ * {@link org.spongycastle.asn1.x509.SubjectPublicKeyInfo SubjectPublicKeyInfo},
+ * {@link org.spongycastle.asn1.x509.AlgorithmIdentifier AlgorithmIdentifier}
+ */
+public class X509CertSelector implements CertSelector
+{
+ private static final Hashtable keyPurposeIdMap = new Hashtable();
+ static
+ {
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_serverAuth.getId(),
+ KeyPurposeId.id_kp_serverAuth);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_clientAuth.getId(),
+ KeyPurposeId.id_kp_clientAuth);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_codeSigning.getId(),
+ KeyPurposeId.id_kp_codeSigning);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_emailProtection.getId(),
+ KeyPurposeId.id_kp_emailProtection);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecEndSystem.getId(),
+ KeyPurposeId.id_kp_ipsecEndSystem);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecTunnel.getId(),
+ KeyPurposeId.id_kp_ipsecTunnel);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecUser.getId(),
+ KeyPurposeId.id_kp_ipsecUser);
+ keyPurposeIdMap.put(KeyPurposeId.id_kp_timeStamping.getId(),
+ KeyPurposeId.id_kp_timeStamping);
+ }
+
+ private X509Certificate x509Cert = null;
+
+ private BigInteger serialNumber = null;
+
+ private Object issuerDN = null;
+
+ private X509Name issuerDNX509 = null;
+
+ private Object subjectDN = null;
+
+ private X509Name subjectDNX509 = null;
+
+ private byte[] subjectKeyID = null;
+
+ private byte[] authorityKeyID = null;
+
+ private Date certValid = null;
+
+ private Date privateKeyValid = null;
+
+ private ASN1ObjectIdentifier subjectKeyAlgID = null;
+
+ private PublicKey subjectPublicKey = null;
+
+ private byte[] subjectPublicKeyByte = null;
+
+ private boolean[] keyUsage = null;
+
+ private Set keyPurposeSet = null;
+
+ private boolean matchAllSubjectAltNames = true;
+
+ private Set subjectAltNames = null;
+
+ private Set subjectAltNamesByte = null;
+
+ private int minMaxPathLen = -1;
+
+ private Set policy = null;
+
+ private Set policyOID = null;
+
+ private Set pathToNames = null;
+
+ private Set pathToNamesByte = null;
+
+ /**
+ * Creates an X509CertSelector
. Initially, no criteria are
+ * set so any X509Certificate
will match.
+ */
+ public X509CertSelector()
+ {
+ }
+
+ /**
+ * Sets the certificateEquals criterion. The specified
+ * X509Certificate
must be equal to the
+ * X509Certificate
passed to the match method. If
+ * null
, then this check is not applied.
+ *
+ * This method is particularly useful when it is necessary to match a single
+ * certificate. Although other criteria can be specified in conjunction with
+ * the certificateEquals criterion, it is usually not practical or
+ * necessary.
+ *
+ * @param cert
+ * the X509Certificate to match (or null
)
+ *
+ * @see #getCertificate()
+ */
+ public void setCertificate(X509Certificate cert)
+ {
+ x509Cert = cert;
+ }
+
+ /**
+ * Sets the serialNumber criterion. The specified serial number must match
+ * the certificate serial number in the X509Certificate
. If
+ * null
, any certificate serial number will do.
+ *
+ * @param serial
+ * the certificate serial number to match (or null
)
+ *
+ * @see #getSerialNumber()
+ */
+ public void setSerialNumber(BigInteger serial)
+ {
+ serialNumber = serial;
+ }
+
+ /**
+ * Sets the issuer criterion. The specified distinguished name must match
+ * the issuer distinguished name in the X509Certificate
. If
+ * null
, any issuer distinguished name will do.
+ *
+ * If issuerDN
is not null
, it should contain
+ * a distinguished name, in RFC 2253 format.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.X509Name X509Name} for parsing the
+ * issuerDN.
+ *
+ * @param issuerDN
+ * a distinguished name in RFC 2253 format (or null
)
+ *
+ * @exception IOException
+ * if a parsing error occurs (incorrect form for DN)
+ */
+ public void setIssuer(String issuerDN) throws IOException
+ {
+ if (issuerDN == null)
+ {
+ this.issuerDN = null;
+ this.issuerDNX509 = null;
+ }
+ else
+ {
+ X509Name nameX509;
+ try
+ {
+ nameX509 = new X509Name(issuerDN);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ throw new IOException(ex.getMessage());
+ }
+ this.issuerDNX509 = nameX509;
+ this.issuerDN = issuerDN;
+ }
+ }
+
+ /**
+ * Sets the issuer criterion. The specified distinguished name must match
+ * the issuer distinguished name in the X509Certificate
. If
+ * null is specified, the issuer criterion is disabled and any issuer
+ * distinguished name will do.
+ *
+ * If issuerDN
is not null
, it should contain
+ * a single DER encoded distinguished name, as defined in X.501. The ASN.1
+ * notation for this structure is as follows.
+ *
+ *
+ *
+ * Name ::= CHOICE {
+ * RDNSequence }
+ *
+ * RDNSequence ::= SEQUENCE OF RDN
+ *
+ * RDN ::=
+ * SET SIZE (1 .. MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ *
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ * ....
+ * DirectoryString ::= CHOICE {
+ * teletexString TeletexString (SIZE (1..MAX)),
+ * printableString PrintableString (SIZE (1..MAX)),
+ * universalString UniversalString (SIZE (1..MAX)),
+ * utf8String UTF8String (SIZE (1.. MAX)),
+ * bmpString BMPString (SIZE (1..MAX)) }
+ *
+ *
+ *
+ *
+ * Note that the byte array specified here is cloned to protect against
+ * subsequent modifications.
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name}
+ *
+ * @param issuerDN -
+ * a byte array containing the distinguished name in ASN.1 DER
+ * encoded form (or null
)
+ *
+ * @exception IOException
+ * if an encoding error occurs (incorrect form for DN)
+ */
+ public void setIssuer(byte[] issuerDN) throws IOException
+ {
+ if (issuerDN == null)
+ {
+ this.issuerDN = null;
+ this.issuerDNX509 = null;
+ }
+ else
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(issuerDN);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object obj = derInStream.readObject();
+ if (obj instanceof ASN1Sequence)
+ {
+ this.issuerDNX509 = new X509Name((ASN1Sequence)obj);
+ }
+ else
+ {
+ throw new IOException("parsing error");
+ }
+ this.issuerDN = (byte[])issuerDN.clone();
+ }
+ }
+
+ /**
+ * Sets the subject criterion. The specified distinguished name must match
+ * the subject distinguished name in the X509Certificate
. If
+ * null, any subject distinguished name will do.
+ *
+ * If subjectDN
is not null
, it should
+ * contain a distinguished name, in RFC 2253 format.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.X509Name X509Name} for parsing the
+ * subjectDN.
+ *
+ * @param subjectDN
+ * a distinguished name in RFC 2253 format (or null
)
+ *
+ * @exception IOException
+ * if a parsing error occurs (incorrect form for DN)
+ */
+ public void setSubject(String subjectDN) throws IOException
+ {
+ if (subjectDN == null)
+ {
+ this.subjectDN = null;
+ this.subjectDNX509 = null;
+ }
+ else
+ {
+ X509Name nameX509;
+ try
+ {
+ nameX509 = new X509Name(subjectDN);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ throw new IOException(ex.getMessage());
+ }
+
+ this.subjectDNX509 = nameX509;
+ this.subjectDN = subjectDN;
+ }
+ }
+
+ /**
+ * Sets the subject criterion. The specified distinguished name must match
+ * the subject distinguished name in the X509Certificate
. If
+ * null, any subject distinguished name will do.
+ *
+ * If subjectDN
is not null
, it should
+ * contain a single DER encoded distinguished name, as defined in X.501. For
+ * the ASN.1 notation for this structure, see
+ * {@link #setIssuer(byte []) setIssuer(byte [] issuerDN)}.
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name}
+ *
+ * @param subjectDN
+ * a byte array containing the distinguished name in ASN.1 DER
+ * format (or null
)
+ *
+ * @exception IOException
+ * if an encoding error occurs (incorrect form for DN)
+ */
+ public void setSubject(byte[] subjectDN) throws IOException
+ {
+ if (subjectDN == null)
+ {
+ this.subjectDN = null;
+ this.subjectDNX509 = null;
+ }
+ else
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(subjectDN);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object obj = derInStream.readObject();
+
+ if (obj instanceof ASN1Sequence)
+ {
+ this.subjectDNX509 = new X509Name((ASN1Sequence)obj);
+ }
+ else
+ {
+ throw new IOException("parsing error");
+ }
+ this.subjectDN = (byte[])subjectDN.clone();
+ }
+ }
+
+ /**
+ * Sets the subjectKeyIdentifier criterion. The X509Certificate
+ * must contain a SubjectKeyIdentifier extension for which the contents of
+ * the extension matches the specified criterion value. If the criterion
+ * value is null, no subjectKeyIdentifier check will be done.
+ *
+ * If subjectKeyID
is not null
, it should
+ * contain a single DER encoded value corresponding to the contents of the
+ * extension value (not including the object identifier, criticality
+ * setting, and encapsulating OCTET STRING) for a SubjectKeyIdentifier
+ * extension. The ASN.1 notation for this structure follows.
+ *
+ *
+ *
+ * SubjectKeyIdentifier ::= KeyIdentifier
+ *
+ * KeyIdentifier ::= OCTET STRING
+ *
+ *
+ *
+ *
+ * Since the format of subject key identifiers is not mandated by any
+ * standard, subject key identifiers are not parsed by the
+ * X509CertSelector
. Instead, the values are compared using
+ * a byte-by-byte comparison.
+ *
+ * Note that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * @param subjectKeyID -
+ * the subject key identifier (or null
)
+ *
+ * @see #getSubjectKeyIdentifier()
+ */
+ public void setSubjectKeyIdentifier(byte[] subjectKeyID)
+ {
+ if (subjectKeyID == null)
+ {
+ this.subjectKeyID = null;
+ }
+ else
+ {
+ this.subjectKeyID = (byte[])subjectKeyID.clone();
+ }
+ }
+
+ /**
+ * Sets the authorityKeyIdentifier criterion. The
+ * X509Certificate
must contain an AuthorityKeyIdentifier
+ * extension for which the contents of the extension value matches the
+ * specified criterion value. If the criterion value is null
,
+ * no authorityKeyIdentifier check will be done.
+ *
+ * If authorityKeyID
is not null
, it should
+ * contain a single DER encoded value corresponding to the contents of the
+ * extension value (not including the object identifier, criticality
+ * setting, and encapsulating OCTET STRING) for an AuthorityKeyIdentifier
+ * extension. The ASN.1 notation for this structure follows.
+ *
+ *
+ *
+ * AuthorityKeyIdentifier ::= SEQUENCE {
+ * keyIdentifier [0] KeyIdentifier OPTIONAL,
+ * authorityCertIssuer [1] GeneralNames OPTIONAL,
+ * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
+ *
+ * KeyIdentifier ::= OCTET STRING
+ *
+ *
+ *
+ *
+ * Authority key identifiers are not parsed by the
+ * X509CertSelector
. Instead, the values are compared using
+ * a byte-by-byte comparison.
+ *
+ * When the keyIdentifier
field of
+ * AuthorityKeyIdentifier
is populated, the value is usually
+ * taken from the SubjectKeyIdentifier extension in the issuer's
+ * certificate. Note, however, that the result of
+ * X509Certificate.getExtensionValue(
+ *
+ * Note also that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * @param authorityKeyID
+ * the authority key identifier (or null
)
+ *
+ * @see #getAuthorityKeyIdentifier()
+ */
+ public void setAuthorityKeyIdentifier(byte[] authorityKeyID)
+ {
+ if (authorityKeyID == null)
+ {
+ this.authorityKeyID = null;
+ }
+ else
+ {
+ this.authorityKeyID = (byte[])authorityKeyID.clone();
+ }
+ }
+
+ /**
+ * Sets the certificateValid criterion. The specified date must fall within
+ * the certificate validity period for the X509Certificate. If
+ * null
, no certificateValid check will be done.
+ *
+ * Note that the Date supplied here is cloned to protect against subsequent
+ * modifications.
+ *
+ * @param certValid
+ * the Date to check (or null
)
+ *
+ * @see #getCertificateValid()
+ */
+ public void setCertificateValid(Date certValid)
+ {
+ if (certValid == null)
+ {
+ this.certValid = null;
+ }
+ else
+ {
+ this.certValid = new Date(certValid.getTime());
+ }
+ }
+
+ /**
+ * Sets the privateKeyValid criterion. The specified date must fall within
+ * the private key validity period for the X509Certificate. If
+ * null
, no privateKeyValid check will be done.
+ *
+ * Note that the Date supplied here is cloned to protect against subsequent
+ * modifications.
+ *
+ * @param privateKeyValid
+ * the Date to check (or null
)
+ *
+ * @see #getPrivateKeyValid()
+ */
+ public void setPrivateKeyValid(Date privateKeyValid)
+ {
+ if (privateKeyValid == null)
+ {
+ this.privateKeyValid = null;
+ }
+ else
+ {
+ this.privateKeyValid = new Date(privateKeyValid.getTime());
+ }
+ }
+
+ /**
+ * Sets the subjectPublicKeyAlgID criterion. The X509Certificate must
+ * contain a subject public key with the specified algorithm. If
+ * null
, no subjectPublicKeyAlgID check will be done.
+ *
+ * @param oid
+ * The object identifier (OID) of the algorithm to check for (or
+ * null
). An OID is represented by a set of
+ * nonnegative integers separated by periods.
+ *
+ * @exception IOException
+ * if the OID is invalid, such as the first component being
+ * not 0, 1 or 2 or the second component being greater than
+ * 39.
+ *
+ * @see #getSubjectPublicKeyAlgID()
+ */
+ public void setSubjectPublicKeyAlgID(String oid) throws IOException
+ {
+ CertUtil.parseOID(oid);
+ subjectKeyAlgID = new ASN1ObjectIdentifier(oid);
+ }
+
+ /**
+ * Sets the subjectPublicKey criterion. The X509Certificate must contain the
+ * specified subject public key. If null, no subjectPublicKey check will be
+ * done.
+ *
+ * @param key
+ * the subject public key to check for (or null)
+ *
+ * @see #getSubjectPublicKey()
+ */
+ public void setSubjectPublicKey(PublicKey key)
+ {
+ if (key == null)
+ {
+ subjectPublicKey = null;
+ subjectPublicKeyByte = null;
+ }
+ else
+ {
+ subjectPublicKey = key;
+ subjectPublicKeyByte = key.getEncoded();
+ }
+ }
+
+ /**
+ * Sets the subjectPublicKey criterion. The X509Certificate
+ * must contain the specified subject public key. If null
,
+ * no subjectPublicKey check will be done.
+ *
+ * Because this method allows the public key to be specified as a byte
+ * array, it may be used for unknown key types.
+ *
+ * If key is not null
, it should contain a single DER
+ * encoded SubjectPublicKeyInfo structure, as defined in X.509. The ASN.1
+ * notation for this structure is as follows.
+ *
+ *
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ * -- contains a value of the type
+ * -- registered for use with the
+ * -- algorithm object identifier value
+ *
+ *
+ *
+ *
+ * Note that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * @param key
+ * a byte array containing the subject public key in ASN.1 DER
+ * form (or null
)
+ *
+ * @exception IOException
+ * if an encoding error occurs (incorrect form for subject
+ * public key)
+ *
+ * @see #getSubjectPublicKey()
+ */
+ public void setSubjectPublicKey(byte[] key) throws IOException
+ {
+ if (key == null)
+ {
+ subjectPublicKey = null;
+ subjectPublicKeyByte = null;
+ }
+ else
+ {
+ subjectPublicKey = null;
+ subjectPublicKeyByte = (byte[])key.clone();
+ // TODO
+ // try to generyte PublicKey Object from subjectPublicKeyByte
+ }
+ }
+
+ /**
+ * Sets the keyUsage criterion. The X509Certificate must allow the specified
+ * keyUsage values. If null, no keyUsage check will be done. Note that an
+ * X509Certificate that has no keyUsage extension implicitly allows all
+ * keyUsage values.
+ *
+ * Note that the boolean array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * @param keyUsage
+ * a boolean array in the same format as the boolean array
+ * returned by X509Certificate.getKeyUsage(). Or
+ * null
.
+ *
+ * @see #getKeyUsage()
+ */
+ public void setKeyUsage(boolean[] keyUsage)
+ {
+ if (keyUsage == null)
+ {
+ this.keyUsage = null;
+ }
+ else
+ {
+ this.keyUsage = (boolean[])keyUsage.clone();
+ }
+ }
+
+ /**
+ * Sets the extendedKeyUsage criterion. The X509Certificate
+ * must allow the specified key purposes in its extended key usage
+ * extension. If keyPurposeSet
is empty or null
,
+ * no extendedKeyUsage check will be done. Note that an
+ * X509Certificate
that has no extendedKeyUsage extension
+ * implicitly allows all key purposes.
+ *
+ * Note that the Set is cloned to protect against subsequent modifications.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.KeyPurposeId KeyPurposeId}
+ *
+ * @param keyPurposeSet
+ * a Set
of key purpose OIDs in string format (or
+ * null
). Each OID is represented by a set of
+ * nonnegative integers separated by periods.
+ *
+ * @exception IOException
+ * if the OID is invalid, such as the first component being
+ * not 0, 1 or 2 or the second component being greater than
+ * 39.
+ *
+ * @see #getExtendedKeyUsage()
+ */
+ public void setExtendedKeyUsage(Set keyPurposeSet) throws IOException
+ {
+ if (keyPurposeSet == null || keyPurposeSet.isEmpty())
+ {
+ this.keyPurposeSet = keyPurposeSet;
+ }
+ else
+ {
+ this.keyPurposeSet = new HashSet();
+ Iterator iter = keyPurposeSet.iterator();
+ Object obj;
+ KeyPurposeId purposeID;
+ while (iter.hasNext())
+ {
+ obj = iter.next();
+ if (obj instanceof String)
+ {
+ purposeID = (KeyPurposeId)keyPurposeIdMap.get((String)obj);
+ if (purposeID == null)
+ {
+ throw new IOException("unknown purposeID "
+ + (String)obj);
+ }
+ this.keyPurposeSet.add(purposeID);
+ }
+ }
+ }
+ }
+
+ /**
+ * Enables/disables matching all of the subjectAlternativeNames specified in
+ * the {@link #setSubjectAlternativeNames setSubjectAlternativeNames} or
+ * {@link #addSubjectAlternativeName addSubjectAlternativeName} methods. If
+ * enabled, the X509Certificate
must contain all of the
+ * specified subject alternative names. If disabled, the X509Certificate
+ * must contain at least one of the specified subject alternative names.
+ *
+ * The matchAllNames flag is true
by default.
+ *
+ * @param matchAllNames
+ * if true
, the flag is enabled; if
+ * false
, the flag is disabled.
+ *
+ * @see #getMatchAllSubjectAltNames()
+ */
+ public void setMatchAllSubjectAltNames(boolean matchAllNames)
+ {
+ matchAllSubjectAltNames = matchAllNames;
+ }
+
+ /**
+ * Sets the subjectAlternativeNames criterion. The
+ * X509Certificate
must contain all or at least one of the
+ * specified subjectAlternativeNames, depending on the value of the
+ * matchAllNames flag (see {@link #setMatchAllSubjectAltNames}).
+ *
+ * This method allows the caller to specify, with a single method call, the
+ * complete set of subject alternative names for the subjectAlternativeNames
+ * criterion. The specified value replaces the previous value for the
+ * subjectAlternativeNames criterion.
+ *
+ * The names
parameter (if not null
) is a
+ * Collection
with one entry for each name to be included in
+ * the subject alternative name criterion. Each entry is a List
+ * whose first entry is an Integer
(the name type, 0-8) and
+ * whose second entry is a String
or a byte array (the name,
+ * in string or ASN.1 DER encoded form, respectively). There can be multiple
+ * names of the same type. If null
is supplied as the value
+ * for this argument, no subjectAlternativeNames check will be performed.
+ *
+ * Each subject alternative name in the Collection
may be
+ * specified either as a String
or as an ASN.1 encoded byte
+ * array. For more details about the formats used, see
+ * {@link #addSubjectAlternativeName(int, String) addSubjectAlternativeName(int type, String name)}
+ * and
+ * {@link #addSubjectAlternativeName(int, byte[]) addSubjectAlternativeName(int type, byte [] name}).
+ *
+ * Note that the names
parameter can contain duplicate names
+ * (same name and name type), but they may be removed from the
+ * Collection
of names returned by the
+ * {@link #getSubjectAlternativeNames} method.
+ *
+ * Note that a deep copy is performed on the Collection to protect against
+ * subsequent modifications.
+ *
+ * @param names -
+ * a Collection of names (or null)
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ *
+ * @see #getSubjectAlternativeNames()
+ */
+ public void setSubjectAlternativeNames(Collection names) throws IOException
+ {
+ try
+ {
+ if (names == null || names.isEmpty())
+ {
+ subjectAltNames = null;
+ subjectAltNamesByte = null;
+ }
+ else
+ {
+ subjectAltNames = new HashSet();
+ subjectAltNamesByte = new HashSet();
+ Iterator iter = names.iterator();
+ List item;
+ int type;
+ Object data;
+ while (iter.hasNext())
+ {
+ item = (List)iter.next();
+ type = ((Integer)item.get(0)).intValue();
+ data = item.get(1);
+ if (data instanceof String)
+ {
+ addSubjectAlternativeName(type, (String)data);
+ }
+ else if (data instanceof byte[])
+ {
+ addSubjectAlternativeName(type, (byte[])data);
+ }
+ else
+ {
+ throw new IOException(
+ "parsing error: unknown data type");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("parsing exception:\n" + ex.toString());
+ }
+ }
+
+ /**
+ * Adds a name to the subjectAlternativeNames criterion. The
+ * X509Certificate
must contain all or at least one of the
+ * specified subjectAlternativeNames, depending on the value of the
+ * matchAllNames flag (see {@link #setMatchAllSubjectAltNames}).
+ *
+ * This method allows the caller to add a name to the set of subject
+ * alternative names. The specified name is added to any previous value for
+ * the subjectAlternativeNames criterion. If the specified name is a
+ * duplicate, it may be ignored.
+ *
+ * The name is provided in string format. RFC 822, DNS, and URI names use
+ * the well-established string formats for those types (subject to the
+ * restrictions included in RFC 2459). IPv4 address names are supplied using
+ * dotted quad notation. OID address names are represented as a series of
+ * nonnegative integers separated by periods. And directory names
+ * (distinguished names) are supplied in RFC 2253 format. No standard string
+ * format is defined for otherNames, X.400 names, EDI party names, IPv6
+ * address names, or any other type of names. They should be specified using
+ * the
+ * {@link #addSubjectAlternativeName(int, byte[]) addSubjectAlternativeName(int type, byte [] name)}
+ * method.
+ *
+ * @param type
+ * the name type (0-8, as specified in RFC 2459, section 4.2.1.7)
+ * @param name -
+ * the name in string form (not null)
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ */
+ public void addSubjectAlternativeName(int type, String name)
+ throws IOException
+ {
+ // TODO full implementation of CertUtil.parseGeneralName
+ byte[] encoded = CertUtil.parseGeneralName(type, name);
+ List tmpList = new ArrayList();
+ tmpList.add(Integers.valueOf(type));
+ tmpList.add(name);
+ subjectAltNames.add(tmpList);
+ tmpList.set(1, encoded);
+ subjectAltNamesByte.add(tmpList);
+ }
+
+ /**
+ * Adds a name to the subjectAlternativeNames criterion. The
+ * X509Certificate
must contain all or at least one of the
+ * specified subjectAlternativeNames, depending on the value of the
+ * matchAllNames flag (see {@link #setMatchAllSubjectAltNames}).
+ *
+ * This method allows the caller to add a name to the set of subject
+ * alternative names. The specified name is added to any previous value for
+ * the subjectAlternativeNames criterion. If the specified name is a
+ * duplicate, it may be ignored.
+ *
+ * The name is provided as a byte array. This byte array should contain the
+ * DER encoded name, as it would appear in the GeneralName structure defined
+ * in RFC 2459 and X.509. The encoded byte array should only contain the
+ * encoded value of the name, and should not include the tag associated with
+ * the name in the GeneralName structure. The ASN.1 definition of this
+ * structure appears below.
+ *
+ *
+ *
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER}
+ *
+ *
+ *
+ *
+ * Note that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * TODO: check encoded format
+ *
+ * @param type
+ * the name type (0-8, as listed above)
+ * @param name
+ * a byte array containing the name in ASN.1 DER encoded form
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ */
+ public void addSubjectAlternativeName(int type, byte[] name)
+ throws IOException
+ {
+ // TODO check encoded format
+ List tmpList = new ArrayList();
+ tmpList.add(Integers.valueOf(type));
+ tmpList.add(name.clone());
+ subjectAltNames.add(tmpList);
+ subjectAltNamesByte.add(tmpList);
+ }
+
+ /**
+ * Sets the name constraints criterion. The X509Certificate
+ * must have subject and subject alternative names that meet the specified
+ * name constraints.
+ *
+ * The name constraints are specified as a byte array. This byte array
+ * should contain the DER encoded form of the name constraints, as they
+ * would appear in the NameConstraints structure defined in RFC 2459 and
+ * X.509. The ASN.1 definition of this structure appears below.
+ *
+ *
+ *
+ * NameConstraints ::= SEQUENCE {
+ * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
+ * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
+ *
+ * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
+ *
+ * GeneralSubtree ::= SEQUENCE {
+ * base GeneralName,
+ * minimum [0] BaseDistance DEFAULT 0,
+ * maximum [1] BaseDistance OPTIONAL }
+ *
+ * BaseDistance ::= INTEGER (0..MAX)
+ *
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER}
+ *
+ *
+ *
+ *
+ * Note that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * TODO: implement this
+ *
+ * @param bytes
+ * a byte array containing the ASN.1 DER encoding of a
+ * NameConstraints extension to be used for checking name
+ * constraints. Only the value of the extension is included, not
+ * the OID or criticality flag. Can be null
, in
+ * which case no name constraints check will be performed
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ * @exception UnsupportedOperationException
+ * because this method is not supported
+ * @see #getNameConstraints()
+ */
+ public void setNameConstraints(byte[] bytes) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Sets the basic constraints constraint. If the value is greater than or
+ * equal to zero, X509Certificates
must include a
+ * basicConstraints extension with a pathLen of at least this value. If the
+ * value is -2, only end-entity certificates are accepted. If the value is
+ * -1, no check is done.
+ *
+ * This constraint is useful when building a certification path forward
+ * (from the target toward the trust anchor. If a partial path has been
+ * built, any candidate certificate must have a maxPathLen value greater
+ * than or equal to the number of certificates in the partial path.
+ *
+ * @param minMaxPathLen
+ * the value for the basic constraints constraint
+ *
+ * @exception IllegalArgumentException
+ * if the value is less than -2
+ *
+ * @see #getBasicConstraints()
+ */
+ public void setBasicConstraints(int minMaxPathLen)
+ {
+ if (minMaxPathLen < -2)
+ {
+ throw new IllegalArgumentException("minMaxPathLen must be >= -2");
+ }
+
+ this.minMaxPathLen = minMaxPathLen;
+ }
+
+ /**
+ * Sets the policy constraint. The X509Certificate must include at least one
+ * of the specified policies in its certificate policies extension. If
+ * certPolicySet is empty, then the X509Certificate must include at least
+ * some specified policy in its certificate policies extension. If
+ * certPolicySet is null, no policy check will be performed.
+ *
+ * Note that the Set is cloned to protect against subsequent modifications.
+ *
+ * TODO: implement match check for this
+ *
+ * @param certPolicySet
+ * a Set of certificate policy OIDs in string format (or null).
+ * Each OID is represented by a set of nonnegative integers
+ * separated by periods.
+ *
+ * @exception IOException
+ * if a parsing error occurs on the OID such as the first
+ * component is not 0, 1 or 2 or the second component is
+ * greater than 39.
+ *
+ * @see #getPolicy()
+ */
+ public void setPolicy(Set certPolicySet) throws IOException
+ {
+ if (certPolicySet == null)
+ {
+ policy = null;
+ policyOID = null;
+ }
+ else
+ {
+ policyOID = new HashSet();
+ Iterator iter = certPolicySet.iterator();
+ Object item;
+ while (iter.hasNext())
+ {
+ item = iter.next();
+ if (item instanceof String)
+ {
+ CertUtil.parseOID((String)item);
+ policyOID.add(new ASN1ObjectIdentifier((String)item));
+ }
+ else
+ {
+ throw new IOException(
+ "certPolicySet contains null values or non String objects");
+ }
+ }
+ policy = new HashSet(certPolicySet);
+ }
+ }
+
+ /**
+ * Sets the pathToNames criterion. The X509Certificate
must
+ * not include name constraints that would prohibit building a path to the
+ * specified names.
+ *
+ * This method allows the caller to specify, with a single method call, the
+ * complete set of names which the X509Certificates
's name
+ * constraints must permit. The specified value replaces the previous value
+ * for the pathToNames criterion.
+ *
+ * This constraint is useful when building a certification path forward
+ * (from the target toward the trust anchor. If a partial path has been
+ * built, any candidate certificate must not include name constraints that
+ * would prohibit building a path to any of the names in the partial path.
+ *
+ * The names parameter (if not null
) is a
+ * Collection
with one entry for each name to be included in
+ * the pathToNames criterion. Each entry is a List
whose
+ * first entry is an Integer (the name type, 0-8) and whose second entry is
+ * a String
or a byte array (the name, in string or ASN.1 DER
+ * encoded form, respectively). There can be multiple names of the same
+ * type. If null
is supplied as the value for this argument,
+ * no pathToNames check will be performed.
+ *
+ * Each name in the Collection may be specified either as a String or as an
+ * ASN.1 encoded byte array. For more details about the formats used, see
+ * {@link #addPathToName(int, String) addPathToName(int type, String name)}
+ * and
+ * {@link #addPathToName(int, byte[]) addPathToName(int type, byte [] name)}.
+ *
+ * Note that the names parameter can contain duplicate names (same name and
+ * name type), but they may be removed from the Collection of names returned
+ * by the {@link #getPathToNames} method.
+ *
+ * Note that a deep copy is performed on the Collection to protect against
+ * subsequent modifications.
+ *
+ * TODO: implement this match check for this
+ *
+ * @param names
+ * a Collection with one entry per name (or null
)
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ * @exception UnsupportedOperationException
+ * because this method is not supported
+ *
+ * @see #getPathToNames()
+ */
+ public void setPathToNames(Collection names) throws IOException
+ {
+ try
+ {
+ if (names == null || names.isEmpty())
+ {
+ pathToNames = null;
+ pathToNamesByte = null;
+ }
+ else
+ {
+ pathToNames = new HashSet();
+ pathToNamesByte = new HashSet();
+ Iterator iter = names.iterator();
+ List item;
+ int type;
+ Object data;
+
+ while (iter.hasNext())
+ {
+ item = (List)iter.next();
+ type = ((Integer)item.get(0)).intValue();
+ data = item.get(1);
+ if (data instanceof String)
+ {
+ addPathToName(type, (String)data);
+ }
+ else if (data instanceof byte[])
+ {
+ addPathToName(type, (byte[])data);
+ }
+ else
+ {
+ throw new IOException(
+ "parsing error: unknown data type");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("parsing exception:\n" + ex.toString());
+ }
+ }
+
+ /**
+ * Adds a name to the pathToNames criterion. The
+ * X509Certificate
must not include name constraints that
+ * would prohibit building a path to the specified name.
+ *
+ * This method allows the caller to add a name to the set of names which the
+ * X509Certificates
's name constraints must permit. The
+ * specified name is added to any previous value for the pathToNames
+ * criterion. If the name is a duplicate, it may be ignored.
+ *
+ * The name is provided in string format. RFC 822, DNS, and URI names use
+ * the well-established string formats for those types (subject to the
+ * restrictions included in RFC 2459). IPv4 address names are supplied using
+ * dotted quad notation. OID address names are represented as a series of
+ * nonnegative integers separated by periods. And directory names
+ * (distinguished names) are supplied in RFC 2253 format. No standard string
+ * format is defined for otherNames, X.400 names, EDI party names, IPv6
+ * address names, or any other type of names. They should be specified using
+ * the
+ * {@link #addPathToName(int, byte[]) addPathToName(int type, byte [] name)}
+ * method.
+ *
+ * TODO: implement this match check for this
+ *
+ * @param type
+ * the name type (0-8, as specified in RFC 2459, section 4.2.1.7)
+ * @param name
+ * the name in string form
+ *
+ * @exceptrion IOException if a parsing error occurs
+ */
+ public void addPathToName(int type, String name) throws IOException
+ {
+ // TODO full implementation of CertUtil.parseGeneralName
+ byte[] encoded = CertUtil.parseGeneralName(type, name);
+ List tmpList = new ArrayList();
+ tmpList.add(Integers.valueOf(type));
+ tmpList.add(name);
+ pathToNames.add(tmpList);
+ tmpList.set(1, encoded);
+ pathToNamesByte.add(tmpList);
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Adds a name to the pathToNames criterion. The
+ * X509Certificate
must not include name constraints that
+ * would prohibit building a path to the specified name.
+ *
+ * This method allows the caller to add a name to the set of names which the
+ * X509Certificates
's name constraints must permit. The
+ * specified name is added to any previous value for the pathToNames
+ * criterion. If the name is a duplicate, it may be ignored.
+ *
+ * The name is provided as a byte array. This byte array should contain the
+ * DER encoded name, as it would appear in the GeneralName structure defined
+ * in RFC 2459 and X.509. The ASN.1 definition of this structure appears in
+ * the documentation for
+ * {@link #addSubjectAlternativeName(int,byte[]) addSubjectAlternativeName(int type, byte[] name)}.
+ *
+ * Note that the byte array supplied here is cloned to protect against
+ * subsequent modifications.
+ *
+ * TODO: implement this match check for this
+ *
+ * @param type
+ * the name type (0-8, as specified in RFC 2459, section 4.2.1.7)
+ * @param name
+ * a byte array containing the name in ASN.1 DER encoded form
+ *
+ * @exception IOException
+ * if a parsing error occurs
+ */
+ public void addPathToName(int type, byte[] name) throws IOException
+ {
+ // TODO check encoded format
+ List tmpList = new ArrayList();
+ tmpList.add(Integers.valueOf(type));
+ tmpList.add(name.clone());
+ pathToNames.add(tmpList);
+ pathToNamesByte.add(tmpList);
+ }
+
+ /**
+ * Returns the certificateEquals criterion. The specified
+ * X509Certificate
must be equal to the
+ * X509Certificate
passed to the match method. If
+ * null
, this check is not applied.
+ *
+ * @retrun the X509Certificate
to match (or null
)
+ *
+ * @see #setCertificate(java.security.cert.X509Certificate)
+ */
+ public X509Certificate getCertificate()
+ {
+ return x509Cert;
+ }
+
+ /**
+ * Returns the serialNumber criterion. The specified serial number must
+ * match the certificate serial number in the X509Certificate
.
+ * If null
, any certificate serial number will do.
+ *
+ * @return the certificate serial number to match (or null
)
+ *
+ * @see #setSerialNumber(java.math.BigInteger)
+ */
+ public BigInteger getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ /**
+ * Returns the issuer criterion as a String. This distinguished name must
+ * match the issuer distinguished name in the X509Certificate
.
+ * If null
, the issuer criterion is disabled and any issuer
+ * distinguished name will do.
+ *
+ * If the value returned is not null
, it is a distinguished
+ * name, in RFC 2253 format.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.X509Name X509Name} for formatiing
+ * byte[] issuerDN to String.
+ *
+ * @return the required issuer distinguished name in RFC 2253 format (or
+ * null
)
+ */
+ public String getIssuerAsString()
+ {
+ if (issuerDN instanceof String)
+ {
+ return new String((String)issuerDN);
+ }
+ else if (issuerDNX509 != null)
+ {
+ return issuerDNX509.toString();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the issuer criterion as a byte array. This distinguished name
+ * must match the issuer distinguished name in the
+ * X509Certificate
. If null
, the issuer
+ * criterion is disabled and any issuer distinguished name will do.
+ *
+ * If the value returned is not null
, it is a byte array
+ * containing a single DER encoded distinguished name, as defined in X.501.
+ * The ASN.1 notation for this structure is supplied in the documentation
+ * for {@link #setIssuer(byte[]) setIssuer(byte [] issuerDN)}.
+ *
+ * Note that the byte array returned is cloned to protect against subsequent
+ * modifications.
+ *
+ * Uses {@link org.spongycastle.asn1.DEROutputStream DEROutputStream},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name} to gnerate byte[]
+ * output for String issuerDN.
+ *
+ * @return a byte array containing the required issuer distinguished name in
+ * ASN.1 DER format (or null
)
+ *
+ * @exception IOException
+ * if an encoding error occurs
+ */
+ public byte[] getIssuerAsBytes() throws IOException
+ {
+ if (issuerDN instanceof byte[])
+ {
+ return (byte[])((byte[])issuerDN).clone();
+ }
+ else if (issuerDNX509 != null)
+ {
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+
+ derOutStream.writeObject(issuerDNX509.toASN1Primitive());
+ derOutStream.close();
+
+ return outStream.toByteArray();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the subject criterion as a String. This distinguished name must
+ * match the subject distinguished name in the X509Certificate
.
+ * If null
, the subject criterion is disabled and any
+ * subject distinguished name will do.
+ *
+ * If the value returned is not null
, it is a distinguished
+ * name, in RFC 2253 format.
+ *
+ * Uses {@link org.spongycastle.asn1.x509.X509Name X509Name} for formatiing
+ * byte[] subjectDN to String.
+ *
+ * @return the required subject distinguished name in RFC 2253 format (or
+ * null
)
+ */
+ public String getSubjectAsString()
+ {
+ if (subjectDN instanceof String)
+ {
+ return new String((String)subjectDN);
+ }
+ else if (subjectDNX509 != null)
+ {
+ return subjectDNX509.toString();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the subject criterion as a byte array. This distinguished name
+ * must match the subject distinguished name in the
+ * X509Certificate
. If null
, the subject
+ * criterion is disabled and any subject distinguished name will do.
+ *
+ * If the value returned is not null
, it is a byte array
+ * containing a single DER encoded distinguished name, as defined in X.501.
+ * The ASN.1 notation for this structure is supplied in the documentation
+ * for {@link #setSubject(byte [] subjectDN) setSubject(byte [] subjectDN)}.
+ *
+ * Note that the byte array returned is cloned to protect against subsequent
+ * modifications.
+ *
+ * Uses {@link org.spongycastle.asn1.DEROutputStream DEROutputStream},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name} to gnerate byte[]
+ * output for String subjectDN.
+ *
+ * @return a byte array containing the required subject distinguished name
+ * in ASN.1 DER format (or null
)
+ *
+ * @exception IOException
+ * if an encoding error occurs
+ */
+ public byte[] getSubjectAsBytes() throws IOException
+ {
+ if (subjectDN instanceof byte[])
+ {
+ return (byte[])((byte[])subjectDN).clone();
+ }
+ else if (subjectDNX509 != null)
+ {
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DEROutputStream derOutStream = new DEROutputStream(outStream);
+
+ derOutStream.writeObject(subjectDNX509.toASN1Primitive());
+ derOutStream.close();
+
+ return outStream.toByteArray();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the subjectKeyIdentifier criterion. The
+ * X509Certificate
must contain a SubjectKeyIdentifier
+ * extension with the specified value. If null
, no
+ * subjectKeyIdentifier check will be done.
+ *
+ * Note that the byte array returned is cloned to protect against subsequent
+ * modifications.
+ *
+ * @return the key identifier (or null
)
+ *
+ * @see #setSubjectKeyIdentifier
+ */
+ public byte[] getSubjectKeyIdentifier()
+ {
+ if (subjectKeyID != null)
+ {
+ return (byte[])subjectKeyID.clone();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the authorityKeyIdentifier criterion. The
+ * X509Certificate
must contain a AuthorityKeyIdentifier
+ * extension with the specified value. If null
, no
+ * authorityKeyIdentifier check will be done.
+ *
+ * Note that the byte array returned is cloned to protect against subsequent
+ * modifications.
+ *
+ * @return the key identifier (or null
)
+ *
+ * @see #setAuthorityKeyIdentifier
+ */
+ public byte[] getAuthorityKeyIdentifier()
+ {
+ if (authorityKeyID != null)
+ {
+ return (byte[])authorityKeyID.clone();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the certificateValid criterion. The specified date must fall
+ * within the certificate validity period for the
+ * X509Certificate
. If null
, no
+ * certificateValid check will be done.
+ *
+ * Note that the Date
returned is cloned to protect against
+ * subsequent modifications.
+ *
+ * @return the Date
to check (or null
)
+ *
+ * @see #setCertificateValid
+ */
+ public Date getCertificateValid()
+ {
+ if (certValid != null)
+ {
+ return new Date(certValid.getTime());
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the privateKeyValid criterion. The specified date must fall
+ * within the private key validity period for the
+ * X509Certificate
. If null
, no
+ * privateKeyValid check will be done.
+ *
+ * Note that the Date
returned is cloned to protect against
+ * subsequent modifications.
+ *
+ * @return the Date
to check (or null
)
+ *
+ * @see #setPrivateKeyValid
+ */
+ public Date getPrivateKeyValid()
+ {
+ if (privateKeyValid != null)
+ {
+ return new Date(privateKeyValid.getTime());
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the subjectPublicKeyAlgID criterion. The
+ * X509Certificate
must contain a subject public key with the
+ * specified algorithm. If null
, no subjectPublicKeyAlgID
+ * check will be done.
+ *
+ * @return the object identifier (OID) of the signature algorithm to check
+ * for (or null
). An OID is represented by a set of
+ * nonnegative integers separated by periods.
+ *
+ * @see #setSubjectPublicKeyAlgID
+ */
+ public String getSubjectPublicKeyAlgID()
+ {
+ if (subjectKeyAlgID != null)
+ {
+ return subjectKeyAlgID.toString();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the subjectPublicKey criterion. The X509Certificate
+ * must contain the specified subject public key. If null
,
+ * no subjectPublicKey check will be done.
+ *
+ * @return the subject public key to check for (or null
)
+ *
+ * @see #setSubjectPublicKey
+ */
+ public PublicKey getSubjectPublicKey()
+ {
+ return subjectPublicKey;
+ }
+
+ /**
+ * Returns the keyUsage criterion. The X509Certificate
must
+ * allow the specified keyUsage values. If null, no keyUsage check will be
+ * done.
+ *
+ * Note that the boolean array returned is cloned to protect against
+ * subsequent modifications.
+ *
+ * @return a boolean array in the same format as the boolean array returned
+ * by
+ * {@link X509Certificate#getKeyUsage() X509Certificate.getKeyUsage()}.
+ * Or null
.
+ *
+ * @see #setKeyUsage
+ */
+ public boolean[] getKeyUsage()
+ {
+ if (keyUsage != null)
+ {
+ return (boolean[])keyUsage.clone();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the extendedKeyUsage criterion. The X509Certificate
+ * must allow the specified key purposes in its extended key usage
+ * extension. If the keyPurposeSet
returned is empty or
+ * null
, no extendedKeyUsage check will be done. Note that
+ * an X509Certificate
that has no extendedKeyUsage extension
+ * implicitly allows all key purposes.
+ *
+ * @return an immutable Set
of key purpose OIDs in string
+ * format (or null
)
+ * @see #setExtendedKeyUsage
+ */
+ public Set getExtendedKeyUsage()
+ {
+ if (keyPurposeSet == null || keyPurposeSet.isEmpty())
+ {
+ return keyPurposeSet;
+ }
+
+ Set returnSet = new HashSet();
+ Iterator iter = keyPurposeSet.iterator();
+ while (iter.hasNext())
+ {
+ returnSet.add(iter.next().toString());
+ }
+
+ return Collections.unmodifiableSet(returnSet);
+ }
+
+ /**
+ * Indicates if the X509Certificate
must contain all or at
+ * least one of the subjectAlternativeNames specified in the
+ * {@link #setSubjectAlternativeNames setSubjectAlternativeNames} or
+ * {@link #addSubjectAlternativeName addSubjectAlternativeName} methods. If
+ * true
, the X509Certificate
must contain all
+ * of the specified subject alternative names. If false
, the
+ * X509Certificate
must contain at least one of the specified
+ * subject alternative names.
+ *
+ * @return true
if the flag is enabled; false
+ * if the flag is disabled. The flag is true
by
+ * default.
+ *
+ * @see #setMatchAllSubjectAltNames
+ */
+ public boolean getMatchAllSubjectAltNames()
+ {
+ return matchAllSubjectAltNames;
+ }
+
+ /**
+ * Returns a copy of the subjectAlternativeNames criterion. The
+ * X509Certificate
must contain all or at least one of the
+ * specified subjectAlternativeNames, depending on the value of the
+ * matchAllNames flag (see {@link #getMatchAllSubjectAltNames
+ * getMatchAllSubjectAltNames}). If the value returned is null
,
+ * no subjectAlternativeNames check will be performed.
+ *
+ * If the value returned is not null
, it is a
+ * Collection
with one entry for each name to be included in
+ * the subject alternative name criterion. Each entry is a List
+ * whose first entry is an Integer
(the name type, 0-8) and
+ * whose second entry is a String
or a byte array (the name,
+ * in string or ASN.1 DER encoded form, respectively). There can be multiple
+ * names of the same type. Note that the Collection
returned
+ * may contain duplicate names (same name and name type).
+ *
+ * Each subject alternative name in the Collection
may be
+ * specified either as a String
or as an ASN.1 encoded byte
+ * array. For more details about the formats used, see
+ * {@link #addSubjectAlternativeName(int type, String name)
+ * addSubjectAlternativeName(int type, String name)} and
+ * {@link #addSubjectAlternativeName(int type, byte [] name)
+ * addSubjectAlternativeName(int type, byte [] name)}.
+ *
+ * Note that a deep copy is performed on the Collection
to
+ * protect against subsequent modifications.
+ *
+ * @return a Collection
of names (or null
)
+ *
+ * @see #setSubjectAlternativeNames
+ */
+ public Collection getSubjectAlternativeNames()
+ {
+ if (subjectAltNames != null)
+ {
+ return null;
+ }
+
+ Set returnAltNames = new HashSet();
+ List returnList;
+ Iterator iter = subjectAltNames.iterator();
+ List obj;
+ while (iter.hasNext())
+ {
+ obj = (List)iter.next();
+ returnList = new ArrayList();
+ returnList.add(obj.get(0));
+ if (obj.get(1) instanceof byte[])
+ {
+ returnList.add(((byte[])obj.get(1)).clone());
+ }
+ else
+ {
+ returnList.add(obj.get(1));
+ }
+ returnAltNames.add(returnList);
+ }
+
+ return returnAltNames;
+ }
+
+ /**
+ * Returns the name constraints criterion. The X509Certificate
+ * must have subject and subject alternative names that meet the specified
+ * name constraints.
+ *
+ * The name constraints are returned as a byte array. This byte array
+ * contains the DER encoded form of the name constraints, as they would
+ * appear in the NameConstraints structure defined in RFC 2459 and X.509.
+ * The ASN.1 notation for this structure is supplied in the documentation
+ * for
+ * {@link #setNameConstraints(byte [] bytes) setNameConstraints(byte [] bytes)}.
+ *
+ * Note that the byte array returned is cloned to protect against subsequent
+ * modifications.
+ *
+ * TODO: implement this
+ *
+ * @return a byte array containing the ASN.1 DER encoding of a
+ * NameConstraints extension used for checking name constraints.
+ * null
if no name constraints check will be
+ * performed.
+ *
+ * @exception UnsupportedOperationException
+ * because this method is not supported
+ *
+ * @see #setNameConstraints
+ */
+ public byte[] getNameConstraints()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the basic constraints constraint. If the value is greater than or
+ * equal to zero, the X509Certificates
must include a
+ * basicConstraints extension with a pathLen of at least this value. If the
+ * value is -2, only end-entity certificates are accepted. If the value is
+ * -1, no basicConstraints check is done.
+ *
+ * @return the value for the basic constraints constraint
+ *
+ * @see #setBasicConstraints
+ */
+ public int getBasicConstraints()
+ {
+ return minMaxPathLen;
+ }
+
+ /**
+ * Returns the policy criterion. The X509Certificate
must
+ * include at least one of the specified policies in its certificate
+ * policies extension. If the Set
returned is empty, then the
+ * X509Certificate
must include at least some specified
+ * policy in its certificate policies extension. If the Set
+ * returned is null
, no policy check will be performed.
+ *
+ * @return an immutable Set
of certificate policy OIDs in
+ * string format (or null
)
+ *
+ * @see #setPolicy
+ */
+ public Set getPolicy()
+ {
+ if (policy == null)
+ {
+ return null;
+ }
+
+ return Collections.unmodifiableSet(policy);
+ }
+
+ /**
+ * Returns a copy of the pathToNames criterion. The
+ * X509Certificate
must not include name constraints that
+ * would prohibit building a path to the specified names. If the value
+ * returned is null
, no pathToNames check will be performed.
+ *
+ * If the value returned is not null
, it is a
+ * Collection
with one entry for each name to be included in
+ * the pathToNames criterion. Each entry is a List
whose
+ * first entry is an Integer
(the name type, 0-8) and whose
+ * second entry is a String
or a byte array (the name, in
+ * string or ASN.1 DER encoded form, respectively). There can be multiple
+ * names of the same type. Note that the Collection
returned
+ * may contain duplicate names (same name and name type).
+ *
+ * Each name in the Collection
may be specified either as a
+ * String
or as an ASN.1 encoded byte array. For more details
+ * about the formats used, see {@link #addPathToName(int type, String name)
+ * addPathToName(int type, String name)} and
+ * {@link #addPathToName(int type, byte [] name) addPathToName(int type,
+ * byte [] name)}.
+ *
+ * Note that a deep copy is performed on the Collection
to
+ * protect against subsequent modifications.
+ *
+ * @return a Collection
of names (or null
)
+ *
+ * @see #setPathToNames
+ */
+ public Collection getPathToNames()
+ {
+ if (pathToNames == null)
+ {
+ return null;
+ }
+
+ Set returnPathToNames = new HashSet();
+ List returnList;
+ Iterator iter = pathToNames.iterator();
+ List obj;
+
+ while (iter.hasNext())
+ {
+ obj = (List)iter.next();
+ returnList = new ArrayList();
+ returnList.add(obj.get(0));
+ if (obj.get(1) instanceof byte[])
+ {
+ returnList.add(((byte[])obj.get(1)).clone());
+ }
+ else
+ {
+ returnList.add(obj.get(1));
+ }
+ returnPathToNames.add(returnList);
+ }
+
+ return returnPathToNames;
+ }
+
+ /**
+ * Return a printable representation of the CertSelector
.
+ *
+ * TODO: implement output for currently unsupported options(name
+ * constraints)
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object},
+ * {@link org.spongycastle.asn1.x509.KeyPurposeId KeyPurposeId}
+ *
+ * @return a String
describing the contents of the
+ * CertSelector
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("X509CertSelector: [\n");
+ if (x509Cert != null)
+ {
+ sb.append(" Certificate: ").append(x509Cert).append('\n');
+ }
+ if (serialNumber != null)
+ {
+ sb.append(" Serial Number: ").append(serialNumber).append('\n');
+ }
+ if (issuerDN != null)
+ {
+ sb.append(" Issuer: ").append(getIssuerAsString()).append('\n');
+ }
+ if (subjectDN != null)
+ {
+ sb.append(" Subject: ").append(getSubjectAsString()).append('\n');
+ }
+ try
+ {
+ if (subjectKeyID != null)
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ subjectKeyID);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object derObject = derInStream.readObject();
+ sb.append(" Subject Key Identifier: ")
+ .append(ASN1Dump.dumpAsString(derObject)).append('\n');
+ }
+ if (authorityKeyID != null)
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ authorityKeyID);
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object derObject = derInStream.readObject();
+ sb.append(" Authority Key Identifier: ")
+ .append(ASN1Dump.dumpAsString(derObject)).append('\n');
+ }
+ }
+ catch (IOException ex)
+ {
+ sb.append(ex.getMessage()).append('\n');
+ }
+ if (certValid != null)
+ {
+ sb.append(" Certificate Valid: ").append(certValid).append('\n');
+ }
+ if (privateKeyValid != null)
+ {
+ sb.append(" Private Key Valid: ").append(privateKeyValid)
+ .append('\n');
+ }
+ if (subjectKeyAlgID != null)
+ {
+ sb.append(" Subject Public Key AlgID: ")
+ .append(subjectKeyAlgID).append('\n');
+ }
+ if (subjectPublicKey != null)
+ {
+ sb.append(" Subject Public Key: ").append(subjectPublicKey)
+ .append('\n');
+ }
+ if (keyUsage != null)
+ {
+ sb.append(" Key Usage: ").append(keyUsage).append('\n');
+ }
+ if (keyPurposeSet != null)
+ {
+ sb.append(" Extended Key Usage: ").append(keyPurposeSet)
+ .append('\n');
+ }
+ if (policy != null)
+ {
+ sb.append(" Policy: ").append(policy).append('\n');
+ }
+ sb.append(" matchAllSubjectAltNames flag: ")
+ .append(matchAllSubjectAltNames).append('\n');
+ if (subjectAltNamesByte != null)
+ {
+ sb.append(" SubjectAlternativNames: \n[");
+ Iterator iter = subjectAltNamesByte.iterator();
+ List obj;
+ try
+ {
+ while (iter.hasNext())
+ {
+ obj = (List)iter.next();
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ (byte[])obj.get(1));
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object derObject = derInStream.readObject();
+ sb.append(" Type: ").append(obj.get(0)).append(" Data: ")
+ .append(ASN1Dump.dumpAsString(derObject)).append('\n');
+ }
+ }
+ catch (IOException ex)
+ {
+ sb.append(ex.getMessage()).append('\n');
+ }
+ sb.append("]\n");
+ }
+ if (pathToNamesByte != null)
+ {
+ sb.append(" PathToNamesNames: \n[");
+ Iterator iter = pathToNamesByte.iterator();
+ List obj;
+ try
+ {
+ while (iter.hasNext())
+ {
+ obj = (List)iter.next();
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ (byte[])obj.get(1));
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Object derObject = derInStream.readObject();
+ sb.append(" Type: ").append(obj.get(0)).append(" Data: ")
+ .append(ASN1Dump.dumpAsString(derObject)).append('\n');
+ }
+ }
+ catch (IOException ex)
+ {
+ sb.append(ex.getMessage()).append('\n');
+ }
+ sb.append("]\n");
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ /**
+ * Decides whether a Certificate
should be selected.
+ *
+ * TODO: implement missing tests (name constraints and path to names)
+ *
+ * Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
+ * {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
+ * {@link org.spongycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
+ * {@link org.spongycastle.asn1.ASN1Object ASN1Object},
+ * {@link org.spongycastle.asn1.DERGeneralizedTime DERGeneralizedTime},
+ * {@link org.spongycastle.asn1.x509.X509Name X509Name},
+ * {@link org.spongycastle.asn1.x509.X509Extensions X509Extensions},
+ * {@link org.spongycastle.asn1.x509.ExtendedKeyUsage ExtendedKeyUsage},
+ * {@link org.spongycastle.asn1.x509.KeyPurposeId KeyPurposeId},
+ * {@link org.spongycastle.asn1.x509.SubjectPublicKeyInfo SubjectPublicKeyInfo},
+ * {@link org.spongycastle.asn1.x509.AlgorithmIdentifier AlgorithmIdentifier}
+ * to access X509 extensions
+ *
+ * @param cert
+ * the Certificate
to be checked
+ *
+ * @return true
if the Certificate
should be
+ * selected, false
otherwise
+ */
+ public boolean match(Certificate cert)
+ {
+ boolean[] booleanArray;
+ List tempList;
+ Iterator tempIter;
+
+ if (!(cert instanceof X509Certificate))
+ {
+ return false;
+ }
+ X509Certificate certX509 = (X509Certificate)cert;
+
+ if (x509Cert != null && !x509Cert.equals(certX509))
+ {
+ return false;
+ }
+ if (serialNumber != null
+ && !serialNumber.equals(certX509.getSerialNumber()))
+ {
+ return false;
+ }
+ try
+ {
+ if (issuerDNX509 != null)
+ {
+ if (!issuerDNX509.equals(PrincipalUtil
+ .getIssuerX509Principal(certX509), true))
+ {
+ return false;
+ }
+ }
+ if (subjectDNX509 != null)
+ {
+ if (!subjectDNX509.equals(PrincipalUtil
+ .getSubjectX509Principal(certX509), true))
+ {
+ return false;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ if (subjectKeyID != null)
+ {
+ byte[] data = certX509
+ .getExtensionValue(X509Extensions.SubjectKeyIdentifier
+ .getId());
+ if (data == null)
+ {
+ return false;
+ }
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ byte[] testData = ((ASN1OctetString)derInputStream.readObject())
+ .getOctets();
+ if (!Arrays.equals(subjectKeyID, testData))
+ {
+ return false;
+ }
+ }
+ catch (IOException ex)
+ {
+ return false;
+ }
+ }
+ if (authorityKeyID != null)
+ {
+ byte[] data = certX509
+ .getExtensionValue(X509Extensions.AuthorityKeyIdentifier
+ .getId());
+ if (data == null)
+ {
+ return false;
+ }
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ byte[] testData = ((ASN1OctetString)derInputStream.readObject())
+ .getOctets();
+ if (!Arrays.equals(authorityKeyID, testData))
+ {
+ return false;
+ }
+ }
+ catch (IOException ex)
+ {
+ return false;
+ }
+ }
+ if (certValid != null)
+ {
+ if (certX509.getNotAfter() != null
+ && certValid.after(certX509.getNotAfter()))
+ {
+ return false;
+ }
+ if (certX509.getNotBefore() != null
+ && certValid.before(certX509.getNotBefore()))
+ {
+ return false;
+ }
+ }
+ if (privateKeyValid != null)
+ {
+ try
+ {
+ byte[] data = certX509
+ .getExtensionValue(X509Extensions.PrivateKeyUsagePeriod
+ .getId());
+ if (data != null)
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ inStream = new ByteArrayInputStream(
+ ((ASN1OctetString)derInputStream.readObject())
+ .getOctets());
+ derInputStream = new ASN1InputStream(inStream);
+ // TODO fix this, Sequence contains tagged objects
+ ASN1Sequence derObject = (ASN1Sequence)derInputStream
+ .readObject();
+ DERGeneralizedTime derDate = DERGeneralizedTime
+ .getInstance(derObject.getObjectAt(0));
+ SimpleDateFormat dateF = new SimpleDateFormat(
+ "yyyyMMddHHmmssZ");
+ if (privateKeyValid.before(dateF.parse(derDate.getTime())))
+ {
+ return false;
+ }
+ derDate = DERGeneralizedTime.getInstance(derObject
+ .getObjectAt(1));
+ if (privateKeyValid.after(dateF.parse(derDate.getTime())))
+ {
+ return false;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+ if (subjectKeyAlgID != null)
+ {
+ try
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ certX509.getPublicKey().getEncoded());
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(
+ (ASN1Sequence)derInputStream.readObject());
+ AlgorithmIdentifier algInfo = publicKeyInfo.getAlgorithmId();
+ if (!algInfo.getObjectId().equals(subjectKeyAlgID))
+ {
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+ if (subjectPublicKeyByte != null)
+ {
+ if (!Arrays.equals(subjectPublicKeyByte, certX509.getPublicKey()
+ .getEncoded()))
+ {
+ return false;
+ }
+ }
+ if (subjectPublicKey != null)
+ {
+ if (!subjectPublicKey.equals(certX509.getPublicKey()))
+ {
+ return false;
+ }
+ }
+ if (keyUsage != null)
+ {
+ booleanArray = certX509.getKeyUsage();
+ if (booleanArray != null)
+ {
+ for (int i = 0; i < keyUsage.length; i++)
+ {
+ if (keyUsage[i]
+ && (booleanArray.length <= i || !booleanArray[i]))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ if (keyPurposeSet != null && !keyPurposeSet.isEmpty())
+ {
+ try
+ {
+ byte[] data = certX509
+ .getExtensionValue(X509Extensions.ExtendedKeyUsage
+ .getId());
+ if (data != null)
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ ExtendedKeyUsage extendedKeyUsage = ExtendedKeyUsage.getInstance(
+ (ASN1Sequence)derInputStream.readObject());
+ tempIter = keyPurposeSet.iterator();
+ while (tempIter.hasNext())
+ {
+ if (!extendedKeyUsage
+ .hasKeyPurposeId((KeyPurposeId)tempIter.next()))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+ if (minMaxPathLen != -1)
+ {
+ if (minMaxPathLen == -2 && certX509.getBasicConstraints() != -1)
+ {
+ return false;
+ }
+ if (minMaxPathLen >= 0
+ && certX509.getBasicConstraints() < minMaxPathLen)
+ {
+ return false;
+ }
+ }
+ if (policyOID != null)
+ {
+ try
+ {
+ byte[] data = certX509
+ .getExtensionValue(X509Extensions.CertificatePolicies
+ .getId());
+ if (data == null)
+ {
+ return false;
+ }
+ if (!policyOID.isEmpty())
+ {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(
+ data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ inStream = new ByteArrayInputStream(
+ ((ASN1OctetString)derInputStream.readObject())
+ .getOctets());
+ derInputStream = new ASN1InputStream(inStream);
+ Enumeration policySequence = ((ASN1Sequence)derInputStream
+ .readObject()).getObjects();
+ ASN1Sequence policyObject;
+ boolean test = false;
+ while (policySequence.hasMoreElements() && !test)
+ {
+ policyObject = (ASN1Sequence)policySequence
+ .nextElement();
+ if (policyOID.contains(policyObject.getObjectAt(0)))
+ {
+ test = true;
+ }
+ }
+ if (!test)
+ {
+ return false;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+ if (subjectAltNamesByte != null)
+ {
+ try
+ {
+ byte[] data = certX509
+ .getExtensionValue(X509Extensions.SubjectAlternativeName
+ .getId());
+ if (data == null)
+ {
+ return false;
+ }
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ ASN1InputStream derInputStream = new ASN1InputStream(inStream);
+ inStream = new ByteArrayInputStream(
+ ((ASN1OctetString)derInputStream.readObject())
+ .getOctets());
+ derInputStream = new ASN1InputStream(inStream);
+ Enumeration altNamesSequence = ((ASN1Sequence)derInputStream
+ .readObject()).getObjects();
+ ASN1TaggedObject altNameObject;
+ boolean test = false;
+ Set testSet = new HashSet(subjectAltNamesByte);
+ List testList;
+ ASN1Object derData;
+ ByteArrayOutputStream outStream;
+ DEROutputStream derOutStream;
+ while (altNamesSequence.hasMoreElements() && !test)
+ {
+ altNameObject = (ASN1TaggedObject)altNamesSequence
+ .nextElement();
+ testList = new ArrayList(2);
+ testList.add(Integers.valueOf(altNameObject.getTagNo()));
+ derData = altNameObject.getObject();
+ outStream = new ByteArrayOutputStream();
+ derOutStream = new DEROutputStream(outStream);
+ derOutStream.writeObject(derData);
+ derOutStream.close();
+ testList.add(outStream.toByteArray());
+
+ if (testSet.remove(testList))
+ {
+ test = true;
+ }
+
+ if (matchAllSubjectAltNames && !testSet.isEmpty())
+ {
+ test = false;
+ }
+ }
+ if (!test)
+ {
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a copy of this object.
+ *
+ * @return the copy
+ */
+ public Object clone()
+ {
+ try
+ {
+ X509CertSelector copy = (X509CertSelector)super.clone();
+ if (issuerDN instanceof byte[])
+ {
+ copy.issuerDN = ((byte[])issuerDN).clone();
+ }
+ if (subjectDN instanceof byte[])
+ {
+ copy.subjectDN = ((byte[])subjectDN).clone();
+ }
+ if (subjectKeyID != null)
+ {
+ copy.subjectKeyID = (byte[])subjectKeyID.clone();
+ }
+ if (authorityKeyID != null)
+ {
+ copy.authorityKeyID = (byte[])authorityKeyID.clone();
+ }
+ if (subjectPublicKeyByte != null)
+ {
+ copy.subjectPublicKeyByte = (byte[])subjectPublicKeyByte
+ .clone();
+ }
+ if (keyUsage != null)
+ {
+ copy.keyUsage = (boolean[])keyUsage.clone();
+ }
+ if (keyPurposeSet != null)
+ {
+ copy.keyPurposeSet = new HashSet(keyPurposeSet);
+ }
+ if (policy != null)
+ {
+ copy.policy = new HashSet(policy);
+ copy.policyOID = new HashSet();
+ Iterator iter = policyOID.iterator();
+ while (iter.hasNext())
+ {
+ copy.policyOID.add(new ASN1ObjectIdentifier(
+ ((ASN1ObjectIdentifier)iter.next()).getId()));
+ }
+ }
+ if (subjectAltNames != null)
+ {
+ copy.subjectAltNames = new HashSet(getSubjectAlternativeNames());
+ Iterator iter = subjectAltNamesByte.iterator();
+ List obj;
+ List cloneObj;
+ while (iter.hasNext())
+ {
+ obj = (List)iter.next();
+ cloneObj = new ArrayList();
+ cloneObj.add(obj.get(0));
+ cloneObj.add(((byte[])obj.get(1)).clone());
+ copy.subjectAltNamesByte.add(cloneObj);
+ }
+ }
+ if (pathToNames != null)
+ {
+ copy.pathToNames = new HashSet(getPathToNames());
+ Iterator iter = pathToNamesByte.iterator();
+ List obj;
+ List cloneObj;
+ while (iter.hasNext())
+ {
+ obj = (List)iter.next();
+ cloneObj = new ArrayList();
+ cloneObj.add(obj.get(0));
+ cloneObj.add(((byte[])obj.get(1)).clone());
+ copy.pathToNamesByte.add(cloneObj);
+ }
+ }
+ return copy;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ /* Cannot happen */
+ throw new InternalError(e.toString());
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509Certificate.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509Certificate.java
new file mode 100644
index 000000000..d56f1c6f3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509Certificate.java
@@ -0,0 +1,33 @@
+
+package java.security.cert;
+
+import java.math.BigInteger;
+import java.security.Principal;
+import java.util.Date;
+
+public abstract class X509Certificate extends Certificate
+implements X509Extension
+{
+ protected X509Certificate()
+ {
+ super("X.509");
+ }
+
+ public abstract void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException;
+ public abstract void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException;
+ public abstract int getBasicConstraints();
+ public abstract Principal getIssuerDN();
+ public abstract boolean[] getIssuerUniqueID();
+ public abstract boolean[] getKeyUsage();
+ public abstract Date getNotAfter();
+ public abstract Date getNotBefore();
+ public abstract BigInteger getSerialNumber();
+ public abstract String getSigAlgName();
+ public abstract String getSigAlgOID();
+ public abstract byte[] getSigAlgParams();
+ public abstract byte[] getSignature();
+ public abstract Principal getSubjectDN();
+ public abstract boolean[] getSubjectUniqueID();
+ public abstract byte[] getTBSCertificate() throws CertificateEncodingException;
+ public abstract int getVersion();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509Extension.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509Extension.java
new file mode 100644
index 000000000..20855be1e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/cert/X509Extension.java
@@ -0,0 +1,12 @@
+
+package java.security.cert;
+
+import java.util.Set;
+
+public interface X509Extension
+{
+ public abstract Set getCriticalExtensionOIDs();
+ public abstract byte[] getExtensionValue(String oid);
+ public abstract Set getNonCriticalExtensionOIDs();
+ public abstract boolean hasUnsupportedCriticalExtension();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
new file mode 100644
index 000000000..0fbb0fb17
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
@@ -0,0 +1,67 @@
+
+package java.security.interfaces;
+
+import java.math.BigInteger;
+import java.security.spec.RSAOtherPrimeInfo;
+
+/**
+ * The interface to an RSA multi-prime private key, as defined in the
+ * PKCS#1 v2.1, using the Chinese Remainder Theorem (CRT) information values.
+ *
+ * @since 1.4
+ * @see RSAPrivateKeySpec, RSAMultiPrimePrivateCrtKeySpec, RSAPrivateKey,
+ * RSAPrivateCrtKey
+ */
+public interface RSAMultiPrimePrivateCrtKey
+extends RSAPrivateKey
+{
+ /**
+ * Returns the public exponent.
+ *
+ * @returns the public exponent.
+ */
+ public BigInteger getPublicExponent();
+
+ /**
+ * Returns the primeP.
+ *
+ * @returns the primeP.
+ */
+ public BigInteger getPrimeP();
+
+ /**
+ * Returns the primeQ.
+ *
+ * @returns the primeQ.
+ */
+ public BigInteger getPrimeQ();
+
+ /**
+ * Returns the primeExponentP.
+ *
+ * @returns the primeExponentP.
+ */
+ public BigInteger getPrimeExponentP();
+
+ /**
+ * Returns the primeExponentQ.
+ *
+ * @returns the primeExponentQ.
+ */
+ public BigInteger getPrimeExponentQ();
+
+ /**
+ * Returns the crtCoefficient.
+ *
+ * @returns the crtCoefficient.
+ */
+ public BigInteger getCrtCoefficient();
+
+ /**
+ * Returns the otherPrimeInfo or null if there are only two prime
+ * factors (p and q).
+ *
+ * @returns the otherPrimeInfo.
+ */
+ public RSAOtherPrimeInfo[] getOtherPrimeInfo();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPrivateCrtKey.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPrivateCrtKey.java
new file mode 100644
index 000000000..81855907c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPrivateCrtKey.java
@@ -0,0 +1,16 @@
+
+package java.security.interfaces;
+
+import java.math.BigInteger;
+
+public interface RSAPrivateCrtKey extends RSAPrivateKey
+{
+ public static final long serialVersionUID = 6034044314589513430L;
+
+ public abstract BigInteger getCrtCoefficient();
+ public abstract BigInteger getPrimeExponentP();
+ public abstract BigInteger getPrimeExponentQ();
+ public abstract BigInteger getPrimeP();
+ public abstract BigInteger getPrimeQ();
+ public abstract BigInteger getPublicExponent();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPrivateKey.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPrivateKey.java
new file mode 100644
index 000000000..9b37eef93
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPrivateKey.java
@@ -0,0 +1,13 @@
+
+package java.security.interfaces;
+
+import java.math.BigInteger;
+import java.security.PrivateKey;
+
+public interface RSAPrivateKey extends PrivateKey
+{
+ public static final long serialVersionUID = 6034044314589513430L;
+
+ public abstract BigInteger getModulus();
+ public abstract BigInteger getPrivateExponent();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPublicKey.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPublicKey.java
new file mode 100644
index 000000000..6ae00ec2c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/interfaces/RSAPublicKey.java
@@ -0,0 +1,13 @@
+
+package java.security.interfaces;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+
+public interface RSAPublicKey extends PublicKey
+{
+ public static final long serialVersionUID = 7187392471159151072L;
+
+ public abstract BigInteger getModulus();
+ public abstract BigInteger getPublicExponent();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/AlgorithmParameterSpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/AlgorithmParameterSpec.java
new file mode 100644
index 000000000..37a03e9b2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/AlgorithmParameterSpec.java
@@ -0,0 +1,6 @@
+
+package java.security.spec;
+
+public interface AlgorithmParameterSpec
+{
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAParameterSpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAParameterSpec.java
new file mode 100644
index 000000000..a3897f8a6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAParameterSpec.java
@@ -0,0 +1,34 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+import java.security.interfaces.DSAParams;
+
+public class DSAParameterSpec implements AlgorithmParameterSpec, DSAParams
+{
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+
+ public DSAParameterSpec(BigInteger p, BigInteger q, BigInteger g)
+ {
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAPrivateKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAPrivateKeySpec.java
new file mode 100644
index 000000000..ff5febef6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAPrivateKeySpec.java
@@ -0,0 +1,40 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+public class DSAPrivateKeySpec implements KeySpec
+{
+ private BigInteger x;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+
+ public DSAPrivateKeySpec(BigInteger x, BigInteger p, BigInteger q, BigInteger g)
+ {
+ this.x = x;
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAPublicKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAPublicKeySpec.java
new file mode 100644
index 000000000..f8ca36792
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/DSAPublicKeySpec.java
@@ -0,0 +1,40 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+public class DSAPublicKeySpec implements KeySpec
+{
+ private BigInteger y;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+
+ public DSAPublicKeySpec(BigInteger y, BigInteger p, BigInteger q, BigInteger g)
+ {
+ this.y = y;
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/EncodedKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/EncodedKeySpec.java
new file mode 100644
index 000000000..7295460f0
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/EncodedKeySpec.java
@@ -0,0 +1,19 @@
+
+package java.security.spec;
+
+public abstract class EncodedKeySpec implements KeySpec
+{
+ private byte[] encodedKey;
+
+ public EncodedKeySpec(byte[] encodedKey)
+ {
+ this.encodedKey = (byte[])encodedKey.clone();
+ }
+
+ public byte[] getEncoded()
+ {
+ return (byte[])encodedKey.clone();
+ }
+
+ public abstract String getFormat();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/InvalidKeySpecException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/InvalidKeySpecException.java
new file mode 100644
index 000000000..cb29aee38
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/InvalidKeySpecException.java
@@ -0,0 +1,16 @@
+
+package java.security.spec;
+
+import java.security.GeneralSecurityException;
+
+public class InvalidKeySpecException extends GeneralSecurityException
+{
+ public InvalidKeySpecException()
+ {
+ }
+
+ public InvalidKeySpecException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/InvalidParameterSpecException.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/InvalidParameterSpecException.java
new file mode 100644
index 000000000..c8303edda
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/InvalidParameterSpecException.java
@@ -0,0 +1,16 @@
+
+package java.security.spec;
+
+import java.security.GeneralSecurityException;
+
+public class InvalidParameterSpecException extends GeneralSecurityException
+{
+ public InvalidParameterSpecException()
+ {
+ }
+
+ public InvalidParameterSpecException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/KeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/KeySpec.java
new file mode 100644
index 000000000..cfa7cb92f
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/KeySpec.java
@@ -0,0 +1,6 @@
+
+package java.security.spec;
+
+public interface KeySpec
+{
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/PKCS8EncodedKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/PKCS8EncodedKeySpec.java
new file mode 100644
index 000000000..10c5f66c2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/PKCS8EncodedKeySpec.java
@@ -0,0 +1,20 @@
+
+package java.security.spec;
+
+public class PKCS8EncodedKeySpec extends EncodedKeySpec
+{
+ public PKCS8EncodedKeySpec(byte[] encodedKey)
+ {
+ super(encodedKey);
+ }
+
+ public byte[] getEncoded()
+ {
+ return super.getEncoded();
+ }
+
+ public final String getFormat()
+ {
+ return "PKCS#8";
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/PSSParameterSpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/PSSParameterSpec.java
new file mode 100644
index 000000000..c4b4989cd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/PSSParameterSpec.java
@@ -0,0 +1,45 @@
+
+package java.security.spec;
+
+/**
+ * This class specifies a parameter spec for RSA PSS encoding scheme,
+ * as defined in the PKCS#1 v2.1.
+ *
+ * @since 1.4
+ * @see AlgorithmParameterSpec, Signature
+ */
+public class PSSParameterSpec
+ extends Object
+ implements AlgorithmParameterSpec
+{
+ private int saltLen;
+
+ /**
+ * Creates a new PSSParameterSpec given the salt length as defined
+ * in PKCS#1.
+ *
+ * @param saltLen - the length of salt in bits to be used in PKCS#1
+ * PSS encoding.
+ * @throws IllegalArgumentException - if saltLen is less than 0.
+ */
+ public PSSParameterSpec(int saltLen)
+ {
+ if ( saltLen < 0 )
+ {
+ throw new IllegalArgumentException("Salt length must be >= 0");
+ }
+
+ this.saltLen = saltLen;
+ }
+
+ /**
+ * Returns the salt length in bits.
+ *
+ * @returns the salt length.
+ */
+ public int getSaltLength()
+ {
+ return saltLen;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAKeyGenParameterSpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAKeyGenParameterSpec.java
new file mode 100644
index 000000000..756c6c0fd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAKeyGenParameterSpec.java
@@ -0,0 +1,35 @@
+package java.security.spec;
+
+import java.math.BigInteger;
+
+/**
+ * specifies parameters to be used for the generation of
+ * a RSA key pair.
+ */
+public class RSAKeyGenParameterSpec
+ implements AlgorithmParameterSpec
+{
+ static BigInteger F0 = BigInteger.valueOf(3);
+ static BigInteger F4 = BigInteger.valueOf(65537);
+
+ private int keysize;
+ private BigInteger publicExponent;
+
+ public RSAKeyGenParameterSpec(
+ int keysize,
+ BigInteger publicExponent)
+ {
+ this.keysize = keysize;
+ this.publicExponent = publicExponent;
+ }
+
+ public int getKeysize()
+ {
+ return keysize;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java
new file mode 100644
index 000000000..53c3a8a51
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java
@@ -0,0 +1,159 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+/**
+ * This class specifies an RSA multi-prime private key, as defined in
+ * the PKCS#1 v2.1, using the Chinese Remainder Theorem (CRT) information
+ * values for efficiency.
+ *
+ * @since 1.4
+ * @see Key, KeyFactory, KeySpec, PKCS8EncodedKeySpec, RSAPrivateKeySpec,
+ * RSAPublicKeySpec, RSAOtherPrimeInfo
+ */
+public class RSAMultiPrimePrivateCrtKeySpec
+ extends RSAPrivateKeySpec
+{
+ private BigInteger publicExponent;
+ private BigInteger privateExponent;
+ private BigInteger primeP;
+ private BigInteger primeQ;
+ private BigInteger primeExponentP;
+ private BigInteger primeExponentQ;
+ private BigInteger crtCoefficient;
+ private RSAOtherPrimeInfo[] otherPrimeInfo;
+
+ /**
+ * Creates a new RSAMultiPrimePrivateCrtKeySpec given the modulus,
+ * publicExponent, privateExponent, primeP, primeQ, primeExponentP,
+ * primeExponentQ, crtCoefficient, and otherPrimeInfo as defined in
+ * PKCS#1 v2.1.
+ *
+ * Note that otherPrimeInfo is cloned when constructing this object.
+ *
+ * @param modulus - the modulus n.
+ * @param publicExponent - the public exponent e.
+ * @param privateExponent - the private exponent d.
+ * @param primeP - the prime factor p of n.
+ * @param primeQ - the prime factor q of n.
+ * @param primeExponentP - this is d mod (p-1).
+ * @param primeExponentQ - this is d mod (q-1).
+ * @param crtCoefficient - the Chinese Remainder Theorem coefficient q-1
+ * mod p.
+ * @param otherPrimeInfo - triplets of the rest of primes, null can be
+ * specified if there are only two prime factors (p and q).
+ * @throws NullPointerException - if any of the parameters, i.e. modulus,
+ * publicExponent, privateExponent, primeP, primeQ, primeExponentP,
+ * primeExponentQ, crtCoefficient, is null.
+ * @throws IllegalArgumentException - if an empty, i.e. 0-length,
+ * otherPrimeInfo is specified.
+ */
+ public RSAMultiPrimePrivateCrtKeySpec(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger primeP,
+ BigInteger primeQ,
+ BigInteger primeExponentP,
+ BigInteger primeExponentQ,
+ BigInteger crtCoefficient,
+ RSAOtherPrimeInfo[] otherPrimeInfo)
+ {
+ super(modulus, privateExponent);
+
+ if ( publicExponent == null || primeP == null || primeQ == null
+ || primeExponentP == null || primeExponentQ == null
+ || crtCoefficient == null )
+ {
+ throw new NullPointerException("Invalid null argument");
+ }
+
+ if ( otherPrimeInfo != null )
+ {
+ if ( otherPrimeInfo.length == 0 )
+ {
+ throw new IllegalArgumentException("Invalid length for otherPrimeInfo");
+ }
+
+ this.otherPrimeInfo = (RSAOtherPrimeInfo[])otherPrimeInfo.clone();
+ }
+ }
+
+ /**
+ * Returns the public exponent.
+ *
+ * @returns the public exponent.
+ */
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * Returns the primeP.
+ *
+ * @returns the primeP.
+ */
+ public BigInteger getPrimeP()
+ {
+ return primeP;
+ }
+
+ /**
+ * Returns the primeQ.
+ *
+ * @returns the primeQ.
+ */
+ public BigInteger getPrimeQ()
+ {
+ return primeQ;
+ }
+
+ /**
+ * Returns the primeExponentP.
+ *
+ * @returns the primeExponentP.
+ */
+ public BigInteger getPrimeExponentP()
+ {
+ return primeExponentP;
+ }
+
+ /**
+ * Returns the primeExponentQ.
+ *
+ * @returns the primeExponentQ.
+ */
+ public BigInteger getPrimeExponentQ()
+ {
+ return primeExponentQ;
+ }
+
+ /**
+ * Returns the crtCofficient.
+ *
+ * @returns the crtCofficient.
+ */
+ public BigInteger getCrtCoefficient()
+ {
+ return crtCoefficient;
+ }
+
+ /**
+ * Returns a copy of the otherPrimeInfo or null if there are only
+ * two prime factors (p and q).
+ *
+ * @returns the otherPrimeInfo.
+ */
+ public RSAOtherPrimeInfo[] getOtherPrimeInfo()
+ {
+ if ( otherPrimeInfo != null )
+ {
+ return (RSAOtherPrimeInfo[])otherPrimeInfo.clone();
+ }
+
+ return null;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAOtherPrimeInfo.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAOtherPrimeInfo.java
new file mode 100644
index 000000000..4d0e1468e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAOtherPrimeInfo.java
@@ -0,0 +1,80 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+/**
+ * This class represents the triplet (prime, exponent, and coefficient)
+ * inside RSA's OtherPrimeInfo structure, as defined in the PKCS#1 v2.1.
+ * The ASN.1 syntax of RSA's OtherPrimeInfo is as follows:
+ *
+ *
+ * OtherPrimeInfo ::= SEQUENCE {
+ * prime INTEGER,
+ * exponent INTEGER,
+ * coefficient INTEGER
+ * }
+ *
+ */
+public class RSAOtherPrimeInfo
+extends Object
+{
+ private BigInteger prime;
+ private BigInteger primeExponent;
+ private BigInteger crtCoefficient;
+
+ /**
+ * Creates a new RSAOtherPrimeInfo given the prime, primeExponent,
+ * and crtCoefficient as defined in PKCS#1.
+ *
+ * @param prime - the prime factor of n.
+ * @param primeExponent - the exponent.
+ * @param crtCoefficient - the Chinese Remainder Theorem coefficient.
+ * @throws NullPointerException - if any of the parameters, i.e. prime,
+ * primeExponent, crtCoefficient, is null.
+ */
+ public RSAOtherPrimeInfo(
+ BigInteger prime,
+ BigInteger primeExponent,
+ BigInteger crtCoefficient)
+ {
+ if ( prime == null || primeExponent == null || crtCoefficient == null )
+ {
+ throw new NullPointerException("Null parameter");
+ }
+
+ this.prime = prime;
+ this.primeExponent = primeExponent;
+ this.crtCoefficient = crtCoefficient;
+ }
+
+ /**
+ * Returns the prime.
+ *
+ * @returns the prime.
+ */
+ public final BigInteger getPrime()
+ {
+ return prime;
+ }
+
+ /**
+ * Returns the prime's exponent.
+ *
+ * @returns the primeExponent.
+ */
+ public final BigInteger getExponent()
+ {
+ return primeExponent;
+ }
+
+ /**
+ * Returns the prime's crtCoefficient.
+ *
+ * @returns the crtCoefficient.
+ */
+ public final BigInteger getCrtCoefficient()
+ {
+ return crtCoefficient;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPrivateCrtKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPrivateCrtKeySpec.java
new file mode 100644
index 000000000..b9d450ad7
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPrivateCrtKeySpec.java
@@ -0,0 +1,64 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+public class RSAPrivateCrtKeySpec extends RSAPrivateKeySpec
+{
+ private BigInteger publicExponent;
+ private BigInteger primeP;
+ private BigInteger primeQ;
+ private BigInteger primeExponentP;
+ private BigInteger primeExponentQ;
+ private BigInteger crtCoefficient;
+
+ public RSAPrivateCrtKeySpec(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger primeP,
+ BigInteger primeQ,
+ BigInteger primeExponentP,
+ BigInteger primeExponentQ,
+ BigInteger crtCoefficient)
+ {
+ super(modulus, privateExponent);
+
+ this.publicExponent = publicExponent;
+ this.primeP = primeP;
+ this.primeQ = primeQ;
+ this.primeExponentP = primeExponentP;
+ this.primeExponentQ = primeExponentQ;
+ this.crtCoefficient = crtCoefficient;
+ }
+
+ public BigInteger getCrtCoefficient()
+ {
+ return crtCoefficient;
+ }
+
+ public BigInteger getPrimeExponentP()
+ {
+ return primeExponentP;
+ }
+
+ public BigInteger getPrimeExponentQ()
+ {
+ return primeExponentQ;
+ }
+
+ public BigInteger getPrimeP()
+ {
+ return primeP;
+ }
+
+ public BigInteger getPrimeQ()
+ {
+ return primeQ;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPrivateKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPrivateKeySpec.java
new file mode 100644
index 000000000..88dc4c159
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPrivateKeySpec.java
@@ -0,0 +1,28 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+public class RSAPrivateKeySpec extends Object implements KeySpec
+{
+ private BigInteger modulus;
+ private BigInteger privateExponent;
+
+ public RSAPrivateKeySpec(
+ BigInteger modulus,
+ BigInteger privateExponent)
+ {
+ this.modulus = modulus;
+ this.privateExponent = privateExponent;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPrivateExponent()
+ {
+ return privateExponent;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPublicKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPublicKeySpec.java
new file mode 100644
index 000000000..b3a367e7e
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/RSAPublicKeySpec.java
@@ -0,0 +1,28 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+public class RSAPublicKeySpec extends Object implements KeySpec
+{
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+
+ public RSAPublicKeySpec(
+ BigInteger modulus,
+ BigInteger publicExponent)
+ {
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/X509EncodedKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/X509EncodedKeySpec.java
new file mode 100644
index 000000000..1d095b11d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/security/spec/X509EncodedKeySpec.java
@@ -0,0 +1,20 @@
+
+package java.security.spec;
+
+public class X509EncodedKeySpec extends EncodedKeySpec
+{
+ public X509EncodedKeySpec(byte[] encodedKey)
+ {
+ super(encodedKey);
+ }
+
+ public byte[] getEncoded()
+ {
+ return super.getEncoded();
+ }
+
+ public final String getFormat()
+ {
+ return "X.509";
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/java/util/AbstractCollection.java b/libraries/spongycastle/core/src/main/jdk1.1/java/util/AbstractCollection.java
new file mode 100644
index 000000000..0ea61b772
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/java/util/AbstractCollection.java
@@ -0,0 +1,242 @@
+package java.util;
+
+import java.lang.reflect.Array;
+/**
+ * Title:
+ * Description:
+ * Copyright: Copyright (c) 2001
+ * Company:
+ * @version 1.0
+ */
+
+
+public abstract class AbstractCollection implements Collection
+ {
+ protected AbstractCollection()
+ {
+ }
+
+ public abstract Iterator iterator();
+
+ public abstract int size();
+
+ public boolean isEmpty()
+ {
+ return size()==0;
+ }
+
+ public boolean contains(Object o)
+ {
+ Iterator it=iterator();
+ while(it.hasNext())
+ {
+ Object e=it.next();
+ if(o==null)
+ {
+ if(e==null)
+ return true;
+ }
+ else
+ {
+ if(o.equals(e))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Object[] toArray()
+ {
+ Object[] arObjects=new Object[size()];
+ Iterator it=iterator();
+ int i=0;
+ while(it.hasNext())
+ {
+ arObjects[i++]=it.next();
+ }
+ return arObjects;
+ }
+
+ public Object[] toArray(Object[] a) throws NullPointerException,ArrayStoreException
+ //TODO: Check if this is realy compatible to SUN!!!
+ {
+ if(a==null)
+ throw new NullPointerException();
+
+ if (isEmpty()) return a;
+ Object[] arObjects=null;
+ int size=size();
+ if(a.length
+ *
+ *
+ *
+ * Each side should derive a session key from the keying material returned by {@link #calculateKeyingMaterial()}.
+ * The caller is responsible for deriving the session key using a secure key derivation function (KDF).
+ *
+ *
+ * Round 3 is an optional key confirmation process.
+ * If you do not execute round 3, then there is no assurance that both participants are using the same key.
+ * (i.e. if the participants used different passwords, then their session keys will differ.)
+ *
+ *
+ * If the round 3 validation succeeds, then the keys are guaranteed to be the same on both sides.
+ *
+ *
+ * The symmetric design can easily support the asymmetric cases when one party initiates the communication.
+ * e.g. Sometimes the round1 payload and round2 payload may be sent in one pass.
+ * Also, in some cases, the key confirmation payload can be sent together with the round2 payload.
+ * These are the trivial techniques to optimize the communication.
+ *
+ *
+ * The key confirmation process is implemented as specified in
+ * NIST SP 800-56A Revision 1,
+ * Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes.
+ *
+ *
+ * This class is stateful and NOT threadsafe.
+ * Each instance should only be used for ONE complete J-PAKE exchange
+ * (i.e. a new {@link JPAKEParticipant} should be constructed for each new J-PAKE exchange).
+ *
+ *
+ * See {@link JPAKEExample} for example usage.
+ */
+public class JPAKEParticipant
+{
+ /*
+ * Possible internal states. Used for state checking.
+ */
+
+ public static final int STATE_INITIALIZED = 0;
+ public static final int STATE_ROUND_1_CREATED = 10;
+ public static final int STATE_ROUND_1_VALIDATED = 20;
+ public static final int STATE_ROUND_2_CREATED = 30;
+ public static final int STATE_ROUND_2_VALIDATED = 40;
+ public static final int STATE_KEY_CALCULATED = 50;
+ public static final int STATE_ROUND_3_CREATED = 60;
+ public static final int STATE_ROUND_3_VALIDATED = 70;
+
+ /**
+ * Unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ */
+ private String participantId;
+
+ /**
+ * Shared secret. This only contains the secret between construction
+ * and the call to {@link #calculateKeyingMaterial()}.
+ *
+ * i.e. When {@link #calculateKeyingMaterial()} is called, this buffer overwritten with 0's,
+ * and the field is set to null.
+ */
+ private char[] password;
+
+ /**
+ * Digest to use during calculations.
+ */
+ private Digest digest;
+
+ /**
+ * Source of secure random data.
+ */
+ private SecureRandom random;
+
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+
+ /**
+ * The participantId of the other participant in this exchange.
+ */
+ private String partnerParticipantId;
+
+ /**
+ * Alice's x1 or Bob's x3.
+ */
+ private BigInteger x1;
+ /**
+ * Alice's x2 or Bob's x4.
+ */
+ private BigInteger x2;
+ /**
+ * Alice's g^x1 or Bob's g^x3.
+ */
+ private BigInteger gx1;
+ /**
+ * Alice's g^x2 or Bob's g^x4.
+ */
+ private BigInteger gx2;
+ /**
+ * Alice's g^x3 or Bob's g^x1.
+ */
+ private BigInteger gx3;
+ /**
+ * Alice's g^x4 or Bob's g^x2.
+ */
+ private BigInteger gx4;
+ /**
+ * Alice's B or Bob's A.
+ */
+ private BigInteger b;
+
+ /**
+ * The current state.
+ * See the STATE_* constants for possible values.
+ */
+ private int state;
+
+ /**
+ * Convenience constructor for a new {@link JPAKEParticipant} that uses
+ * the {@link JPAKEPrimeOrderGroups#NIST_3072} prime order group,
+ * a SHA-256 digest, and a default {@link SecureRandom} implementation.
+ *
+ * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
+ *
+ * @param participantId unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ * @param password shared secret.
+ * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called).
+ * Caller should clear the input password as soon as possible.
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if password is empty
+ */
+ public JPAKEParticipant(
+ String participantId,
+ char[] password)
+ {
+ this(
+ participantId,
+ password,
+ JPAKEPrimeOrderGroups.NIST_3072);
+ }
+
+
+ /**
+ * Convenience constructor for a new {@link JPAKEParticipant} that uses
+ * a SHA-256 digest and a default {@link SecureRandom} implementation.
+ *
+ * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
+ *
+ * @param participantId unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ * @param password shared secret.
+ * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called).
+ * Caller should clear the input password as soon as possible.
+ * @param group prime order group.
+ * See {@link JPAKEPrimeOrderGroups} for standard groups
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if password is empty
+ */
+ public JPAKEParticipant(
+ String participantId,
+ char[] password,
+ JPAKEPrimeOrderGroup group)
+ {
+ this(
+ participantId,
+ password,
+ group,
+ new SHA256Digest(),
+ new SecureRandom());
+ }
+
+
+ /**
+ * Construct a new {@link JPAKEParticipant}.
+ *
+ * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
+ *
+ * @param participantId unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ * @param password shared secret.
+ * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called).
+ * Caller should clear the input password as soon as possible.
+ * @param group prime order group.
+ * See {@link JPAKEPrimeOrderGroups} for standard groups
+ * @param digest digest to use during zero knowledge proofs and key confirmation (SHA-256 or stronger preferred)
+ * @param random source of secure random data for x1 and x2, and for the zero knowledge proofs
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if password is empty
+ */
+ public JPAKEParticipant(
+ String participantId,
+ char[] password,
+ JPAKEPrimeOrderGroup group,
+ Digest digest,
+ SecureRandom random)
+ {
+ JPAKEUtil.validateNotNull(participantId, "participantId");
+ JPAKEUtil.validateNotNull(password, "password");
+ JPAKEUtil.validateNotNull(group, "p");
+ JPAKEUtil.validateNotNull(digest, "digest");
+ JPAKEUtil.validateNotNull(random, "random");
+ if (password.length == 0)
+ {
+ throw new IllegalArgumentException("Password must not be empty.");
+ }
+
+ this.participantId = participantId;
+
+ /*
+ * Create a defensive copy so as to fully encapsulate the password.
+ *
+ * This array will contain the password for the lifetime of this
+ * participant BEFORE {@link #calculateKeyingMaterial()} is called.
+ *
+ * i.e. When {@link #calculateKeyingMaterial()} is called, the array will be cleared
+ * in order to remove the password from memory.
+ *
+ * The caller is responsible for clearing the original password array
+ * given as input to this constructor.
+ */
+ this.password = Arrays.copyOf(password, password.length);
+
+ this.p = group.getP();
+ this.q = group.getQ();
+ this.g = group.getG();
+
+ this.digest = digest;
+ this.random = random;
+
+ this.state = STATE_INITIALIZED;
+ }
+
+ /**
+ * Gets the current state of this participant.
+ * See the STATE_* constants for possible values.
+ */
+ public int getState()
+ {
+ return this.state;
+ }
+
+ /**
+ * Creates and returns the payload to send to the other participant during round 1.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_CREATED}.
+ */
+ public JPAKERound1Payload createRound1PayloadToSend()
+ {
+ if (this.state >= STATE_ROUND_1_CREATED)
+ {
+ throw new IllegalStateException("Round1 payload already created for " + participantId);
+ }
+
+ this.x1 = JPAKEUtil.generateX1(q, random);
+ this.x2 = JPAKEUtil.generateX2(q, random);
+
+ this.gx1 = JPAKEUtil.calculateGx(p, g, x1);
+ this.gx2 = JPAKEUtil.calculateGx(p, g, x2);
+ BigInteger[] knowledgeProofForX1 = JPAKEUtil.calculateZeroKnowledgeProof(p, q, g, gx1, x1, participantId, digest, random);
+ BigInteger[] knowledgeProofForX2 = JPAKEUtil.calculateZeroKnowledgeProof(p, q, g, gx2, x2, participantId, digest, random);
+
+ this.state = STATE_ROUND_1_CREATED;
+
+ return new JPAKERound1Payload(participantId, gx1, gx2, knowledgeProofForX1, knowledgeProofForX2);
+ }
+
+ /**
+ * Validates the payload received from the other participant during round 1.
+ *
+ *
+ * Must be called prior to {@link #createRound2PayloadToSend()}.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_VALIDATED}.
+ *
+ * @throws CryptoException if validation fails.
+ * @throws IllegalStateException if called multiple times.
+ */
+ public void validateRound1PayloadReceived(JPAKERound1Payload round1PayloadReceived)
+ throws CryptoException
+ {
+ if (this.state >= STATE_ROUND_1_VALIDATED)
+ {
+ throw new IllegalStateException("Validation already attempted for round1 payload for" + participantId);
+ }
+ this.partnerParticipantId = round1PayloadReceived.getParticipantId();
+ this.gx3 = round1PayloadReceived.getGx1();
+ this.gx4 = round1PayloadReceived.getGx2();
+
+ BigInteger[] knowledgeProofForX3 = round1PayloadReceived.getKnowledgeProofForX1();
+ BigInteger[] knowledgeProofForX4 = round1PayloadReceived.getKnowledgeProofForX2();
+
+ JPAKEUtil.validateParticipantIdsDiffer(participantId, round1PayloadReceived.getParticipantId());
+ JPAKEUtil.validateGx4(gx4);
+ JPAKEUtil.validateZeroKnowledgeProof(p, q, g, gx3, knowledgeProofForX3, round1PayloadReceived.getParticipantId(), digest);
+ JPAKEUtil.validateZeroKnowledgeProof(p, q, g, gx4, knowledgeProofForX4, round1PayloadReceived.getParticipantId(), digest);
+
+ this.state = STATE_ROUND_1_VALIDATED;
+ }
+
+ /**
+ * Creates and returns the payload to send to the other participant during round 2.
+ *
+ *
+ * {@link #validateRound1PayloadReceived(JPAKERound1Payload)} must be called prior to this method.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_CREATED}.
+ *
+ * @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times
+ */
+ public JPAKERound2Payload createRound2PayloadToSend()
+ {
+ if (this.state >= STATE_ROUND_2_CREATED)
+ {
+ throw new IllegalStateException("Round2 payload already created for " + this.participantId);
+ }
+ if (this.state < STATE_ROUND_1_VALIDATED)
+ {
+ throw new IllegalStateException("Round1 payload must be validated prior to creating Round2 payload for " + this.participantId);
+ }
+ BigInteger gA = JPAKEUtil.calculateGA(p, gx1, gx3, gx4);
+ BigInteger s = JPAKEUtil.calculateS(password);
+ BigInteger x2s = JPAKEUtil.calculateX2s(q, x2, s);
+ BigInteger A = JPAKEUtil.calculateA(p, q, gA, x2s);
+ BigInteger[] knowledgeProofForX2s = JPAKEUtil.calculateZeroKnowledgeProof(p, q, gA, A, x2s, participantId, digest, random);
+
+ this.state = STATE_ROUND_2_CREATED;
+
+ return new JPAKERound2Payload(participantId, A, knowledgeProofForX2s);
+ }
+
+ /**
+ * Validates the payload received from the other participant during round 2.
+ *
+ *
+ * Note that this DOES NOT detect a non-common password.
+ * The only indication of a non-common password is through derivation
+ * of different keys (which can be detected explicitly by executing round 3 and round 4)
+ *
+ *
+ * Must be called prior to {@link #calculateKeyingMaterial()}.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_VALIDATED}.
+ *
+ * @throws CryptoException if validation fails.
+ * @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times
+ */
+ public void validateRound2PayloadReceived(JPAKERound2Payload round2PayloadReceived)
+ throws CryptoException
+ {
+ if (this.state >= STATE_ROUND_2_VALIDATED)
+ {
+ throw new IllegalStateException("Validation already attempted for round2 payload for" + participantId);
+ }
+ if (this.state < STATE_ROUND_1_VALIDATED)
+ {
+ throw new IllegalStateException("Round1 payload must be validated prior to validating Round2 payload for " + this.participantId);
+ }
+ BigInteger gB = JPAKEUtil.calculateGA(p, gx3, gx1, gx2);
+ this.b = round2PayloadReceived.getA();
+ BigInteger[] knowledgeProofForX4s = round2PayloadReceived.getKnowledgeProofForX2s();
+
+ JPAKEUtil.validateParticipantIdsDiffer(participantId, round2PayloadReceived.getParticipantId());
+ JPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, round2PayloadReceived.getParticipantId());
+ JPAKEUtil.validateGa(gB);
+ JPAKEUtil.validateZeroKnowledgeProof(p, q, gB, b, knowledgeProofForX4s, round2PayloadReceived.getParticipantId(), digest);
+
+ this.state = STATE_ROUND_2_VALIDATED;
+ }
+
+ /**
+ * Calculates and returns the key material.
+ * A session key must be derived from this key material using a secure key derivation function (KDF).
+ * The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}).
+ *
+ *
+ * The keying material will be identical for each participant if and only if
+ * each participant's password is the same. i.e. If the participants do not
+ * share the same password, then each participant will derive a different key.
+ * Therefore, if you immediately start using a key derived from
+ * the keying material, then you must handle detection of incorrect keys.
+ * If you want to handle this detection explicitly, you can optionally perform
+ * rounds 3 and 4. See {@link JPAKEParticipant} for details on how to execute
+ * rounds 3 and 4.
+ *
+ *
+ * The keying material will be in the range [0, p-1].
+ *
+ *
+ * {@link #validateRound2PayloadReceived(JPAKERound2Payload)} must be called prior to this method.
+ *
+ *
+ * As a side effect, the internal {@link #password} array is cleared, since it is no longer needed.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_KEY_CALCULATED}.
+ *
+ * @throws IllegalStateException if called prior to {@link #validateRound2PayloadReceived(JPAKERound2Payload)},
+ * or if called multiple times.
+ */
+ public BigInteger calculateKeyingMaterial()
+ {
+ if (this.state >= STATE_KEY_CALCULATED)
+ {
+ throw new IllegalStateException("Key already calculated for " + participantId);
+ }
+ if (this.state < STATE_ROUND_2_VALIDATED)
+ {
+ throw new IllegalStateException("Round2 payload must be validated prior to creating key for " + participantId);
+ }
+ BigInteger s = JPAKEUtil.calculateS(password);
+
+ /*
+ * Clear the password array from memory, since we don't need it anymore.
+ *
+ * Also set the field to null as a flag to indicate that the key has already been calculated.
+ */
+ Arrays.fill(password, (char)0);
+ this.password = null;
+
+ BigInteger keyingMaterial = JPAKEUtil.calculateKeyingMaterial(p, q, gx4, x2, s, b);
+
+ /*
+ * Clear the ephemeral private key fields as well.
+ * Note that we're relying on the garbage collector to do its job to clean these up.
+ * The old objects will hang around in memory until the garbage collector destroys them.
+ *
+ * If the ephemeral private keys x1 and x2 are leaked,
+ * the attacker might be able to brute-force the password.
+ */
+ this.x1 = null;
+ this.x2 = null;
+ this.b = null;
+
+ /*
+ * Do not clear gx* yet, since those are needed by round 3.
+ */
+
+ this.state = STATE_KEY_CALCULATED;
+
+ return keyingMaterial;
+ }
+
+
+ /**
+ * Creates and returns the payload to send to the other participant during round 3.
+ *
+ *
+ * See {@link JPAKEParticipant} for more details on round 3.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_CREATED}.
+ *
+ * @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}.
+ * @throws IllegalStateException if called prior to {@link #calculateKeyingMaterial()}, or multiple times
+ */
+ public JPAKERound3Payload createRound3PayloadToSend(BigInteger keyingMaterial)
+ {
+ if (this.state >= STATE_ROUND_3_CREATED)
+ {
+ throw new IllegalStateException("Round3 payload already created for " + this.participantId);
+ }
+ if (this.state < STATE_KEY_CALCULATED)
+ {
+ throw new IllegalStateException("Keying material must be calculated prior to creating Round3 payload for " + this.participantId);
+ }
+
+ BigInteger macTag = JPAKEUtil.calculateMacTag(
+ this.participantId,
+ this.partnerParticipantId,
+ this.gx1,
+ this.gx2,
+ this.gx3,
+ this.gx4,
+ keyingMaterial,
+ this.digest);
+
+ this.state = STATE_ROUND_3_CREATED;
+
+ return new JPAKERound3Payload(participantId, macTag);
+ }
+
+ /**
+ * Validates the payload received from the other participant during round 3.
+ *
+ *
+ * See {@link JPAKEParticipant} for more details on round 3.
+ *
+ *
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_VALIDATED}.
+ *
+ * @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}.
+ * @throws CryptoException if validation fails.
+ * @throws IllegalStateException if called prior to {@link #calculateKeyingMaterial()}, or multiple times
+ */
+ public void validateRound3PayloadReceived(JPAKERound3Payload round3PayloadReceived, BigInteger keyingMaterial)
+ throws CryptoException
+ {
+ if (this.state >= STATE_ROUND_3_VALIDATED)
+ {
+ throw new IllegalStateException("Validation already attempted for round3 payload for" + participantId);
+ }
+ if (this.state < STATE_KEY_CALCULATED)
+ {
+ throw new IllegalStateException("Keying material must be calculated validated prior to validating Round3 payload for " + this.participantId);
+ }
+ JPAKEUtil.validateParticipantIdsDiffer(participantId, round3PayloadReceived.getParticipantId());
+ JPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, round3PayloadReceived.getParticipantId());
+
+ JPAKEUtil.validateMacTag(
+ this.participantId,
+ this.partnerParticipantId,
+ this.gx1,
+ this.gx2,
+ this.gx3,
+ this.gx4,
+ keyingMaterial,
+ this.digest,
+ round3PayloadReceived.getMacTag());
+
+
+ /*
+ * Clear the rest of the fields.
+ */
+ this.gx1 = null;
+ this.gx2 = null;
+ this.gx3 = null;
+ this.gx4 = null;
+
+ this.state = STATE_ROUND_3_VALIDATED;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
new file mode 100644
index 000000000..59e93385c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
@@ -0,0 +1,122 @@
+package org.spongycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+
+/**
+ * A pre-computed prime order group for use during a J-PAKE exchange.
+ *
+ *
+ * Typically a Schnorr group is used. In general, J-PAKE can use any prime order group
+ * that is suitable for public key cryptography, including elliptic curve cryptography.
+ *
+ *
+ * See {@link JPAKEPrimeOrderGroups} for convenient standard groups.
+ *
+ *
+ * NIST publishes
+ * many groups that can be used for the desired level of security.
+ */
+public class JPAKEPrimeOrderGroup
+{
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+
+ /**
+ * Constructs a new {@link JPAKEPrimeOrderGroup}.
+ *
+ *
+ * In general, you should use one of the pre-approved groups from
+ * {@link JPAKEPrimeOrderGroups}, rather than manually constructing one.
+ *
+ *
+ * The following basic checks are performed:
+ *
+ *
+ *
+ *
+ * The prime checks are performed using {@link BigInteger#isProbablePrime(int)},
+ * and are therefore subject to the same probability guarantees.
+ *
+ *
+ * These checks prevent trivial mistakes.
+ * However, due to the small uncertainties if p and q are not prime,
+ * advanced attacks are not prevented.
+ * Use it at your own risk.
+ *
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if any of the above validations fail
+ */
+ public JPAKEPrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g)
+ {
+ /*
+ * Don't skip the checks on user-specified groups.
+ */
+ this(p, q, g, false);
+ }
+
+ /**
+ * Internal package-private constructor used by the pre-approved
+ * groups in {@link JPAKEPrimeOrderGroups}.
+ * These pre-approved groups can avoid the expensive checks.
+ */
+ JPAKEPrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g, boolean skipChecks)
+ {
+ JPAKEUtil.validateNotNull(p, "p");
+ JPAKEUtil.validateNotNull(q, "q");
+ JPAKEUtil.validateNotNull(g, "g");
+
+ if (!skipChecks)
+ {
+ if (!p.subtract(JPAKEUtil.ONE).mod(q).equals(JPAKEUtil.ZERO))
+ {
+ throw new IllegalArgumentException("p-1 must be evenly divisible by q");
+ }
+ if (g.compareTo(BigInteger.valueOf(2)) == -1 || g.compareTo(p.subtract(JPAKEUtil.ONE)) == 1)
+ {
+ throw new IllegalArgumentException("g must be in [2, p-1]");
+ }
+ if (!g.modPow(q, p).equals(JPAKEUtil.ONE))
+ {
+ throw new IllegalArgumentException("g^q mod p must equal 1");
+ }
+ /*
+ * Note that these checks do not guarantee that p and q are prime.
+ * We just have reasonable certainty that they are prime.
+ */
+ if (!p.isProbablePrime(20))
+ {
+ throw new IllegalArgumentException("p must be prime");
+ }
+ if (!q.isProbablePrime(20))
+ {
+ throw new IllegalArgumentException("q must be prime");
+ }
+ }
+
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/digests/SkeinEngine.java b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/digests/SkeinEngine.java
new file mode 100644
index 000000000..a278ff4e4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/digests/SkeinEngine.java
@@ -0,0 +1,817 @@
+package org.spongycastle.crypto.digests;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.macs.SkeinMac;
+import org.spongycastle.crypto.params.SkeinParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
+ * sizes, based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ *
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ *
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ *
+ * This implementation is the basis for {@link SkeinDigest} and {@link SkeinMac}, implementing the
+ * parameter based configuration system that allows Skein to be adapted to multiple applications.
+ * Initialising the engine with {@link SkeinParameters} allows standard and arbitrary parameters to
+ * be applied during the Skein hash function.
+ *
+ * Implemented:
+ *
+ *
+ *
+ * Not implemented:
+ *
+ *
+ *
+ * @see SkeinParameters
+ */
+public class SkeinEngine
+ implements Memoable
+{
+ /**
+ * 256 bit block size - Skein 256
+ */
+ public static final int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
+ /**
+ * 512 bit block size - Skein 512
+ */
+ public static final int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
+ /**
+ * 1024 bit block size - Skein 1024
+ */
+ public static final int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
+
+ // Minimal at present, but more complex when tree hashing is implemented
+ private static class Configuration
+ {
+ private byte[] bytes = new byte[32];
+
+ public Configuration(long outputSizeBits)
+ {
+ // 0..3 = ASCII SHA3
+ bytes[0] = (byte)'S';
+ bytes[1] = (byte)'H';
+ bytes[2] = (byte)'A';
+ bytes[3] = (byte)'3';
+
+ // 4..5 = version number in LSB order
+ bytes[4] = 1;
+ bytes[5] = 0;
+
+ // 8..15 = output length
+ ThreefishEngine.wordToBytes(outputSizeBits, bytes, 8);
+ }
+
+ public byte[] getBytes()
+ {
+ return bytes;
+ }
+
+ }
+
+ public static class Parameter
+ {
+ private int type;
+ private byte[] value;
+
+ public Parameter(int type, byte[] value)
+ {
+ this.type = type;
+ this.value = value;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public byte[] getValue()
+ {
+ return value;
+ }
+
+ }
+
+ /**
+ * The parameter type for the Skein key.
+ */
+ private static final int PARAM_TYPE_KEY = 0;
+
+ /**
+ * The parameter type for the Skein configuration block.
+ */
+ private static final int PARAM_TYPE_CONFIG = 4;
+
+ /**
+ * The parameter type for the message.
+ */
+ private static final int PARAM_TYPE_MESSAGE = 48;
+
+ /**
+ * The parameter type for the output transformation.
+ */
+ private static final int PARAM_TYPE_OUTPUT = 63;
+
+ /**
+ * Precalculated UBI(CFG) states for common state/output combinations without key or other
+ * pre-message params.
+ */
+ private static final Hashtable INITIAL_STATES = new Hashtable();
+
+ static
+ {
+ // From Appendix C of the Skein 1.3 NIST submission
+ initialState(SKEIN_256, 128, new long[]{
+ 0xe1111906964d7260L,
+ 0x883daaa77c8d811cL,
+ 0x10080df491960f7aL,
+ 0xccf7dde5b45bc1c2L});
+
+ initialState(SKEIN_256, 160, new long[]{
+ 0x1420231472825e98L,
+ 0x2ac4e9a25a77e590L,
+ 0xd47a58568838d63eL,
+ 0x2dd2e4968586ab7dL});
+
+ initialState(SKEIN_256, 224, new long[]{
+ 0xc6098a8c9ae5ea0bL,
+ 0x876d568608c5191cL,
+ 0x99cb88d7d7f53884L,
+ 0x384bddb1aeddb5deL});
+
+ initialState(SKEIN_256, 256, new long[]{
+ 0xfc9da860d048b449L,
+ 0x2fca66479fa7d833L,
+ 0xb33bc3896656840fL,
+ 0x6a54e920fde8da69L});
+
+ initialState(SKEIN_512, 128, new long[]{
+ 0xa8bc7bf36fbf9f52L,
+ 0x1e9872cebd1af0aaL,
+ 0x309b1790b32190d3L,
+ 0xbcfbb8543f94805cL,
+ 0x0da61bcd6e31b11bL,
+ 0x1a18ebead46a32e3L,
+ 0xa2cc5b18ce84aa82L,
+ 0x6982ab289d46982dL});
+
+ initialState(SKEIN_512, 160, new long[]{
+ 0x28b81a2ae013bd91L,
+ 0xc2f11668b5bdf78fL,
+ 0x1760d8f3f6a56f12L,
+ 0x4fb747588239904fL,
+ 0x21ede07f7eaf5056L,
+ 0xd908922e63ed70b8L,
+ 0xb8ec76ffeccb52faL,
+ 0x01a47bb8a3f27a6eL});
+
+ initialState(SKEIN_512, 224, new long[]{
+ 0xccd0616248677224L,
+ 0xcba65cf3a92339efL,
+ 0x8ccd69d652ff4b64L,
+ 0x398aed7b3ab890b4L,
+ 0x0f59d1b1457d2bd0L,
+ 0x6776fe6575d4eb3dL,
+ 0x99fbc70e997413e9L,
+ 0x9e2cfccfe1c41ef7L});
+
+ initialState(SKEIN_512, 384, new long[]{
+ 0xa3f6c6bf3a75ef5fL,
+ 0xb0fef9ccfd84faa4L,
+ 0x9d77dd663d770cfeL,
+ 0xd798cbf3b468fddaL,
+ 0x1bc4a6668a0e4465L,
+ 0x7ed7d434e5807407L,
+ 0x548fc1acd4ec44d6L,
+ 0x266e17546aa18ff8L});
+
+ initialState(SKEIN_512, 512, new long[]{
+ 0x4903adff749c51ceL,
+ 0x0d95de399746df03L,
+ 0x8fd1934127c79bceL,
+ 0x9a255629ff352cb1L,
+ 0x5db62599df6ca7b0L,
+ 0xeabe394ca9d5c3f4L,
+ 0x991112c71a75b523L,
+ 0xae18a40b660fcc33L});
+ }
+
+ private static void initialState(int blockSize, int outputSize, long[] state)
+ {
+ INITIAL_STATES.put(variantIdentifier(blockSize / 8, outputSize / 8), state);
+ }
+
+ private static Integer variantIdentifier(int blockSizeBytes, int outputSizeBytes)
+ {
+ return new Integer((outputSizeBytes << 16) | blockSizeBytes);
+ }
+
+ private static class UbiTweak
+ {
+ /**
+ * Point at which position might overflow long, so switch to add with carry logic
+ */
+ private static final long LOW_RANGE = Long.MAX_VALUE - Integer.MAX_VALUE;
+
+ /**
+ * Bit 127 = final
+ */
+ private static final long T1_FINAL = 1L << 63;
+
+ /**
+ * Bit 126 = first
+ */
+ private static final long T1_FIRST = 1L << 62;
+
+ /**
+ * UBI uses a 128 bit tweak
+ */
+ private long tweak[] = new long[2];
+
+ /**
+ * Whether 64 bit position exceeded
+ */
+ private boolean extendedPosition;
+
+ public UbiTweak()
+ {
+ reset();
+ }
+
+ public void reset(UbiTweak tweak)
+ {
+ this.tweak = Arrays.clone(tweak.tweak, this.tweak);
+ this.extendedPosition = tweak.extendedPosition;
+ }
+
+ public void reset()
+ {
+ tweak[0] = 0;
+ tweak[1] = 0;
+ extendedPosition = false;
+ setFirst(true);
+ }
+
+ public void setType(int type)
+ {
+ // Bits 120..125 = type
+ tweak[1] = (tweak[1] & 0xFFFFFFC000000000L) | ((type & 0x3FL) << 56);
+ }
+
+ public int getType()
+ {
+ return (int)((tweak[1] >>> 56) & 0x3FL);
+ }
+
+ public void setFirst(boolean first)
+ {
+ if (first)
+ {
+ tweak[1] |= T1_FIRST;
+ }
+ else
+ {
+ tweak[1] &= ~T1_FIRST;
+ }
+ }
+
+ public boolean isFirst()
+ {
+ return ((tweak[1] & T1_FIRST) != 0);
+ }
+
+ public void setFinal(boolean last)
+ {
+ if (last)
+ {
+ tweak[1] |= T1_FINAL;
+ }
+ else
+ {
+ tweak[1] &= ~T1_FINAL;
+ }
+ }
+
+ public boolean isFinal()
+ {
+ return ((tweak[1] & T1_FINAL) != 0);
+ }
+
+ /**
+ * Advances the position in the tweak by the specified value.
+ */
+ public void advancePosition(int advance)
+ {
+ // Bits 0..95 = position
+ if (extendedPosition)
+ {
+ long[] parts = new long[3];
+ parts[0] = tweak[0] & 0xFFFFFFFFL;
+ parts[1] = (tweak[0] >>> 32) & 0xFFFFFFFFL;
+ parts[2] = tweak[1] & 0xFFFFFFFFL;
+
+ long carry = advance;
+ for (int i = 0; i < parts.length; i++)
+ {
+ carry += parts[i];
+ parts[i] = carry;
+ carry >>>= 32;
+ }
+ tweak[0] = ((parts[1] & 0xFFFFFFFFL) << 32) | (parts[0] & 0xFFFFFFFFL);
+ tweak[1] = (tweak[1] & 0xFFFFFFFF00000000L) | (parts[2] & 0xFFFFFFFFL);
+ }
+ else
+ {
+ long position = tweak[0];
+ position += advance;
+ tweak[0] = position;
+ if (position > LOW_RANGE)
+ {
+ extendedPosition = true;
+ }
+ }
+ }
+
+ public long[] getWords()
+ {
+ return tweak;
+ }
+
+ public String toString()
+ {
+ return getType() + " first: " + isFirst() + ", final: " + isFinal();
+ }
+
+ }
+
+ /**
+ * The Unique Block Iteration chaining mode.
+ */
+ // TODO: This might be better as methods...
+ private class UBI
+ {
+ private final UbiTweak tweak = new UbiTweak();
+
+ /**
+ * Buffer for the current block of message data
+ */
+ private byte[] currentBlock;
+
+ /**
+ * Offset into the current message block
+ */
+ private int currentOffset;
+
+ /**
+ * Buffer for message words for feedback into encrypted block
+ */
+ private long[] message;
+
+ public UBI(int blockSize)
+ {
+ currentBlock = new byte[blockSize];
+ message = new long[currentBlock.length / 8];
+ }
+
+ public void reset(UBI ubi)
+ {
+ currentBlock = Arrays.clone(ubi.currentBlock, currentBlock);
+ currentOffset = ubi.currentOffset;
+ message = Arrays.clone(ubi.message, this.message);
+ tweak.reset(ubi.tweak);
+ }
+
+ public void reset(int type)
+ {
+ tweak.reset();
+ tweak.setType(type);
+ currentOffset = 0;
+ }
+
+ public void update(byte[] value, int offset, int len, long[] output)
+ {
+ /*
+ * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
+ * are subsequent bytes (last block must be processed in doFinal() with final=true set).
+ */
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentOffset == currentBlock.length)
+ {
+ processBlock(output);
+ tweak.setFirst(false);
+ currentOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), currentBlock.length - currentOffset);
+ System.arraycopy(value, offset + copied, currentBlock, currentOffset, toCopy);
+ copied += toCopy;
+ currentOffset += toCopy;
+ tweak.advancePosition(toCopy);
+ }
+ }
+
+ private void processBlock(long[] output)
+ {
+ threefish.init(true, chain, tweak.getWords());
+ for (int i = 0; i < message.length; i++)
+ {
+ message[i] = ThreefishEngine.bytesToWord(currentBlock, i * 8);
+ }
+
+ threefish.processBlock(message, output);
+
+ for (int i = 0; i < output.length; i++)
+ {
+ output[i] ^= message[i];
+ }
+ }
+
+ public void doFinal(long[] output)
+ {
+ // Pad remainder of current block with zeroes
+ for (int i = currentOffset; i < currentBlock.length; i++)
+ {
+ currentBlock[i] = 0;
+ }
+
+ tweak.setFinal(true);
+ processBlock(output);
+ }
+
+ }
+
+ /**
+ * Underlying Threefish tweakable block cipher
+ */
+ private ThreefishEngine threefish;
+
+ /**
+ * Size of the digest output, in bytes
+ */
+ private int outputSizeBytes;
+
+ /**
+ * The current chaining/state value
+ */
+ long[] chain;
+
+ /**
+ * The initial state value
+ */
+ private long[] initialState;
+
+ /**
+ * The (optional) key parameter
+ */
+ private byte[] key;
+
+ /**
+ * Parameters to apply prior to the message
+ */
+ private Parameter[] preMessageParameters;
+
+ /**
+ * Parameters to apply after the message, but prior to output
+ */
+ private Parameter[] postMessageParameters;
+
+ /**
+ * The current UBI operation
+ */
+ private UBI ubi;
+
+ /**
+ * Buffer for single byte update method
+ */
+ private final byte[] singleByte = new byte[1];
+
+ /**
+ * Constructs a Skein engine.
+ *
+ * @param blockSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param outputSizeBits the output/digest size to produce in bits, which must be an integral number of
+ * bytes.
+ */
+ public SkeinEngine(int blockSizeBits, int outputSizeBits)
+ {
+ if (outputSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
+ }
+ // TODO: Prevent digest sizes > block size?
+ this.outputSizeBytes = outputSizeBits / 8;
+
+ this.threefish = new ThreefishEngine(blockSizeBits);
+ this.ubi = new UBI(threefish.getBlockSize());
+ }
+
+ /**
+ * Creates a SkeinEngine as an exact copy of an existing instance.
+ */
+ public SkeinEngine(SkeinEngine engine)
+ {
+ this(engine.getBlockSize() * 8, engine.getOutputSize() * 8);
+ copyIn(engine);
+ }
+
+ private void copyIn(SkeinEngine engine)
+ {
+ this.ubi.reset(engine.ubi);
+ this.chain = Arrays.clone(engine.chain, this.chain);
+ this.initialState = Arrays.clone(engine.initialState, this.initialState);
+ this.key = Arrays.clone(engine.key, this.key);
+ this.preMessageParameters = clone(engine.preMessageParameters, this.preMessageParameters);
+ this.postMessageParameters = clone(engine.postMessageParameters, this.postMessageParameters);
+ }
+
+ private static Parameter[] clone(Parameter[] data, Parameter[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ existing = new Parameter[data.length];
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
+ public Memoable copy()
+ {
+ return new SkeinEngine(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ SkeinEngine s = (SkeinEngine)other;
+ if ((getBlockSize() != s.getBlockSize()) || (outputSizeBytes != s.outputSizeBytes))
+ {
+ throw new IllegalArgumentException("Incompatible parameters in provided SkeinEngine.");
+ }
+ copyIn(s);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSizeBytes;
+ }
+
+ public int getBlockSize()
+ {
+ return threefish.getBlockSize();
+ }
+
+ /**
+ * Initialises the Skein engine with the provided parameters. See {@link SkeinParameters} for
+ * details on the parameterisation of the Skein hash function.
+ *
+ * @param params the parameters to apply to this engine, or null
to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ this.chain = null;
+ this.key = null;
+ this.preMessageParameters = null;
+ this.postMessageParameters = null;
+
+ if (params != null)
+ {
+ byte[] key = params.getKey();
+ if (key.length < 16)
+ {
+ throw new IllegalArgumentException("Skein key must be at least 128 bits.");
+ }
+ initParams(params.getParameters());
+ }
+ createInitialState();
+
+ // Initialise message block
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void initParams(Hashtable parameters)
+ {
+ Enumeration keys = parameters.keys();
+ final Vector pre = new Vector();
+ final Vector post = new Vector();
+
+ while (keys.hasMoreElements())
+ {
+ Integer type = (Integer)keys.nextElement();
+ byte[] value = (byte[])parameters.get(type);
+
+ if (type.intValue() == PARAM_TYPE_KEY)
+ {
+ this.key = value;
+ }
+ else if (type.intValue() < PARAM_TYPE_MESSAGE)
+ {
+ pre.addElement(new Parameter(type.intValue(), value));
+ }
+ else
+ {
+ post.addElement(new Parameter(type.intValue(), value));
+ }
+ }
+ preMessageParameters = new Parameter[pre.size()];
+ pre.copyInto(preMessageParameters);
+ sort(preMessageParameters);
+
+ postMessageParameters = new Parameter[post.size()];
+ post.copyInto(postMessageParameters);
+ sort(postMessageParameters);
+ }
+
+ private static void sort(Parameter[] params)
+ {
+ if (params == null)
+ {
+ return;
+ }
+ // Insertion sort, for Java 1.1 compatibility
+ for (int i = 1; i < params.length; i++)
+ {
+ Parameter param = params[i];
+ int hole = i;
+ while (hole > 0 && param.getType() < params[hole - 1].getType())
+ {
+ params[hole] = params[hole - 1];
+ hole = hole - 1;
+ }
+ params[hole] = param;
+ }
+ }
+
+ /**
+ * Calculate the initial (pre message block) chaining state.
+ */
+ private void createInitialState()
+ {
+ long[] precalc = (long[])INITIAL_STATES.get(variantIdentifier(getBlockSize(), getOutputSize()));
+ if ((key == null) && (precalc != null))
+ {
+ // Precalculated UBI(CFG)
+ chain = Arrays.clone(precalc);
+ }
+ else
+ {
+ // Blank initial state
+ chain = new long[getBlockSize() / 8];
+
+ // Process key block
+ if (key != null)
+ {
+ ubiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
+ }
+
+ // Process configuration block
+ ubiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).getBytes());
+ }
+
+ // Process additional pre-message parameters
+ if (preMessageParameters != null)
+ {
+ for (int i = 0; i < preMessageParameters.length; i++)
+ {
+ Parameter param = preMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+ initialState = Arrays.clone(chain);
+ }
+
+ /**
+ * Reset the engine to the initial state (with the key and any pre-message parameters , ready to
+ * accept message input.
+ */
+ public void reset()
+ {
+ System.arraycopy(initialState, 0, chain, 0, chain.length);
+
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void ubiComplete(int type, byte[] value)
+ {
+ ubiInit(type);
+ this.ubi.update(value, 0, value.length, chain);
+ ubiFinal();
+ }
+
+ private void ubiInit(int type)
+ {
+ this.ubi.reset(type);
+ }
+
+ private void ubiFinal()
+ {
+ ubi.doFinal(chain);
+ }
+
+ private void checkInitialised()
+ {
+ if (this.ubi == null)
+ {
+ throw new IllegalArgumentException("Skein engine is not initialised.");
+ }
+ }
+
+ public void update(byte in)
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ checkInitialised();
+ ubi.update(in, inOff, len, chain);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ checkInitialised();
+ if (out.length < (outOff + outputSizeBytes))
+ {
+ throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes");
+ }
+
+ // Finalise message block
+ ubiFinal();
+
+ // Process additional post-message parameters
+ if (postMessageParameters != null)
+ {
+ for (int i = 0; i < postMessageParameters.length; i++)
+ {
+ Parameter param = postMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+
+ // Perform the output transform
+ final int blockSize = getBlockSize();
+ final int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
+ for (int i = 0; i < blocksRequired; i++)
+ {
+ final int toWrite = Math.min(blockSize, outputSizeBytes - (i * blockSize));
+ output(i, out, outOff + (i * blockSize), toWrite);
+ }
+
+ reset();
+
+ return outputSizeBytes;
+ }
+
+ private void output(long outputSequence, byte[] out, int outOff, int outputBytes)
+ {
+ byte[] currentBytes = new byte[8];
+ ThreefishEngine.wordToBytes(outputSequence, currentBytes, 0);
+
+ // Output is a sequence of UBI invocations all of which use and preserve the pre-output
+ // state
+ long[] outputWords = new long[chain.length];
+ ubiInit(PARAM_TYPE_OUTPUT);
+ this.ubi.update(currentBytes, 0, currentBytes.length, outputWords);
+ ubi.doFinal(outputWords);
+
+ final int wordsRequired = ((outputBytes + 8 - 1) / 8);
+ for (int i = 0; i < wordsRequired; i++)
+ {
+ int toWrite = Math.min(8, outputBytes - (i * 8));
+ if (toWrite == 8)
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], out, outOff + (i * 8));
+ }
+ else
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], currentBytes, 0);
+ System.arraycopy(currentBytes, 0, out, outOff + (i * 8), toWrite);
+ }
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/encodings/PKCS1Encoding.java b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/encodings/PKCS1Encoding.java
new file mode 100644
index 000000000..3149a1f37
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/crypto/encodings/PKCS1Encoding.java
@@ -0,0 +1,238 @@
+package org.spongycastle.crypto.encodings;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+import java.security.SecureRandom;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+public class PKCS1Encoding
+ implements AsymmetricBlockCipher
+{
+ /**
+ * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
+ * work with one of these set the system property org.spongycastle.pkcs1.strict to false.
+ * resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource,String id) throws NullPointerException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource,String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ this.encoding = encoding;
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ this.encoding = encoding;
+ }
+
+ /**
+ * Reads the entry id + "." + key
from the resource file and returns a
+ * formated message for the given Locale and TimeZone.
+ * @param key second part of the entry id
+ * @param loc the used {@link Locale}
+ * @param timezone the used {@link TimeZone}
+ * @return a Strng containing the localized message
+ * @throws MissingEntryException if the resource file is not available or the entry does not exist.
+ */
+ public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ String entry = id;
+ if (key != null)
+ {
+ entry += "." + key;
+ }
+
+ try
+ {
+ ResourceBundle bundle;
+ if (loader == null)
+ {
+ bundle = ResourceBundle.getBundle(resource,loc);
+ }
+ else
+ {
+ bundle = ResourceBundle.getBundle(resource, loc);
+ }
+ String result = bundle.getString(entry);
+ if (!encoding.equals(DEFAULT_ENCODING))
+ {
+ result = new String(result.getBytes(DEFAULT_ENCODING), encoding);
+ }
+ if (!arguments.isEmpty())
+ {
+ result = formatWithTimeZone(result,arguments.getFilteredArgs(loc),loc,timezone);
+ }
+ result = addExtraArgs(result, loc);
+ return result;
+ }
+ catch (MissingResourceException mre)
+ {
+ throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
+ resource,
+ entry,
+ loc,
+ loader != null ? loader : this.getClassLoader());
+ }
+ catch (UnsupportedEncodingException use)
+ {
+ // should never occur - cause we already test this in the constructor
+ throw new RuntimeException(use.toString());
+ }
+ }
+
+ protected String formatWithTimeZone(
+ String template,
+ Object[] arguments,
+ Locale locale,
+ TimeZone timezone)
+ {
+ MessageFormat mf = new MessageFormat(" ");
+ mf.setLocale(locale);
+ mf.applyPattern(template);
+ if (!timezone.equals(TimeZone.getDefault()))
+ {
+ Format[] formats = mf.getFormats();
+ for (int i = 0; i < formats.length; i++)
+ {
+ if (formats[i] instanceof DateFormat)
+ {
+ DateFormat temp = (DateFormat) formats[i];
+ temp.setTimeZone(timezone);
+ mf.setFormat(i,temp);
+ }
+ }
+ }
+ return mf.format(arguments);
+ }
+
+ protected String addExtraArgs(String msg, Locale locale)
+ {
+ if (extraArgs != null)
+ {
+ StringBuffer sb = new StringBuffer(msg);
+ Object[] filteredArgs = extraArgs.getFilteredArgs(locale);
+ for (int i = 0; i < filteredArgs.length; i++)
+ {
+ sb.append(filteredArgs[i]);
+ }
+ msg = sb.toString();
+ }
+ return msg;
+ }
+
+ /**
+ * Sets the {@link Filter} that is used to filter the arguments of this message
+ * @param filter the {@link Filter} to use. null
to disable filtering.
+ */
+ public void setFilter(Filter filter)
+ {
+ arguments.setFilter(filter);
+ if (extraArgs != null)
+ {
+ extraArgs.setFilter(filter);
+ }
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the current filter.
+ * @return the current filter
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ /**
+ * Set the {@link ClassLoader} which loads the resource files. If it is set to null
+ * then the default {@link ClassLoader} is used.
+ * @param loader the {@link ClassLoader} which loads the resource files
+ */
+ public void setClassLoader(ClassLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} which loads the resource files or null
+ * if the default ClassLoader is used.
+ * @return the {@link ClassLoader} which loads the resource files
+ */
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ /**
+ * Returns the id of the message in the resource bundle.
+ * @return the id of the message
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the name of the resource bundle for this message
+ * @return name of the resource file
+ */
+ public String getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Returns an Object[]
containing the message arguments.
+ * @return the message arguments
+ */
+ public Object[] getArguments()
+ {
+ return arguments.getArguments();
+ }
+
+ /**
+ *
+ * @param extraArg
+ */
+ public void setExtraArgument(Object extraArg)
+ {
+ setExtraArguments(new Object[] {extraArg});
+ }
+
+ /**
+ *
+ * @param extraArgs
+ */
+ public void setExtraArguments(Object[] extraArgs)
+ {
+ if (extraArgs != null)
+ {
+ this.extraArgs = new FilteredArguments(extraArgs);
+ this.extraArgs.setFilter(filter);
+ }
+ else
+ {
+ this.extraArgs = null;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Object[] getExtraArgs()
+ {
+ return (extraArgs == null) ? null : extraArgs.getArguments();
+ }
+
+ protected class FilteredArguments
+ {
+
+ protected Filter filter = null;
+
+ protected boolean[] isLocaleSpecific;
+ protected int[] argFilterType;
+ protected Object[] arguments;
+ protected Object[] unpackedArgs;
+ protected Object[] filteredArgs;
+
+ FilteredArguments()
+ {
+ this(new Object[0]);
+ }
+
+ FilteredArguments(Object[] args)
+ {
+ this.arguments = args;
+ this.unpackedArgs = new Object[args.length];
+ this.filteredArgs = new Object[args.length];
+ this.isLocaleSpecific = new boolean[args.length];
+ this.argFilterType = new int[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i] instanceof TrustedInput)
+ {
+ this.unpackedArgs[i] = ((TrustedInput) args[i]).getInput();
+ argFilterType[i] = NO_FILTER;
+ }
+ else if (args[i] instanceof UntrustedInput)
+ {
+ this.unpackedArgs[i] = ((UntrustedInput) args[i]).getInput();
+ if (args[i] instanceof UntrustedUrlInput)
+ {
+ argFilterType[i] = FILTER_URL;
+ }
+ else
+ {
+ argFilterType[i] = FILTER;
+ }
+ }
+ else
+ {
+ this.unpackedArgs[i] = args[i];
+ argFilterType[i] = FILTER;
+ }
+
+ // locale specific
+ this.isLocaleSpecific[i] = (this.unpackedArgs[i] instanceof LocaleString);
+ }
+ }
+
+ public boolean isEmpty()
+ {
+ return unpackedArgs.length == 0;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Object[] getFilteredArgs(Locale locale)
+ {
+ Object[] result = new Object[unpackedArgs.length];
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ Object arg;
+ if (filteredArgs[i] != null)
+ {
+ arg = filteredArgs[i];
+ }
+ else
+ {
+ arg = unpackedArgs[i];
+ if (isLocaleSpecific[i])
+ {
+ // get locale
+ arg = ((LocaleString) arg).getLocaleString(locale);
+ arg = filter(argFilterType[i], arg);
+ }
+ else
+ {
+ arg = filter(argFilterType[i], arg);
+ filteredArgs[i] = arg;
+ }
+ }
+ result[i] = arg;
+ }
+ return result;
+ }
+
+ private Object filter(int type, Object obj)
+ {
+ if (filter != null)
+ {
+ Object o = (null == obj) ? "null" : obj;
+ switch (type)
+ {
+ case NO_FILTER:
+ return o;
+ case FILTER:
+ return filter.doFilter(o.toString());
+ case FILTER_URL:
+ return filter.doFilterUrl(o.toString());
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ return obj;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(Filter filter)
+ {
+ if (filter != this.filter)
+ {
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ filteredArgs[i] = null;
+ }
+ }
+ this.filter = filter;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Resource: \"").append(resource);
+ sb.append("\" Id: \"").append(id).append("\"");
+ sb.append(" Arguments: ").append(arguments.getArguments().length).append(" normal, ")
+ .append(extraArgs.getArguments().length).append(" extra");
+ sb.append(" Encoding: ").append(encoding);
+ sb.append(" ClassLoader: ").append(loader);
+ return sb.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/i18n/MissingEntryException.java b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/i18n/MissingEntryException.java
new file mode 100644
index 000000000..d876a91af
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.1/org/spongycastle/i18n/MissingEntryException.java
@@ -0,0 +1,63 @@
+package org.spongycastle.i18n;
+
+import java.net.URL;
+import java.util.Locale;
+
+public class MissingEntryException extends RuntimeException
+{
+
+ protected final String resource;
+ protected final String key;
+ protected final ClassLoader loader;
+ protected final Locale locale;
+
+ private String debugMsg;
+
+ public MissingEntryException(String message, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public MissingEntryException(String message, Throwable cause, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message + ": " + cause);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public String getResource()
+ {
+ return resource;
+ }
+
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ public String getDebugMsg()
+ {
+ if (debugMsg == null)
+ {
+ debugMsg = "Can not find entry " + key + " in resource file " + resource + " for the locale " + locale + ".";
+ }
+ return debugMsg;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java b/libraries/spongycastle/core/src/main/jdk1.2/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
new file mode 100644
index 000000000..042eb3eb2
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
@@ -0,0 +1,67 @@
+
+package java.security.interfaces;
+
+import java.math.BigInteger;
+import java.security.spec.RSAOtherPrimeInfo;
+
+/**
+ * The interface to an RSA multi-prime private key, as defined in the
+ * PKCS#1 v2.1, using the Chinese Remainder Theorem (CRT) information values.
+ *
+ * @since 1.4
+ * @see RSAPrivateKeySpec, RSAMultiPrimePrivateCrtKeySpec, RSAPrivateKey,
+ * RSAPrivateCrtKey
+ */
+public interface RSAMultiPrimePrivateCrtKey
+extends RSAPrivateKey
+{
+ /**
+ * Returns the public exponent.
+ *
+ * @returns the public exponent.
+ */
+ public BigInteger getPublicExponent();
+
+ /**
+ * Returns the primeP.
+ *
+ * @returns the primeP.
+ */
+ public BigInteger getPrimeP();
+
+ /**
+ * Returns the primeQ.
+ *
+ * @returns the primeQ.
+ */
+ public BigInteger getPrimeQ();
+
+ /**
+ * Returns the primeExponentP.
+ *
+ * @returns the primeExponentP.
+ */
+ public BigInteger getPrimeExponentP();
+
+ /**
+ * Returns the primeExponentQ.
+ *
+ * @returns the primeExponentQ.
+ */
+ public BigInteger getPrimeExponentQ();
+
+ /**
+ * Returns the crtCoefficient.
+ *
+ * @returns the crtCoefficient.
+ */
+ public BigInteger getCrtCoefficient();
+
+ /**
+ * Returns the otherPrimeInfo or null if there are only two prime
+ * factors (p and q).
+ *
+ * @returns the otherPrimeInfo.
+ */
+ public RSAOtherPrimeInfo[] getOtherPrimeInfo();
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/PSSParameterSpec.java b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/PSSParameterSpec.java
new file mode 100644
index 000000000..f58d83b78
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/PSSParameterSpec.java
@@ -0,0 +1,45 @@
+
+package java.security.spec;
+
+/**
+ * This class specifies a parameter spec for RSA PSS encoding scheme,
+ * as defined in the PKCS#1 v2.1.
+ *
+ * @since 1.4
+ * @see AlgorithmParameterSpec, Signature
+ */
+public class PSSParameterSpec
+ extends Object
+ implements AlgorithmParameterSpec
+{
+ private int saltLen;
+
+ /**
+ * Creates a new PSSParameterSpec given the salt length as defined
+ * in PKCS#1.
+ *
+ * @param saltLen - the length of salt in bits to be used in PKCS#1
+ * PSS encoding.
+ * @throws IllegalArgumentException - if saltLen is less than 0.
+ */
+ public PSSParameterSpec(int saltLen)
+ {
+ if ( saltLen < 0 )
+ {
+ throw new IllegalArgumentException("Salt length must be >= 0");
+ }
+
+ this.saltLen = saltLen;
+ }
+
+ /**
+ * Returns the salt length in bits.
+ *
+ * @returns the salt length.
+ */
+ public int getSaltLength()
+ {
+ return saltLen;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAKeyGenParameterSpec.java b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAKeyGenParameterSpec.java
new file mode 100644
index 000000000..756c6c0fd
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAKeyGenParameterSpec.java
@@ -0,0 +1,35 @@
+package java.security.spec;
+
+import java.math.BigInteger;
+
+/**
+ * specifies parameters to be used for the generation of
+ * a RSA key pair.
+ */
+public class RSAKeyGenParameterSpec
+ implements AlgorithmParameterSpec
+{
+ static BigInteger F0 = BigInteger.valueOf(3);
+ static BigInteger F4 = BigInteger.valueOf(65537);
+
+ private int keysize;
+ private BigInteger publicExponent;
+
+ public RSAKeyGenParameterSpec(
+ int keysize,
+ BigInteger publicExponent)
+ {
+ this.keysize = keysize;
+ this.publicExponent = publicExponent;
+ }
+
+ public int getKeysize()
+ {
+ return keysize;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java
new file mode 100644
index 000000000..1339b4f6d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java
@@ -0,0 +1,159 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+/**
+ * This class specifies an RSA multi-prime private key, as defined in
+ * the PKCS#1 v2.1, using the Chinese Remainder Theorem (CRT) information
+ * values for efficiency.
+ *
+ * @since 1.4
+ * @see Key, KeyFactory, KeySpec, PKCS8EncodedKeySpec, RSAPrivateKeySpec,
+ * RSAPublicKeySpec, RSAOtherPrimeInfo
+ */
+public class RSAMultiPrimePrivateCrtKeySpec
+ extends RSAPrivateKeySpec
+{
+ private BigInteger publicExponent;
+ private BigInteger privateExponent;
+ private BigInteger primeP;
+ private BigInteger primeQ;
+ private BigInteger primeExponentP;
+ private BigInteger primeExponentQ;
+ private BigInteger crtCoefficient;
+ private RSAOtherPrimeInfo[] otherPrimeInfo;
+
+ /**
+ * Creates a new RSAMultiPrimePrivateCrtKeySpec given the modulus,
+ * publicExponent, privateExponent, primeP, primeQ, primeExponentP,
+ * primeExponentQ, crtCoefficient, and otherPrimeInfo as defined in
+ * PKCS#1 v2.1.
+ *
+ * Note that otherPrimeInfo is cloned when constructing this object.
+ *
+ * @param modulus - the modulus n.
+ * @param publicExponent - the public exponent e.
+ * @param privateExponent - the private exponent d.
+ * @param primeP - the prime factor p of n.
+ * @param primeQ - the prime factor q of n.
+ * @param primeExponentP - this is d mod (p-1).
+ * @param primeExponentQ - this is d mod (q-1).
+ * @param crtCoefficient - the Chinese Remainder Theorem coefficient q-1
+ * mod p.
+ * @param otherPrimeInfo - triplets of the rest of primes, null can be
+ * specified if there are only two prime factors (p and q).
+ * @throws NullPointerException - if any of the parameters, i.e. modulus,
+ * publicExponent, privateExponent, primeP, primeQ, primeExponentP,
+ * primeExponentQ, crtCoefficient, is null.
+ * @throws IllegalArgumentException - if an empty, i.e. 0-length,
+ * otherPrimeInfo is specified.
+ */
+ public RSAMultiPrimePrivateCrtKeySpec(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger primeP,
+ BigInteger primeQ,
+ BigInteger primeExponentP,
+ BigInteger primeExponentQ,
+ BigInteger crtCoefficient,
+ RSAOtherPrimeInfo[] otherPrimeInfo)
+ {
+ super(modulus, privateExponent);
+
+ if ( publicExponent == null || primeP == null || primeQ == null
+ || primeExponentP == null || primeExponentQ == null
+ || crtCoefficient == null )
+ {
+ throw new NullPointerException("Invalid null argument");
+ }
+
+ if ( otherPrimeInfo != null )
+ {
+ if ( otherPrimeInfo.length == 0 )
+ {
+ throw new IllegalArgumentException("Invalid length for otherPrimeInfo");
+ }
+
+ this.otherPrimeInfo = (RSAOtherPrimeInfo[])otherPrimeInfo.clone();
+ }
+ }
+
+ /**
+ * Returns the public exponent.
+ *
+ * @returns the public exponent.
+ */
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * Returns the primeP.
+ *
+ * @returns the primeP.
+ */
+ public BigInteger getPrimeP()
+ {
+ return primeP;
+ }
+
+ /**
+ * Returns the primeQ.
+ *
+ * @returns the primeQ.
+ */
+ public BigInteger getPrimeQ()
+ {
+ return primeQ;
+ }
+
+ /**
+ * Returns the primeExponentP.
+ *
+ * @returns the primeExponentP.
+ */
+ public BigInteger getPrimeExponentP()
+ {
+ return primeExponentP;
+ }
+
+ /**
+ * Returns the primeExponentQ.
+ *
+ * @returns the primeExponentQ.
+ */
+ public BigInteger getPrimeExponentQ()
+ {
+ return primeExponentQ;
+ }
+
+ /**
+ * Returns the crtCofficient.
+ *
+ * @returns the crtCofficient.
+ */
+ public BigInteger getCrtCoefficient()
+ {
+ return crtCoefficient;
+ }
+
+ /**
+ * Returns a copy of the otherPrimeInfo or null if there are only
+ * two prime factors (p and q).
+ *
+ * @returns the otherPrimeInfo.
+ */
+ public RSAOtherPrimeInfo[] getOtherPrimeInfo()
+ {
+ if ( otherPrimeInfo != null )
+ {
+ return (RSAOtherPrimeInfo[])otherPrimeInfo.clone();
+ }
+
+ return null;
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAOtherPrimeInfo.java b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAOtherPrimeInfo.java
new file mode 100644
index 000000000..42a4fce6d
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/java/security/spec/RSAOtherPrimeInfo.java
@@ -0,0 +1,80 @@
+
+package java.security.spec;
+
+import java.math.BigInteger;
+
+/**
+ * This class represents the triplet (prime, exponent, and coefficient)
+ * inside RSA's OtherPrimeInfo structure, as defined in the PKCS#1 v2.1.
+ * The ASN.1 syntax of RSA's OtherPrimeInfo is as follows:
+ *
+ *
+ * OtherPrimeInfo ::= SEQUENCE {
+ * prime INTEGER,
+ * exponent INTEGER,
+ * coefficient INTEGER
+ * }
+ *
+ */
+public class RSAOtherPrimeInfo
+extends Object
+{
+ private BigInteger prime;
+ private BigInteger primeExponent;
+ private BigInteger crtCoefficient;
+
+ /**
+ * Creates a new RSAOtherPrimeInfo given the prime, primeExponent,
+ * and crtCoefficient as defined in PKCS#1.
+ *
+ * @param prime - the prime factor of n.
+ * @param primeExponent - the exponent.
+ * @param crtCoefficient - the Chinese Remainder Theorem coefficient.
+ * @throws NullPointerException - if any of the parameters, i.e. prime,
+ * primeExponent, crtCoefficient, is null.
+ */
+ public RSAOtherPrimeInfo(
+ BigInteger prime,
+ BigInteger primeExponent,
+ BigInteger crtCoefficient)
+ {
+ if ( prime == null || primeExponent == null || crtCoefficient == null )
+ {
+ throw new NullPointerException("Null parameter");
+ }
+
+ this.prime = prime;
+ this.primeExponent = primeExponent;
+ this.crtCoefficient = crtCoefficient;
+ }
+
+ /**
+ * Returns the prime.
+ *
+ * @returns the prime.
+ */
+ public final BigInteger getPrime()
+ {
+ return prime;
+ }
+
+ /**
+ * Returns the prime's exponent.
+ *
+ * @returns the primeExponent.
+ */
+ public final BigInteger getExponent()
+ {
+ return primeExponent;
+ }
+
+ /**
+ * Returns the prime's crtCoefficient.
+ *
+ * @returns the crtCoefficient.
+ */
+ public final BigInteger getCrtCoefficient()
+ {
+ return crtCoefficient;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/org/spongycastle/i18n/LocalizedMessage.java b/libraries/spongycastle/core/src/main/jdk1.2/org/spongycastle/i18n/LocalizedMessage.java
new file mode 100644
index 000000000..1eab110cb
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/org/spongycastle/i18n/LocalizedMessage.java
@@ -0,0 +1,464 @@
+package org.spongycastle.i18n;
+
+import org.spongycastle.i18n.filter.Filter;
+import org.spongycastle.i18n.filter.TrustedInput;
+import org.spongycastle.i18n.filter.UntrustedInput;
+import org.spongycastle.i18n.filter.UntrustedUrlInput;
+
+import java.io.UnsupportedEncodingException;
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+public class LocalizedMessage
+{
+ protected static final int NO_FILTER = 0;
+ protected static final int FILTER = 1;
+ protected static final int FILTER_URL = 2;
+
+ protected String id;
+ protected String resource;
+
+ // ISO-8859-1 is the default encoding
+ public static final String DEFAULT_ENCODING = "ISO-8859-1";
+ protected String encoding = DEFAULT_ENCODING;
+
+ protected FilteredArguments arguments;
+ protected FilteredArguments extraArgs = null;
+
+ protected Filter filter = null;
+
+ protected ClassLoader loader = null;
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource,String id) throws NullPointerException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource,String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ this.encoding = encoding;
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ this.encoding = encoding;
+ }
+
+ /**
+ * Reads the entry id + "." + key
from the resource file and returns a
+ * formated message for the given Locale and TimeZone.
+ * @param key second part of the entry id
+ * @param loc the used {@link Locale}
+ * @param timezone the used {@link TimeZone}
+ * @return a Strng containing the localized message
+ * @throws MissingEntryException if the resource file is not available or the entry does not exist.
+ */
+ public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ String entry = id;
+ if (key != null)
+ {
+ entry += "." + key;
+ }
+
+ try
+ {
+ ResourceBundle bundle;
+ if (loader == null)
+ {
+ bundle = ResourceBundle.getBundle(resource,loc);
+ }
+ else
+ {
+ bundle = ResourceBundle.getBundle(resource, loc);
+ }
+ String result = bundle.getString(entry);
+ if (!encoding.equals(DEFAULT_ENCODING))
+ {
+ result = new String(result.getBytes(DEFAULT_ENCODING), encoding);
+ }
+ if (!arguments.isEmpty())
+ {
+ result = formatWithTimeZone(result,arguments.getFilteredArgs(loc),loc,timezone);
+ }
+ result = addExtraArgs(result, loc);
+ return result;
+ }
+ catch (MissingResourceException mre)
+ {
+ throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
+ resource,
+ entry,
+ loc,
+ loader != null ? loader : this.getClassLoader());
+ }
+ catch (UnsupportedEncodingException use)
+ {
+ // should never occur - cause we already test this in the constructor
+ throw new RuntimeException(use.toString());
+ }
+ }
+
+ protected String formatWithTimeZone(
+ String template,
+ Object[] arguments,
+ Locale locale,
+ TimeZone timezone)
+ {
+ MessageFormat mf = new MessageFormat(" ");
+ mf.setLocale(locale);
+ mf.applyPattern(template);
+ if (!timezone.equals(TimeZone.getDefault()))
+ {
+ Format[] formats = mf.getFormats();
+ for (int i = 0; i < formats.length; i++)
+ {
+ if (formats[i] instanceof DateFormat)
+ {
+ DateFormat temp = (DateFormat) formats[i];
+ temp.setTimeZone(timezone);
+ mf.setFormat(i,temp);
+ }
+ }
+ }
+ return mf.format(arguments);
+ }
+
+ protected String addExtraArgs(String msg, Locale locale)
+ {
+ if (extraArgs != null)
+ {
+ StringBuffer sb = new StringBuffer(msg);
+ Object[] filteredArgs = extraArgs.getFilteredArgs(locale);
+ for (int i = 0; i < filteredArgs.length; i++)
+ {
+ sb.append(filteredArgs[i]);
+ }
+ msg = sb.toString();
+ }
+ return msg;
+ }
+
+ /**
+ * Sets the {@link Filter} that is used to filter the arguments of this message
+ * @param filter the {@link Filter} to use. null
to disable filtering.
+ */
+ public void setFilter(Filter filter)
+ {
+ arguments.setFilter(filter);
+ if (extraArgs != null)
+ {
+ extraArgs.setFilter(filter);
+ }
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the current filter.
+ * @return the current filter
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ /**
+ * Set the {@link ClassLoader} which loads the resource files. If it is set to null
+ * then the default {@link ClassLoader} is used.
+ * @param loader the {@link ClassLoader} which loads the resource files
+ */
+ public void setClassLoader(ClassLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} which loads the resource files or null
+ * if the default ClassLoader is used.
+ * @return the {@link ClassLoader} which loads the resource files
+ */
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ /**
+ * Returns the id of the message in the resource bundle.
+ * @return the id of the message
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the name of the resource bundle for this message
+ * @return name of the resource file
+ */
+ public String getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Returns an Object[]
containing the message arguments.
+ * @return the message arguments
+ */
+ public Object[] getArguments()
+ {
+ return arguments.getArguments();
+ }
+
+ /**
+ *
+ * @param extraArg
+ */
+ public void setExtraArgument(Object extraArg)
+ {
+ setExtraArguments(new Object[] {extraArg});
+ }
+
+ /**
+ *
+ * @param extraArgs
+ */
+ public void setExtraArguments(Object[] extraArgs)
+ {
+ if (extraArgs != null)
+ {
+ this.extraArgs = new FilteredArguments(extraArgs);
+ this.extraArgs.setFilter(filter);
+ }
+ else
+ {
+ this.extraArgs = null;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Object[] getExtraArgs()
+ {
+ return (extraArgs == null) ? null : extraArgs.getArguments();
+ }
+
+ protected class FilteredArguments
+ {
+
+ protected Filter filter = null;
+
+ protected boolean[] isLocaleSpecific;
+ protected int[] argFilterType;
+ protected Object[] arguments;
+ protected Object[] unpackedArgs;
+ protected Object[] filteredArgs;
+
+ FilteredArguments()
+ {
+ this(new Object[0]);
+ }
+
+ FilteredArguments(Object[] args)
+ {
+ this.arguments = args;
+ this.unpackedArgs = new Object[args.length];
+ this.filteredArgs = new Object[args.length];
+ this.isLocaleSpecific = new boolean[args.length];
+ this.argFilterType = new int[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i] instanceof TrustedInput)
+ {
+ this.unpackedArgs[i] = ((TrustedInput) args[i]).getInput();
+ argFilterType[i] = NO_FILTER;
+ }
+ else if (args[i] instanceof UntrustedInput)
+ {
+ this.unpackedArgs[i] = ((UntrustedInput) args[i]).getInput();
+ if (args[i] instanceof UntrustedUrlInput)
+ {
+ argFilterType[i] = FILTER_URL;
+ }
+ else
+ {
+ argFilterType[i] = FILTER;
+ }
+ }
+ else
+ {
+ this.unpackedArgs[i] = args[i];
+ argFilterType[i] = FILTER;
+ }
+
+ // locale specific
+ this.isLocaleSpecific[i] = (this.unpackedArgs[i] instanceof LocaleString);
+ }
+ }
+
+ public boolean isEmpty()
+ {
+ return unpackedArgs.length == 0;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Object[] getFilteredArgs(Locale locale)
+ {
+ Object[] result = new Object[unpackedArgs.length];
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ Object arg;
+ if (filteredArgs[i] != null)
+ {
+ arg = filteredArgs[i];
+ }
+ else
+ {
+ arg = unpackedArgs[i];
+ if (isLocaleSpecific[i])
+ {
+ // get locale
+ arg = ((LocaleString) arg).getLocaleString(locale);
+ arg = filter(argFilterType[i], arg);
+ }
+ else
+ {
+ arg = filter(argFilterType[i], arg);
+ filteredArgs[i] = arg;
+ }
+ }
+ result[i] = arg;
+ }
+ return result;
+ }
+
+ private Object filter(int type, Object obj)
+ {
+ if (filter != null)
+ {
+ Object o = (null == obj) ? "null" : obj;
+ switch (type)
+ {
+ case NO_FILTER:
+ return o;
+ case FILTER:
+ return filter.doFilter(o.toString());
+ case FILTER_URL:
+ return filter.doFilterUrl(o.toString());
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ return obj;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(Filter filter)
+ {
+ if (filter != this.filter)
+ {
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ filteredArgs[i] = null;
+ }
+ }
+ this.filter = filter;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Resource: \"").append(resource);
+ sb.append("\" Id: \"").append(id).append("\"");
+ sb.append(" Arguments: ").append(arguments.getArguments().length).append(" normal, ")
+ .append(extraArgs.getArguments().length).append(" extra");
+ sb.append(" Encoding: ").append(encoding);
+ sb.append(" ClassLoader: ").append(loader);
+ return sb.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.2/org/spongycastle/i18n/MissingEntryException.java b/libraries/spongycastle/core/src/main/jdk1.2/org/spongycastle/i18n/MissingEntryException.java
new file mode 100644
index 000000000..d876a91af
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.2/org/spongycastle/i18n/MissingEntryException.java
@@ -0,0 +1,63 @@
+package org.spongycastle.i18n;
+
+import java.net.URL;
+import java.util.Locale;
+
+public class MissingEntryException extends RuntimeException
+{
+
+ protected final String resource;
+ protected final String key;
+ protected final ClassLoader loader;
+ protected final Locale locale;
+
+ private String debugMsg;
+
+ public MissingEntryException(String message, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public MissingEntryException(String message, Throwable cause, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message + ": " + cause);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public String getResource()
+ {
+ return resource;
+ }
+
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ public String getDebugMsg()
+ {
+ if (debugMsg == null)
+ {
+ debugMsg = "Can not find entry " + key + " in resource file " + resource + " for the locale " + locale + ".";
+ }
+ return debugMsg;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/asn1/StreamUtil.java b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/asn1/StreamUtil.java
new file mode 100644
index 000000000..0b0b183e6
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/asn1/StreamUtil.java
@@ -0,0 +1,89 @@
+package org.spongycastle.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+class StreamUtil
+{
+ /**
+ * Find out possible longest length...
+ *
+ * @param in input stream of interest
+ * @return length calculation or MAX_VALUE.
+ */
+ static int findLimit(InputStream in)
+ {
+ if (in instanceof LimitedInputStream)
+ {
+ return ((LimitedInputStream)in).getRemaining();
+ }
+ else if (in instanceof ASN1InputStream)
+ {
+ return ((ASN1InputStream)in).getLimit();
+ }
+ else if (in instanceof ByteArrayInputStream)
+ {
+ return ((ByteArrayInputStream)in).available();
+ }
+
+ return Integer.MAX_VALUE;
+ }
+
+ static int calculateBodyLength(
+ int length)
+ {
+ int count = 1;
+
+ if (length > 127)
+ {
+ int size = 1;
+ int val = length;
+
+ while ((val >>>= 8) != 0)
+ {
+ size++;
+ }
+
+ for (int i = (size - 1) * 8; i >= 0; i -= 8)
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ static int calculateTagLength(int tagNo)
+ throws IOException
+ {
+ int length = 1;
+
+ if (tagNo >= 31)
+ {
+ if (tagNo < 128)
+ {
+ length++;
+ }
+ else
+ {
+ byte[] stack = new byte[5];
+ int pos = stack.length;
+
+ stack[--pos] = (byte)(tagNo & 0x7F);
+
+ do
+ {
+ tagNo >>= 7;
+ stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
+ }
+ while (tagNo > 127);
+
+ length += stack.length - pos;
+ }
+ }
+
+ return length;
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/crypto/tls/UDPTransport.java b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/crypto/tls/UDPTransport.java
new file mode 100644
index 000000000..9391e1d26
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/crypto/tls/UDPTransport.java
@@ -0,0 +1,78 @@
+package org.spongycastle.crypto.tls;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+
+public class UDPTransport
+ implements DatagramTransport
+{
+ protected final static int MIN_IP_OVERHEAD = 20;
+ protected final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
+ protected final static int UDP_OVERHEAD = 8;
+
+ protected final DatagramSocket socket;
+ protected final int receiveLimit, sendLimit;
+
+ public UDPTransport(DatagramSocket socket, int mtu)
+ throws IOException
+ {
+ //
+ // In 1.3 and earlier sockets were bound and connected during creation
+ //
+ //if (!socket.isBound() || !socket.isConnected())
+ //{
+ // throw new IllegalArgumentException("'socket' must be bound and connected");
+ //}
+
+ this.socket = socket;
+
+ // NOTE: As of JDK 1.6, can use NetworkInterface.getMTU
+
+ this.receiveLimit = mtu - MIN_IP_OVERHEAD - UDP_OVERHEAD;
+ this.sendLimit = mtu - MAX_IP_OVERHEAD - UDP_OVERHEAD;
+ }
+
+ public int getReceiveLimit()
+ {
+ return receiveLimit;
+ }
+
+ public int getSendLimit()
+ {
+ // TODO[DTLS] Implement Path-MTU discovery?
+ return sendLimit;
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ socket.setSoTimeout(waitMillis);
+ DatagramPacket packet = new DatagramPacket(buf, off, len);
+ socket.receive(packet);
+ return packet.getLength();
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ if (len > getSendLimit())
+ {
+ /*
+ * RFC 4347 4.1.1. "If the application attempts to send a record larger than the MTU,
+ * the DTLS implementation SHOULD generate an error, thus avoiding sending a packet
+ * which will be fragmented."
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ DatagramPacket packet = new DatagramPacket(buf, off, len);
+ socket.send(packet);
+ }
+
+ public void close()
+ throws IOException
+ {
+ socket.close();
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/i18n/LocalizedMessage.java b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/i18n/LocalizedMessage.java
new file mode 100644
index 000000000..e0a4d84ce
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/i18n/LocalizedMessage.java
@@ -0,0 +1,464 @@
+package org.spongycastle.i18n;
+
+import org.spongycastle.i18n.filter.Filter;
+import org.spongycastle.i18n.filter.TrustedInput;
+import org.spongycastle.i18n.filter.UntrustedInput;
+import org.spongycastle.i18n.filter.UntrustedUrlInput;
+
+import java.io.UnsupportedEncodingException;
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+public class LocalizedMessage
+{
+
+ protected final String id;
+ protected final String resource;
+
+ // ISO-8859-1 is the default encoding
+ public static final String DEFAULT_ENCODING = "ISO-8859-1";
+ protected String encoding = DEFAULT_ENCODING;
+
+ protected FilteredArguments arguments;
+ protected FilteredArguments extraArgs = null;
+
+ protected Filter filter = null;
+
+ protected ClassLoader loader = null;
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource,String id) throws NullPointerException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource,String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ this.encoding = encoding;
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ */
+ public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using resource
as the base name for the
+ * RessourceBundle and id
as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if resource
or id
is null
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ this.encoding = encoding;
+ }
+
+ /**
+ * Reads the entry id + "." + key
from the resource file and returns a
+ * formated message for the given Locale and TimeZone.
+ * @param key second part of the entry id
+ * @param loc the used {@link Locale}
+ * @param timezone the used {@link TimeZone}
+ * @return a Strng containing the localized message
+ * @throws MissingEntryException if the resource file is not available or the entry does not exist.
+ */
+ public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ String entry = id;
+ if (key != null)
+ {
+ entry += "." + key;
+ }
+
+ try
+ {
+ ResourceBundle bundle;
+ if (loader == null)
+ {
+ bundle = ResourceBundle.getBundle(resource,loc);
+ }
+ else
+ {
+ bundle = ResourceBundle.getBundle(resource, loc, loader);
+ }
+ String result = bundle.getString(entry);
+ if (!encoding.equals(DEFAULT_ENCODING))
+ {
+ result = new String(result.getBytes(DEFAULT_ENCODING), encoding);
+ }
+ if (!arguments.isEmpty())
+ {
+ result = formatWithTimeZone(result,arguments.getFilteredArgs(loc),loc,timezone);
+ }
+ result = addExtraArgs(result, loc);
+ return result;
+ }
+ catch (MissingResourceException mre)
+ {
+ throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
+ resource,
+ entry,
+ loc,
+ loader != null ? loader : this.getClassLoader());
+ }
+ catch (UnsupportedEncodingException use)
+ {
+ // should never occur - cause we already test this in the constructor
+ throw new RuntimeException(use.toString());
+ }
+ }
+
+ protected String formatWithTimeZone(
+ String template,
+ Object[] arguments,
+ Locale locale,
+ TimeZone timezone)
+ {
+ MessageFormat mf = new MessageFormat(" ");
+ mf.setLocale(locale);
+ mf.applyPattern(template);
+ if (!timezone.equals(TimeZone.getDefault()))
+ {
+ Format[] formats = mf.getFormats();
+ for (int i = 0; i < formats.length; i++)
+ {
+ if (formats[i] instanceof DateFormat)
+ {
+ DateFormat temp = (DateFormat) formats[i];
+ temp.setTimeZone(timezone);
+ mf.setFormat(i,temp);
+ }
+ }
+ }
+ return mf.format(arguments);
+ }
+
+ protected String addExtraArgs(String msg, Locale locale)
+ {
+ if (extraArgs != null)
+ {
+ StringBuffer sb = new StringBuffer(msg);
+ Object[] filteredArgs = extraArgs.getFilteredArgs(locale);
+ for (int i = 0; i < filteredArgs.length; i++)
+ {
+ sb.append(filteredArgs[i]);
+ }
+ msg = sb.toString();
+ }
+ return msg;
+ }
+
+ /**
+ * Sets the {@link Filter} that is used to filter the arguments of this message
+ * @param filter the {@link Filter} to use. null
to disable filtering.
+ */
+ public void setFilter(Filter filter)
+ {
+ arguments.setFilter(filter);
+ if (extraArgs != null)
+ {
+ extraArgs.setFilter(filter);
+ }
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the current filter.
+ * @return the current filter
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ /**
+ * Set the {@link ClassLoader} which loads the resource files. If it is set to null
+ * then the default {@link ClassLoader} is used.
+ * @param loader the {@link ClassLoader} which loads the resource files
+ */
+ public void setClassLoader(ClassLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} which loads the resource files or null
+ * if the default ClassLoader is used.
+ * @return the {@link ClassLoader} which loads the resource files
+ */
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ /**
+ * Returns the id of the message in the resource bundle.
+ * @return the id of the message
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the name of the resource bundle for this message
+ * @return name of the resource file
+ */
+ public String getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Returns an Object[]
containing the message arguments.
+ * @return the message arguments
+ */
+ public Object[] getArguments()
+ {
+ return arguments.getArguments();
+ }
+
+ /**
+ *
+ * @param extraArg
+ */
+ public void setExtraArgument(Object extraArg)
+ {
+ setExtraArguments(new Object[] {extraArg});
+ }
+
+ /**
+ *
+ * @param extraArgs
+ */
+ public void setExtraArguments(Object[] extraArgs)
+ {
+ if (extraArgs != null)
+ {
+ this.extraArgs = new FilteredArguments(extraArgs);
+ this.extraArgs.setFilter(filter);
+ }
+ else
+ {
+ this.extraArgs = null;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Object[] getExtraArgs()
+ {
+ return (extraArgs == null) ? null : extraArgs.getArguments();
+ }
+
+ protected class FilteredArguments
+ {
+ protected static final int NO_FILTER = 0;
+ protected static final int FILTER = 1;
+ protected static final int FILTER_URL = 2;
+
+ protected Filter filter = null;
+
+ protected boolean[] isLocaleSpecific;
+ protected int[] argFilterType;
+ protected Object[] arguments;
+ protected Object[] unpackedArgs;
+ protected Object[] filteredArgs;
+
+ FilteredArguments()
+ {
+ this(new Object[0]);
+ }
+
+ FilteredArguments(Object[] args)
+ {
+ this.arguments = args;
+ this.unpackedArgs = new Object[args.length];
+ this.filteredArgs = new Object[args.length];
+ this.isLocaleSpecific = new boolean[args.length];
+ this.argFilterType = new int[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i] instanceof TrustedInput)
+ {
+ this.unpackedArgs[i] = ((TrustedInput) args[i]).getInput();
+ argFilterType[i] = NO_FILTER;
+ }
+ else if (args[i] instanceof UntrustedInput)
+ {
+ this.unpackedArgs[i] = ((UntrustedInput) args[i]).getInput();
+ if (args[i] instanceof UntrustedUrlInput)
+ {
+ argFilterType[i] = FILTER_URL;
+ }
+ else
+ {
+ argFilterType[i] = FILTER;
+ }
+ }
+ else
+ {
+ this.unpackedArgs[i] = args[i];
+ argFilterType[i] = FILTER;
+ }
+
+ // locale specific
+ this.isLocaleSpecific[i] = (this.unpackedArgs[i] instanceof LocaleString);
+ }
+ }
+
+ public boolean isEmpty()
+ {
+ return unpackedArgs.length == 0;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Object[] getFilteredArgs(Locale locale)
+ {
+ Object[] result = new Object[unpackedArgs.length];
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ Object arg;
+ if (filteredArgs[i] != null)
+ {
+ arg = filteredArgs[i];
+ }
+ else
+ {
+ arg = unpackedArgs[i];
+ if (isLocaleSpecific[i])
+ {
+ // get locale
+ arg = ((LocaleString) arg).getLocaleString(locale);
+ arg = filter(argFilterType[i], arg);
+ }
+ else
+ {
+ arg = filter(argFilterType[i], arg);
+ filteredArgs[i] = arg;
+ }
+ }
+ result[i] = arg;
+ }
+ return result;
+ }
+
+ private Object filter(int type, Object obj)
+ {
+ if (filter != null)
+ {
+ Object o = (null == obj) ? "null" : obj;
+ switch (type)
+ {
+ case NO_FILTER:
+ return o;
+ case FILTER:
+ return filter.doFilter(o.toString());
+ case FILTER_URL:
+ return filter.doFilterUrl(o.toString());
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ return obj;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(Filter filter)
+ {
+ if (filter != this.filter)
+ {
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ filteredArgs[i] = null;
+ }
+ }
+ this.filter = filter;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Resource: \"").append(resource);
+ sb.append("\" Id: \"").append(id).append("\"");
+ sb.append(" Arguments: ").append(arguments.getArguments().length).append(" normal, ")
+ .append(extraArgs.getArguments().length).append(" extra");
+ sb.append(" Encoding: ").append(encoding);
+ sb.append(" ClassLoader: ").append(loader);
+ return sb.toString();
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/i18n/MissingEntryException.java b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/i18n/MissingEntryException.java
new file mode 100644
index 000000000..582b9ab9b
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/i18n/MissingEntryException.java
@@ -0,0 +1,81 @@
+package org.spongycastle.i18n;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+
+public class MissingEntryException
+ extends RuntimeException
+{
+
+ protected final String resource;
+ protected final String key;
+ protected final ClassLoader loader;
+ protected final Locale locale;
+
+ private Throwable cause;
+ private String debugMsg;
+
+ public MissingEntryException(String message, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public MissingEntryException(String message, Throwable cause, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message);
+ this.cause = cause;
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public String getResource()
+ {
+ return resource;
+ }
+
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ public String getDebugMsg()
+ {
+ if (debugMsg == null)
+ {
+ debugMsg = "Can not find entry " + key + " in resource file " + resource + " for the locale " + locale + ".";
+ if (loader instanceof URLClassLoader)
+ {
+ URL[] urls = ((URLClassLoader) loader).getURLs();
+ debugMsg += " The following entries in the classpath were searched: ";
+ for (int i = 0; i != urls.length; i++)
+ {
+ debugMsg += urls[i] + " ";
+ }
+ }
+ }
+ return debugMsg;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/util/Shorts.java b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/util/Shorts.java
new file mode 100644
index 000000000..66a992f77
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.3/org/spongycastle/util/Shorts.java
@@ -0,0 +1,9 @@
+package org.spongycastle.util;
+
+public class Shorts
+{
+ public static Short valueOf(short value)
+ {
+ return new Short(value);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.4/org/spongycastle/util/Integers.java b/libraries/spongycastle/core/src/main/jdk1.4/org/spongycastle/util/Integers.java
new file mode 100644
index 000000000..3ac592d3c
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.4/org/spongycastle/util/Integers.java
@@ -0,0 +1,9 @@
+package org.spongycastle.util;
+
+public class Integers
+{
+ public static Integer valueOf(int value)
+ {
+ return new Integer(value);
+ }
+}
diff --git a/libraries/spongycastle/core/src/main/jdk1.4/org/spongycastle/util/Shorts.java b/libraries/spongycastle/core/src/main/jdk1.4/org/spongycastle/util/Shorts.java
new file mode 100644
index 000000000..66a992f77
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/jdk1.4/org/spongycastle/util/Shorts.java
@@ -0,0 +1,9 @@
+package org.spongycastle.util;
+
+public class Shorts
+{
+ public static Short valueOf(short value)
+ {
+ return new Short(value);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/README b/libraries/spongycastle/core/src/test/data/PKITS/README
new file mode 100644
index 000000000..00a124b65
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/PKITS/README
@@ -0,0 +1,3 @@
+PKITS test data from http://csrc.nist.gov/pki/testing/x509paths.html
+
+For more details please check the website above.
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesNoPoliciesTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesNoPoliciesTest2EE.crt
new file mode 100644
index 000000000..6c40491f2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesNoPoliciesTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesSamePoliciesTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesSamePoliciesTest10EE.crt
new file mode 100644
index 000000000..41b6d15b2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesSamePoliciesTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesSamePoliciesTest13EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesSamePoliciesTest13EE.crt
new file mode 100644
index 000000000..9210106f5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesSamePoliciesTest13EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesanyPolicyTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesanyPolicyTest11EE.crt
new file mode 100644
index 000000000..a7f9d22b0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/AllCertificatesanyPolicyTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/AnyPolicyTest14EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/AnyPolicyTest14EE.crt
new file mode 100644
index 000000000..b5961c75f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/AnyPolicyTest14EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BadCRLIssuerNameCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadCRLIssuerNameCACert.crt
new file mode 100644
index 000000000..6afe5ddd0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadCRLIssuerNameCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BadCRLSignatureCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadCRLSignatureCACert.crt
new file mode 100644
index 000000000..bfac8a406
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadCRLSignatureCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BadSignedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadSignedCACert.crt
new file mode 100644
index 000000000..abf7f319c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadSignedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BadnotAfterDateCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadnotAfterDateCACert.crt
new file mode 100644
index 000000000..d8babc267
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadnotAfterDateCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BadnotBeforeDateCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadnotBeforeDateCACert.crt
new file mode 100644
index 000000000..9b4cd824a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BadnotBeforeDateCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedCRLSigningKeyCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedCRLSigningKeyCACert.crt
new file mode 100644
index 000000000..41881148c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedCRLSigningKeyCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedCRLSigningKeyCRLCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedCRLSigningKeyCRLCert.crt
new file mode 100644
index 000000000..7c1b13952
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedCRLSigningKeyCRLCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedNewKeyCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedNewKeyCACert.crt
new file mode 100644
index 000000000..b4ccbe84f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedNewKeyCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedNewKeyOldWithNewCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedNewKeyOldWithNewCACert.crt
new file mode 100644
index 000000000..336a1caa0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedNewKeyOldWithNewCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedOldKeyCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedOldKeyCACert.crt
new file mode 100644
index 000000000..78953d66f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedOldKeyCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedOldKeyNewWithOldCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedOldKeyNewWithOldCACert.crt
new file mode 100644
index 000000000..853dc0613
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/BasicSelfIssuedOldKeyNewWithOldCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/CPSPointerQualifierTest20EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/CPSPointerQualifierTest20EE.crt
new file mode 100644
index 000000000..8bc703698
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/CPSPointerQualifierTest20EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DSACACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DSACACert.crt
new file mode 100644
index 000000000..a1f9e05f6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DSACACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DSAParametersInheritedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DSAParametersInheritedCACert.crt
new file mode 100644
index 000000000..7eae4863e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DSAParametersInheritedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest12EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest12EE.crt
new file mode 100644
index 000000000..6aa6ae5cd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest12EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest3EE.crt
new file mode 100644
index 000000000..8cd24ec7e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest4EE.crt
new file mode 100644
index 000000000..6af794d89
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest5EE.crt
new file mode 100644
index 000000000..43150466b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest7EE.crt
new file mode 100644
index 000000000..8caf46a33
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest8EE.crt
new file mode 100644
index 000000000..f49721d7e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest9EE.crt
new file mode 100644
index 000000000..49a38a558
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/DifferentPoliciesTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/GeneralizedTimeCRLnextUpdateCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/GeneralizedTimeCRLnextUpdateCACert.crt
new file mode 100644
index 000000000..c22228aa0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/GeneralizedTimeCRLnextUpdateCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodCACert.crt
new file mode 100644
index 000000000..5aecbc0cf
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodsubCACert.crt
new file mode 100644
index 000000000..09c98aad6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodsubCAPanyPolicyMapping1to2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodsubCAPanyPolicyMapping1to2CACert.crt
new file mode 100644
index 000000000..2540cd45d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/GoodsubCAPanyPolicyMapping1to2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBadCRLIssuerNameTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBadCRLIssuerNameTest5EE.crt
new file mode 100644
index 000000000..749f7cc0f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBadCRLIssuerNameTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBadCRLSignatureTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBadCRLSignatureTest4EE.crt
new file mode 100644
index 000000000..3c4d2cbe2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBadCRLSignatureTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt
new file mode 100644
index 000000000..7919115d2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt
new file mode 100644
index 000000000..b242bad64
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedNewWithOldTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedNewWithOldTest5EE.crt
new file mode 100644
index 000000000..e75d97865
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedNewWithOldTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedOldWithNewTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedOldWithNewTest2EE.crt
new file mode 100644
index 000000000..ed3000124
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidBasicSelfIssuedOldWithNewTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCASignatureTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCASignatureTest2EE.crt
new file mode 100644
index 000000000..8d337bddf
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCASignatureTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCAnotAfterDateTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCAnotAfterDateTest5EE.crt
new file mode 100644
index 000000000..6f6748d07
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCAnotAfterDateTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCAnotBeforeDateTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCAnotBeforeDateTest1EE.crt
new file mode 100644
index 000000000..e7b01de4d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidCAnotBeforeDateTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest31EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest31EE.crt
new file mode 100644
index 000000000..3f86eefe0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest31EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest33EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest33EE.crt
new file mode 100644
index 000000000..805205b29
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest33EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest38EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest38EE.crt
new file mode 100644
index 000000000..eff3779d3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNSnameConstraintsTest38EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNandRFC822nameConstraintsTest28EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNandRFC822nameConstraintsTest28EE.crt
new file mode 100644
index 000000000..7c1dd9cb2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNandRFC822nameConstraintsTest28EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNandRFC822nameConstraintsTest29EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNandRFC822nameConstraintsTest29EE.crt
new file mode 100644
index 000000000..bdf08adcb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNandRFC822nameConstraintsTest29EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest10EE.crt
new file mode 100644
index 000000000..5ff84a45b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest12EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest12EE.crt
new file mode 100644
index 000000000..11ec10fe8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest12EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest13EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest13EE.crt
new file mode 100644
index 000000000..08c3050d1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest13EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest15EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest15EE.crt
new file mode 100644
index 000000000..28ab46587
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest15EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest16EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest16EE.crt
new file mode 100644
index 000000000..56e42d0f1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest16EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest17EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest17EE.crt
new file mode 100644
index 000000000..f9f53b932
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest17EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest20EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest20EE.crt
new file mode 100644
index 000000000..15fbe8faa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest20EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest2EE.crt
new file mode 100644
index 000000000..8daf2f2a6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest3EE.crt
new file mode 100644
index 000000000..52cd9993e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest7EE.crt
new file mode 100644
index 000000000..799760b5f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest8EE.crt
new file mode 100644
index 000000000..d874621eb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest9EE.crt
new file mode 100644
index 000000000..18314bd01
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDNnameConstraintsTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDSASignatureTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDSASignatureTest6EE.crt
new file mode 100644
index 000000000..bcc900cf4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidDSASignatureTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEESignatureTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEESignatureTest3EE.crt
new file mode 100644
index 000000000..e21461e37
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEESignatureTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEEnotAfterDateTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEEnotAfterDateTest6EE.crt
new file mode 100644
index 000000000..46269d05e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEEnotAfterDateTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEEnotBeforeDateTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEEnotBeforeDateTest2EE.crt
new file mode 100644
index 000000000..f1bf1d1a4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidEEnotBeforeDateTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidIDPwithindirectCRLTest23EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidIDPwithindirectCRLTest23EE.crt
new file mode 100644
index 000000000..31965f628
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidIDPwithindirectCRLTest23EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidIDPwithindirectCRLTest26EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidIDPwithindirectCRLTest26EE.crt
new file mode 100644
index 000000000..b9b87a6c1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidIDPwithindirectCRLTest26EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidLongSerialNumberTest18EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidLongSerialNumberTest18EE.crt
new file mode 100644
index 000000000..1c84dce0e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidLongSerialNumberTest18EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMappingFromanyPolicyTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMappingFromanyPolicyTest7EE.crt
new file mode 100644
index 000000000..49aab726e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMappingFromanyPolicyTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMappingToanyPolicyTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMappingToanyPolicyTest8EE.crt
new file mode 100644
index 000000000..0a56c5e0d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMappingToanyPolicyTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMissingCRLTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMissingCRLTest1EE.crt
new file mode 100644
index 000000000..7af5ce5aa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMissingCRLTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMissingbasicConstraintsTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMissingbasicConstraintsTest1EE.crt
new file mode 100644
index 000000000..f5d8703c3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidMissingbasicConstraintsTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNameChainingOrderTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNameChainingOrderTest2EE.crt
new file mode 100644
index 000000000..9c40a3391
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNameChainingOrderTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNameChainingTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNameChainingTest1EE.crt
new file mode 100644
index 000000000..f7ae3b0e8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNameChainingTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNegativeSerialNumberTest15EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNegativeSerialNumberTest15EE.crt
new file mode 100644
index 000000000..2d323b4df
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidNegativeSerialNumberTest15EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidOldCRLnextUpdateTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidOldCRLnextUpdateTest11EE.crt
new file mode 100644
index 000000000..858db72d6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidOldCRLnextUpdateTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest10EE.crt
new file mode 100644
index 000000000..ef227b8ea
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest2EE.crt
new file mode 100644
index 000000000..58ace916a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest4EE.crt
new file mode 100644
index 000000000..c0dd555d5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidPolicyMappingTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest22EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest22EE.crt
new file mode 100644
index 000000000..d8134e0a6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest22EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest24EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest24EE.crt
new file mode 100644
index 000000000..b71c6a377
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest24EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest26EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest26EE.crt
new file mode 100644
index 000000000..0ffdf26fa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRFC822nameConstraintsTest26EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRevokedCATest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRevokedCATest2EE.crt
new file mode 100644
index 000000000..65a0a1ab4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRevokedCATest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRevokedEETest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRevokedEETest3EE.crt
new file mode 100644
index 000000000..80ea4d3f5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidRevokedEETest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitAnyPolicyTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitAnyPolicyTest10EE.crt
new file mode 100644
index 000000000..21c32e976
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitAnyPolicyTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitAnyPolicyTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitAnyPolicyTest8EE.crt
new file mode 100644
index 000000000..d57f0cb52
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitAnyPolicyTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest10EE.crt
new file mode 100644
index 000000000..577998c4f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest11EE.crt
new file mode 100644
index 000000000..621c325eb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest8EE.crt
new file mode 100644
index 000000000..98f8160cb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest9EE.crt
new file mode 100644
index 000000000..b80277286
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedinhibitPolicyMappingTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedpathLenConstraintTest16EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedpathLenConstraintTest16EE.crt
new file mode 100644
index 000000000..9fba63afb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedpathLenConstraintTest16EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedrequireExplicitPolicyTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedrequireExplicitPolicyTest7EE.crt
new file mode 100644
index 000000000..a1b58ffaa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedrequireExplicitPolicyTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedrequireExplicitPolicyTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedrequireExplicitPolicyTest8EE.crt
new file mode 100644
index 000000000..108f38c5f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSelfIssuedrequireExplicitPolicyTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSeparateCertificateandCRLKeysTest20EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSeparateCertificateandCRLKeysTest20EE.crt
new file mode 100644
index 000000000..8671cc6f5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSeparateCertificateandCRLKeysTest20EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSeparateCertificateandCRLKeysTest21EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSeparateCertificateandCRLKeysTest21EE.crt
new file mode 100644
index 000000000..b8faa4acc
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidSeparateCertificateandCRLKeysTest21EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidURInameConstraintsTest35EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidURInameConstraintsTest35EE.crt
new file mode 100644
index 000000000..5313fc96d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidURInameConstraintsTest35EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidURInameConstraintsTest37EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidURInameConstraintsTest37EE.crt
new file mode 100644
index 000000000..81fd01db7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidURInameConstraintsTest37EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLEntryExtensionTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLEntryExtensionTest8EE.crt
new file mode 100644
index 000000000..6170daef8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLEntryExtensionTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLExtensionTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLExtensionTest10EE.crt
new file mode 100644
index 000000000..8c8dfc0d8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLExtensionTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLExtensionTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLExtensionTest9EE.crt
new file mode 100644
index 000000000..83c1b6af4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCRLExtensionTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCriticalCertificateExtensionTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCriticalCertificateExtensionTest2EE.crt
new file mode 100644
index 000000000..65c7a68ec
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidUnknownCriticalCertificateExtensionTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidWrongCRLTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidWrongCRLTest6EE.crt
new file mode 100644
index 000000000..206892ceb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidWrongCRLTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcAFalseTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcAFalseTest2EE.crt
new file mode 100644
index 000000000..25b925a28
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcAFalseTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcAFalseTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcAFalseTest3EE.crt
new file mode 100644
index 000000000..5d8f86320
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcAFalseTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest27EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest27EE.crt
new file mode 100644
index 000000000..1cb9f595d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest27EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest31EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest31EE.crt
new file mode 100644
index 000000000..7e08a55d3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest31EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest32EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest32EE.crt
new file mode 100644
index 000000000..ec020a1a4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest32EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest34EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest34EE.crt
new file mode 100644
index 000000000..b309b809b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest34EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest35EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest35EE.crt
new file mode 100644
index 000000000..257daab5a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidcRLIssuerTest35EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLIndicatorNoBaseTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLIndicatorNoBaseTest1EE.crt
new file mode 100644
index 000000000..30e49a4ec
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLIndicatorNoBaseTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest10EE.crt
new file mode 100644
index 000000000..d60e4f698
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest3EE.crt
new file mode 100644
index 000000000..27ce111ae
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest4EE.crt
new file mode 100644
index 000000000..a7edcf890
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest6EE.crt
new file mode 100644
index 000000000..0e247f2ab
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest9EE.crt
new file mode 100644
index 000000000..e5b34e4ac
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddeltaCRLTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest2EE.crt
new file mode 100644
index 000000000..c61a6460a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest3EE.crt
new file mode 100644
index 000000000..531033234
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest6EE.crt
new file mode 100644
index 000000000..9236346ee
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest8EE.crt
new file mode 100644
index 000000000..3520f6a3f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest9EE.crt
new file mode 100644
index 000000000..5982bb6a2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvaliddistributionPointTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest1EE.crt
new file mode 100644
index 000000000..b6fe66182
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest4EE.crt
new file mode 100644
index 000000000..e6a924a1a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest5EE.crt
new file mode 100644
index 000000000..20c40a497
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest6EE.crt
new file mode 100644
index 000000000..03c8a3208
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitAnyPolicyTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest1EE.crt
new file mode 100644
index 000000000..f028a76ff
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest3EE.crt
new file mode 100644
index 000000000..3393af56c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest5EE.crt
new file mode 100644
index 000000000..374681425
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest6EE.crt
new file mode 100644
index 000000000..9f7eafa22
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidinhibitPolicyMappingTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt
new file mode 100644
index 000000000..888f7e2ae
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt
new file mode 100644
index 000000000..43b0d9587
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt
new file mode 100644
index 000000000..fbfb4c4e4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt
new file mode 100644
index 000000000..a9da41446
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsAttributeCertsTest14EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsAttributeCertsTest14EE.crt
new file mode 100644
index 000000000..60e0f7dbf
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsAttributeCertsTest14EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsCACertsTest12EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsCACertsTest12EE.crt
new file mode 100644
index 000000000..c0ada1281
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsCACertsTest12EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsUserCertsTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsUserCertsTest11EE.crt
new file mode 100644
index 000000000..acf119985
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlyContainsUserCertsTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest15EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest15EE.crt
new file mode 100644
index 000000000..45460ee00
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest15EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest16EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest16EE.crt
new file mode 100644
index 000000000..b82e84e61
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest16EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest17EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest17EE.crt
new file mode 100644
index 000000000..d29a9ff08
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest17EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest20EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest20EE.crt
new file mode 100644
index 000000000..431d60071
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest20EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest21EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest21EE.crt
new file mode 100644
index 000000000..68bf8f9ab
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidonlySomeReasonsTest21EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest10EE.crt
new file mode 100644
index 000000000..788389d3e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest11EE.crt
new file mode 100644
index 000000000..01ea4d0b7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest12EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest12EE.crt
new file mode 100644
index 000000000..231641608
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest12EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest5EE.crt
new file mode 100644
index 000000000..7164f04fe
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest6EE.crt
new file mode 100644
index 000000000..eec5f9de1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest9EE.crt
new file mode 100644
index 000000000..6a063a61d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidpathLenConstraintTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/Invalidpre2000CRLnextUpdateTest12EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/Invalidpre2000CRLnextUpdateTest12EE.crt
new file mode 100644
index 000000000..634a08e51
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/Invalidpre2000CRLnextUpdateTest12EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/Invalidpre2000UTCEEnotAfterDateTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/Invalidpre2000UTCEEnotAfterDateTest7EE.crt
new file mode 100644
index 000000000..88916af6f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/Invalidpre2000UTCEEnotAfterDateTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidrequireExplicitPolicyTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidrequireExplicitPolicyTest3EE.crt
new file mode 100644
index 000000000..38f98e475
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidrequireExplicitPolicyTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidrequireExplicitPolicyTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidrequireExplicitPolicyTest5EE.crt
new file mode 100644
index 000000000..ca3ea17b8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/InvalidrequireExplicitPolicyTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/LongSerialNumberCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/LongSerialNumberCACert.crt
new file mode 100644
index 000000000..6aaf3d0a4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/LongSerialNumberCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/Mapping1to2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/Mapping1to2CACert.crt
new file mode 100644
index 000000000..a458115a3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/Mapping1to2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/MappingFromanyPolicyCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/MappingFromanyPolicyCACert.crt
new file mode 100644
index 000000000..812da596d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/MappingFromanyPolicyCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/MappingToanyPolicyCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/MappingToanyPolicyCACert.crt
new file mode 100644
index 000000000..42effeb79
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/MappingToanyPolicyCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/MissingbasicConstraintsCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/MissingbasicConstraintsCACert.crt
new file mode 100644
index 000000000..17ebf2523
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/MissingbasicConstraintsCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/NameOrderingCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/NameOrderingCACert.crt
new file mode 100644
index 000000000..6b744db06
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/NameOrderingCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/NegativeSerialNumberCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/NegativeSerialNumberCACert.crt
new file mode 100644
index 000000000..57fc93300
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/NegativeSerialNumberCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/NoCRLCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/NoCRLCACert.crt
new file mode 100644
index 000000000..acd908c6f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/NoCRLCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/NoPoliciesCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/NoPoliciesCACert.crt
new file mode 100644
index 000000000..5e2a3fc70
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/NoPoliciesCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/NoissuingDistributionPointCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/NoissuingDistributionPointCACert.crt
new file mode 100644
index 000000000..bf4f8140f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/NoissuingDistributionPointCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/OldCRLnextUpdateCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/OldCRLnextUpdateCACert.crt
new file mode 100644
index 000000000..c9fb043b8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/OldCRLnextUpdateCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/OverlappingPoliciesTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/OverlappingPoliciesTest6EE.crt
new file mode 100644
index 000000000..56d136cfd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/OverlappingPoliciesTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3CACert.crt
new file mode 100644
index 000000000..f9ed7b756
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3subCACert.crt
new file mode 100644
index 000000000..2029d6ba7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3subsubCACert.crt
new file mode 100644
index 000000000..50e7fcd26
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/P12Mapping1to3subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/P1Mapping1to234CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/P1Mapping1to234CACert.crt
new file mode 100644
index 000000000..d7b3028d6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/P1Mapping1to234CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/P1Mapping1to234subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/P1Mapping1to234subCACert.crt
new file mode 100644
index 000000000..8648ddec9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/P1Mapping1to234subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/P1anyPolicyMapping1to2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/P1anyPolicyMapping1to2CACert.crt
new file mode 100644
index 000000000..85e39feaa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/P1anyPolicyMapping1to2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PanyPolicyMapping1to2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PanyPolicyMapping1to2CACert.crt
new file mode 100644
index 000000000..5abbb788d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PanyPolicyMapping1to2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234CACert.crt
new file mode 100644
index 000000000..9a5eb5b73
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234subCAP123Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234subCAP123Cert.crt
new file mode 100644
index 000000000..9b385455d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234subCAP123Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234subsubCAP123P12Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234subsubCAP123P12Cert.crt
new file mode 100644
index 000000000..4990a9b9d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP1234subsubCAP123P12Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123CACert.crt
new file mode 100644
index 000000000..03509d172
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subCAP12Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subCAP12Cert.crt
new file mode 100644
index 000000000..0009819fa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subCAP12Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubCAP12P1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubCAP12P1Cert.crt
new file mode 100644
index 000000000..669c18190
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubCAP12P1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubCAP12P2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubCAP12P2Cert.crt
new file mode 100644
index 000000000..faa7516b7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubCAP12P2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubsubCAP12P2P1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubsubCAP12P2P1Cert.crt
new file mode 100644
index 000000000..44346d5c2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP123subsubsubCAP12P2P1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12CACert.crt
new file mode 100644
index 000000000..27bf52414
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12subCAP1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12subCAP1Cert.crt
new file mode 100644
index 000000000..9a24328a7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12subCAP1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12subsubCAP1P2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12subsubCAP1P2Cert.crt
new file mode 100644
index 000000000..49cc0ed6d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP12subsubCAP1P2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP2subCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP2subCA2Cert.crt
new file mode 100644
index 000000000..ccbedc695
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP2subCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP2subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP2subCACert.crt
new file mode 100644
index 000000000..ce66a79b5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP2subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP3CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP3CACert.crt
new file mode 100644
index 000000000..90c4d266e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/PoliciesP3CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/RFC3280MandatoryAttributeTypesCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/RFC3280MandatoryAttributeTypesCACert.crt
new file mode 100644
index 000000000..fa0e1c8f4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/RFC3280MandatoryAttributeTypesCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/RFC3280OptionalAttributeTypesCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/RFC3280OptionalAttributeTypesCACert.crt
new file mode 100644
index 000000000..973373b10
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/RFC3280OptionalAttributeTypesCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/RevokedsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/RevokedsubCACert.crt
new file mode 100644
index 000000000..edbd547f0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/RevokedsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/RolloverfromPrintableStringtoUTF8StringCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/RolloverfromPrintableStringtoUTF8StringCACert.crt
new file mode 100644
index 000000000..658f20cf5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/RolloverfromPrintableStringtoUTF8StringCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCA2CRLSigningCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCA2CRLSigningCert.crt
new file mode 100644
index 000000000..67135a6c0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCA2CRLSigningCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCA2CertificateSigningCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCA2CertificateSigningCACert.crt
new file mode 100644
index 000000000..c05f92c3b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCA2CertificateSigningCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCRLSigningCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCRLSigningCert.crt
new file mode 100644
index 000000000..8c7200f87
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCRLSigningCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCertificateSigningCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCertificateSigningCACert.crt
new file mode 100644
index 000000000..10deedd4f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/SeparateCertificateandCRLKeysCertificateSigningCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/TrustAnchorRootCertificate.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/TrustAnchorRootCertificate.crt
new file mode 100644
index 000000000..21f520ee5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/TrustAnchorRootCertificate.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/TwoCRLsCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/TwoCRLsCACert.crt
new file mode 100644
index 000000000..c6389d36a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/TwoCRLsCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UIDCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UIDCACert.crt
new file mode 100644
index 000000000..d852bc095
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UIDCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UTF8StringCaseInsensitiveMatchCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UTF8StringCaseInsensitiveMatchCACert.crt
new file mode 100644
index 000000000..c59715d5e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UTF8StringCaseInsensitiveMatchCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UTF8StringEncodedNamesCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UTF8StringEncodedNamesCACert.crt
new file mode 100644
index 000000000..68d49e021
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UTF8StringEncodedNamesCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UnknownCRLEntryExtensionCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UnknownCRLEntryExtensionCACert.crt
new file mode 100644
index 000000000..8c81c3744
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UnknownCRLEntryExtensionCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UnknownCRLExtensionCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UnknownCRLExtensionCACert.crt
new file mode 100644
index 000000000..db7d39e59
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UnknownCRLExtensionCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest15EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest15EE.crt
new file mode 100644
index 000000000..e912cdfa9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest15EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest16EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest16EE.crt
new file mode 100644
index 000000000..ec04e1f3d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest16EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest17EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest17EE.crt
new file mode 100644
index 000000000..f78a47d6a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest17EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest18EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest18EE.crt
new file mode 100644
index 000000000..cc5cf5a77
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest18EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest19EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest19EE.crt
new file mode 100644
index 000000000..3df534a98
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/UserNoticeQualifierTest19EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt
new file mode 100644
index 000000000..034de7fe0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedNewWithOldTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedNewWithOldTest3EE.crt
new file mode 100644
index 000000000..199afb768
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedNewWithOldTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedNewWithOldTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedNewWithOldTest4EE.crt
new file mode 100644
index 000000000..d4323162e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedNewWithOldTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedOldWithNewTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedOldWithNewTest1EE.crt
new file mode 100644
index 000000000..b54a8b0f4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidBasicSelfIssuedOldWithNewTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidCertificatePathTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidCertificatePathTest1EE.crt
new file mode 100644
index 000000000..26985c9f6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidCertificatePathTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNSnameConstraintsTest30EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNSnameConstraintsTest30EE.crt
new file mode 100644
index 000000000..ec7d43d46
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNSnameConstraintsTest30EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNSnameConstraintsTest32EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNSnameConstraintsTest32EE.crt
new file mode 100644
index 000000000..ed88860ba
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNSnameConstraintsTest32EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNandRFC822nameConstraintsTest27EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNandRFC822nameConstraintsTest27EE.crt
new file mode 100644
index 000000000..73759dca2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNandRFC822nameConstraintsTest27EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest11EE.crt
new file mode 100644
index 000000000..1af47ba80
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest14EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest14EE.crt
new file mode 100644
index 000000000..ff249f051
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest14EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest18EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest18EE.crt
new file mode 100644
index 000000000..b658d671b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest18EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest19EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest19EE.crt
new file mode 100644
index 000000000..f4e2b8448
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest19EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest1EE.crt
new file mode 100644
index 000000000..4c86f9b77
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest4EE.crt
new file mode 100644
index 000000000..beb401360
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest5EE.crt
new file mode 100644
index 000000000..b68b6f7ca
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest6EE.crt
new file mode 100644
index 000000000..9aff6e874
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDNnameConstraintsTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDSAParameterInheritanceTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDSAParameterInheritanceTest5EE.crt
new file mode 100644
index 000000000..8fe2af452
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDSAParameterInheritanceTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDSASignaturesTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDSASignaturesTest4EE.crt
new file mode 100644
index 000000000..5b1cbc827
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidDSASignaturesTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimeCRLnextUpdateTest13EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimeCRLnextUpdateTest13EE.crt
new file mode 100644
index 000000000..a22f2e6c1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimeCRLnextUpdateTest13EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimenotAfterDateTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimenotAfterDateTest8EE.crt
new file mode 100644
index 000000000..15689c18c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimenotAfterDateTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimenotBeforeDateTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimenotBeforeDateTest4EE.crt
new file mode 100644
index 000000000..385bb1eaa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidGeneralizedTimenotBeforeDateTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest22EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest22EE.crt
new file mode 100644
index 000000000..6706cf1c3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest22EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest24EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest24EE.crt
new file mode 100644
index 000000000..bea72fe2b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest24EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest25EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest25EE.crt
new file mode 100644
index 000000000..994c90ad6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidIDPwithindirectCRLTest25EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidLongSerialNumberTest16EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidLongSerialNumberTest16EE.crt
new file mode 100644
index 000000000..11ba787ab
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidLongSerialNumberTest16EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidLongSerialNumberTest17EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidLongSerialNumberTest17EE.crt
new file mode 100644
index 000000000..75504db35
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidLongSerialNumberTest17EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingCapitalizationTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingCapitalizationTest5EE.crt
new file mode 100644
index 000000000..5b633871e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingCapitalizationTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingWhitespaceTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingWhitespaceTest3EE.crt
new file mode 100644
index 000000000..2aef73cfc
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingWhitespaceTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingWhitespaceTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingWhitespaceTest4EE.crt
new file mode 100644
index 000000000..6890cdd85
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameChainingWhitespaceTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameUIDsTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameUIDsTest6EE.crt
new file mode 100644
index 000000000..3cddea4a2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNameUIDsTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNegativeSerialNumberTest14EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNegativeSerialNumberTest14EE.crt
new file mode 100644
index 000000000..139a086d8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNegativeSerialNumberTest14EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNoissuingDistributionPointTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNoissuingDistributionPointTest10EE.crt
new file mode 100644
index 000000000..bd8ca38b0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidNoissuingDistributionPointTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest11EE.crt
new file mode 100644
index 000000000..9d19ad192
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest12EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest12EE.crt
new file mode 100644
index 000000000..76b9fe52c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest12EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest13EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest13EE.crt
new file mode 100644
index 000000000..7db330523
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest13EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest14EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest14EE.crt
new file mode 100644
index 000000000..57bf42655
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest14EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest1EE.crt
new file mode 100644
index 000000000..436cb9e02
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest3EE.crt
new file mode 100644
index 000000000..c835b0b24
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest5EE.crt
new file mode 100644
index 000000000..faaeb9e46
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest6EE.crt
new file mode 100644
index 000000000..2cd443323
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest9EE.crt
new file mode 100644
index 000000000..4debc2942
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidPolicyMappingTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC3280MandatoryAttributeTypesTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC3280MandatoryAttributeTypesTest7EE.crt
new file mode 100644
index 000000000..c3e977666
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC3280MandatoryAttributeTypesTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC3280OptionalAttributeTypesTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC3280OptionalAttributeTypesTest8EE.crt
new file mode 100644
index 000000000..f33c9d509
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC3280OptionalAttributeTypesTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest21EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest21EE.crt
new file mode 100644
index 000000000..743e9eb9a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest21EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest23EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest23EE.crt
new file mode 100644
index 000000000..f8703ed8d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest23EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest25EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest25EE.crt
new file mode 100644
index 000000000..e89ae7f80
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRFC822nameConstraintsTest25EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt
new file mode 100644
index 000000000..924d2f60a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitAnyPolicyTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitAnyPolicyTest7EE.crt
new file mode 100644
index 000000000..2e7e99546
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitAnyPolicyTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitAnyPolicyTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitAnyPolicyTest9EE.crt
new file mode 100644
index 000000000..764024716
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitAnyPolicyTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitPolicyMappingTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitPolicyMappingTest7EE.crt
new file mode 100644
index 000000000..b4b282d99
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedinhibitPolicyMappingTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedpathLenConstraintTest15EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedpathLenConstraintTest15EE.crt
new file mode 100644
index 000000000..0bad35f76
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedpathLenConstraintTest15EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedpathLenConstraintTest17EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedpathLenConstraintTest17EE.crt
new file mode 100644
index 000000000..1535ef864
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedpathLenConstraintTest17EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedrequireExplicitPolicyTest6EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedrequireExplicitPolicyTest6EE.crt
new file mode 100644
index 000000000..781ce0d38
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSelfIssuedrequireExplicitPolicyTest6EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSeparateCertificateandCRLKeysTest19EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSeparateCertificateandCRLKeysTest19EE.crt
new file mode 100644
index 000000000..fda18197b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidSeparateCertificateandCRLKeysTest19EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidTwoCRLsTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidTwoCRLsTest7EE.crt
new file mode 100644
index 000000000..e04433ad2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidTwoCRLsTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidURInameConstraintsTest34EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidURInameConstraintsTest34EE.crt
new file mode 100644
index 000000000..b554f91e6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidURInameConstraintsTest34EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidURInameConstraintsTest36EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidURInameConstraintsTest36EE.crt
new file mode 100644
index 000000000..8b1f00f21
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidURInameConstraintsTest36EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUTF8StringCaseInsensitiveMatchTest11EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUTF8StringCaseInsensitiveMatchTest11EE.crt
new file mode 100644
index 000000000..8a9d0ca82
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUTF8StringCaseInsensitiveMatchTest11EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUTF8StringEncodedNamesTest9EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUTF8StringEncodedNamesTest9EE.crt
new file mode 100644
index 000000000..c901690b8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUTF8StringEncodedNamesTest9EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUnknownNotCriticalCertificateExtensionTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUnknownNotCriticalCertificateExtensionTest1EE.crt
new file mode 100644
index 000000000..6ee8d1e3b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidUnknownNotCriticalCertificateExtensionTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidbasicConstraintsNotCriticalTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidbasicConstraintsNotCriticalTest4EE.crt
new file mode 100644
index 000000000..543710fda
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidbasicConstraintsNotCriticalTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest28EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest28EE.crt
new file mode 100644
index 000000000..1448aa4a3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest28EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest29EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest29EE.crt
new file mode 100644
index 000000000..ec442e1b1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest29EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest30EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest30EE.crt
new file mode 100644
index 000000000..2dc236766
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest30EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest33EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest33EE.crt
new file mode 100644
index 000000000..65b28440d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidcRLIssuerTest33EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest2EE.crt
new file mode 100644
index 000000000..0d24df052
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest5EE.crt
new file mode 100644
index 000000000..2bdaaf21e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest7EE.crt
new file mode 100644
index 000000000..3f6792194
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest8EE.crt
new file mode 100644
index 000000000..65861281c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddeltaCRLTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest1EE.crt
new file mode 100644
index 000000000..487cc2f9a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest4EE.crt
new file mode 100644
index 000000000..3782e79a8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest5EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest5EE.crt
new file mode 100644
index 000000000..07a8c494a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest5EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest7EE.crt
new file mode 100644
index 000000000..948f37fb9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValiddistributionPointTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitAnyPolicyTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitAnyPolicyTest2EE.crt
new file mode 100644
index 000000000..dc61d5231
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitAnyPolicyTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitPolicyMappingTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitPolicyMappingTest2EE.crt
new file mode 100644
index 000000000..fc432a1d4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitPolicyMappingTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitPolicyMappingTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitPolicyMappingTest4EE.crt
new file mode 100644
index 000000000..dce692769
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidinhibitPolicyMappingTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidkeyUsageNotCriticalTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidkeyUsageNotCriticalTest3EE.crt
new file mode 100644
index 000000000..bddbb9a26
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidkeyUsageNotCriticalTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlyContainsCACertsTest13EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlyContainsCACertsTest13EE.crt
new file mode 100644
index 000000000..f35f5de2a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlyContainsCACertsTest13EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlySomeReasonsTest18EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlySomeReasonsTest18EE.crt
new file mode 100644
index 000000000..7c0e1dedc
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlySomeReasonsTest18EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlySomeReasonsTest19EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlySomeReasonsTest19EE.crt
new file mode 100644
index 000000000..f3a811c1b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidonlySomeReasonsTest19EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest13EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest13EE.crt
new file mode 100644
index 000000000..dcf88340d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest13EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest14EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest14EE.crt
new file mode 100644
index 000000000..66fe25647
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest14EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest7EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest7EE.crt
new file mode 100644
index 000000000..5f689eaf7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest7EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest8EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest8EE.crt
new file mode 100644
index 000000000..2357bebf1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidpathLenConstraintTest8EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/Validpre2000UTCnotBeforeDateTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/Validpre2000UTCnotBeforeDateTest3EE.crt
new file mode 100644
index 000000000..5a6149927
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/Validpre2000UTCnotBeforeDateTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest1EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest1EE.crt
new file mode 100644
index 000000000..10399269e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest1EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest2EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest2EE.crt
new file mode 100644
index 000000000..451c2d81d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest2EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest4EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest4EE.crt
new file mode 100644
index 000000000..a2fa2f12b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/ValidrequireExplicitPolicyTest4EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/WrongCRLCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/WrongCRLCACert.crt
new file mode 100644
index 000000000..4cc51952a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/WrongCRLCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/anyPolicyCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/anyPolicyCACert.crt
new file mode 100644
index 000000000..4d9fb79b2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/anyPolicyCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsCriticalcAFalseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsCriticalcAFalseCACert.crt
new file mode 100644
index 000000000..12a8b5037
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsCriticalcAFalseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsNotCriticalCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsNotCriticalCACert.crt
new file mode 100644
index 000000000..8f9da1eb1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsNotCriticalCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsNotCriticalcAFalseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsNotCriticalcAFalseCACert.crt
new file mode 100644
index 000000000..ca61262ec
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/basicConstraintsNotCriticalcAFalseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA1Cert.crt
new file mode 100644
index 000000000..47f74eb40
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA2Cert.crt
new file mode 100644
index 000000000..a99dd4030
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA3Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA3Cert.crt
new file mode 100644
index 000000000..eeaaa36e4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLCA3Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLIndicatorNoBaseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLIndicatorNoBaseCACert.crt
new file mode 100644
index 000000000..5ec06b3d6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/deltaCRLIndicatorNoBaseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/distributionPoint1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/distributionPoint1CACert.crt
new file mode 100644
index 000000000..1e74bed04
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/distributionPoint1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/distributionPoint2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/distributionPoint2CACert.crt
new file mode 100644
index 000000000..a6d37be90
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/distributionPoint2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA1Cert.crt
new file mode 100644
index 000000000..ef079f653
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA2Cert.crt
new file mode 100644
index 000000000..4bfc0b5ed
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA3Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA3Cert.crt
new file mode 100644
index 000000000..f0787f0c5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA3Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA3cRLIssuerCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA3cRLIssuerCert.crt
new file mode 100644
index 000000000..f8089937b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA3cRLIssuerCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA4Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA4Cert.crt
new file mode 100644
index 000000000..6ebe43ce5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA4Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA4cRLIssuerCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA4cRLIssuerCert.crt
new file mode 100644
index 000000000..0468b0866
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA4cRLIssuerCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA5Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA5Cert.crt
new file mode 100644
index 000000000..90ba7d7e5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA5Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA6Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA6Cert.crt
new file mode 100644
index 000000000..3c5781e96
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/indirectCRLCA6Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy0CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy0CACert.crt
new file mode 100644
index 000000000..474968979
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy0CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1CACert.crt
new file mode 100644
index 000000000..8d35b0e67
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1SelfIssuedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1SelfIssuedCACert.crt
new file mode 100644
index 000000000..0362dde87
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1SelfIssuedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1SelfIssuedsubCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1SelfIssuedsubCA2Cert.crt
new file mode 100644
index 000000000..1d24bc194
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1SelfIssuedsubCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCA1Cert.crt
new file mode 100644
index 000000000..af02467cd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCA2Cert.crt
new file mode 100644
index 000000000..e8590f72e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCAIAP5Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCAIAP5Cert.crt
new file mode 100644
index 000000000..75bc59e4a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subCAIAP5Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subsubCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subsubCA2Cert.crt
new file mode 100644
index 000000000..cbf40ff09
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy1subsubCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5CACert.crt
new file mode 100644
index 000000000..3765f6e38
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5subCACert.crt
new file mode 100644
index 000000000..f75006d1e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5subsubCACert.crt
new file mode 100644
index 000000000..f2898ea06
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicy5subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicyTest3EE.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicyTest3EE.crt
new file mode 100644
index 000000000..850d6499f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitAnyPolicyTest3EE.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping0CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping0CACert.crt
new file mode 100644
index 000000000..b4934e898
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping0CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping0subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping0subCACert.crt
new file mode 100644
index 000000000..79f45b82b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping0subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12CACert.crt
new file mode 100644
index 000000000..57dd683c6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subCACert.crt
new file mode 100644
index 000000000..1e6bd7006
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subCAIPM5Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subCAIPM5Cert.crt
new file mode 100644
index 000000000..e68346842
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subCAIPM5Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subsubCACert.crt
new file mode 100644
index 000000000..f73f4d299
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subsubCAIPM5Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subsubCAIPM5Cert.crt
new file mode 100644
index 000000000..fe32edafb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P12subsubCAIPM5Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1CACert.crt
new file mode 100644
index 000000000..b3bff4667
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1SelfIssuedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1SelfIssuedCACert.crt
new file mode 100644
index 000000000..399bd824f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1SelfIssuedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1SelfIssuedsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1SelfIssuedsubCACert.crt
new file mode 100644
index 000000000..cd2ce9448
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1SelfIssuedsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1subCACert.crt
new file mode 100644
index 000000000..31d9af5dd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1subsubCACert.crt
new file mode 100644
index 000000000..13e78f0cc
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping1P1subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5CACert.crt
new file mode 100644
index 000000000..86ea42633
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subCACert.crt
new file mode 100644
index 000000000..788622cb9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subsubCACert.crt
new file mode 100644
index 000000000..d4a025e32
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subsubsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subsubsubCACert.crt
new file mode 100644
index 000000000..2c0e6e888
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/inhibitPolicyMapping5subsubsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageCriticalcRLSignFalseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageCriticalcRLSignFalseCACert.crt
new file mode 100644
index 000000000..a50545a1e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageCriticalcRLSignFalseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageCriticalkeyCertSignFalseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageCriticalkeyCertSignFalseCACert.crt
new file mode 100644
index 000000000..f6824d3a2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageCriticalkeyCertSignFalseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalCACert.crt
new file mode 100644
index 000000000..344f7d90a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalcRLSignFalseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalcRLSignFalseCACert.crt
new file mode 100644
index 000000000..c02d6f491
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalcRLSignFalseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalkeyCertSignFalseCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalkeyCertSignFalseCACert.crt
new file mode 100644
index 000000000..b9c46b5b8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/keyUsageNotCriticalkeyCertSignFalseCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1CACert.crt
new file mode 100644
index 000000000..5379f1fbb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1SelfIssuedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1SelfIssuedCACert.crt
new file mode 100644
index 000000000..75f1f7e60
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1SelfIssuedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA1Cert.crt
new file mode 100644
index 000000000..670291b8e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA2Cert.crt
new file mode 100644
index 000000000..a010eee16
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA3Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA3Cert.crt
new file mode 100644
index 000000000..b31c28a6f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN1subCA3Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN2CACert.crt
new file mode 100644
index 000000000..3aab55b46
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3CACert.crt
new file mode 100644
index 000000000..f1af18eac
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3subCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3subCA1Cert.crt
new file mode 100644
index 000000000..e40c5f06b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3subCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3subCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3subCA2Cert.crt
new file mode 100644
index 000000000..94247454d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN3subCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN4CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN4CACert.crt
new file mode 100644
index 000000000..141eb7264
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN4CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN5CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN5CACert.crt
new file mode 100644
index 000000000..a0d06284a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDN5CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDNS1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDNS1CACert.crt
new file mode 100644
index 000000000..10f0b35d2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDNS1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDNS2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDNS2CACert.crt
new file mode 100644
index 000000000..83fabc569
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsDNS2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA1Cert.crt
new file mode 100644
index 000000000..07fcc37a5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA2Cert.crt
new file mode 100644
index 000000000..2001bfa39
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA3Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA3Cert.crt
new file mode 100644
index 000000000..e3723693b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsRFC822CA3Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsURI1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsURI1CACert.crt
new file mode 100644
index 000000000..d0dbca5ee
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsURI1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsURI2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsURI2CACert.crt
new file mode 100644
index 000000000..bf988e6d6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/nameConstraintsURI2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsAttributeCertsCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsAttributeCertsCACert.crt
new file mode 100644
index 000000000..6855fbe46
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsAttributeCertsCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsCACertsCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsCACertsCACert.crt
new file mode 100644
index 000000000..055d8aace
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsCACertsCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsUserCertsCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsUserCertsCACert.crt
new file mode 100644
index 000000000..f8fc85e7a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlyContainsUserCertsCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA1Cert.crt
new file mode 100644
index 000000000..26ee389ad
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA2Cert.crt
new file mode 100644
index 000000000..3c444e1e6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA3Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA3Cert.crt
new file mode 100644
index 000000000..3b0969977
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA3Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA4Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA4Cert.crt
new file mode 100644
index 000000000..4889d5ce8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/onlySomeReasonsCA4Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0CACert.crt
new file mode 100644
index 000000000..73c9433c4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0SelfIssuedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0SelfIssuedCACert.crt
new file mode 100644
index 000000000..f66228e11
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0SelfIssuedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0subCA2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0subCA2Cert.crt
new file mode 100644
index 000000000..c5cdea3df
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0subCA2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0subCACert.crt
new file mode 100644
index 000000000..c51de222e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint0subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1CACert.crt
new file mode 100644
index 000000000..b1da15b82
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1SelfIssuedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1SelfIssuedCACert.crt
new file mode 100644
index 000000000..02aeffa91
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1SelfIssuedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1SelfIssuedsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1SelfIssuedsubCACert.crt
new file mode 100644
index 000000000..0a94f5f4f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1SelfIssuedsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1subCACert.crt
new file mode 100644
index 000000000..a84fb534a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint1subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6CACert.crt
new file mode 100644
index 000000000..d89052bd9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA0Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA0Cert.crt
new file mode 100644
index 000000000..4b220169b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA0Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA1Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA1Cert.crt
new file mode 100644
index 000000000..b25ba34cb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA1Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA4Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA4Cert.crt
new file mode 100644
index 000000000..ca87ee8bf
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subCA4Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA00Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA00Cert.crt
new file mode 100644
index 000000000..2b88eb6d7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA00Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA11Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA11Cert.crt
new file mode 100644
index 000000000..de511c4d9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA11Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA41Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA41Cert.crt
new file mode 100644
index 000000000..c6e6096b0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubCA41Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubsubCA11XCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubsubCA11XCert.crt
new file mode 100644
index 000000000..9c9dc4e15
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubsubCA11XCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubsubCA41XCert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubsubCA41XCert.crt
new file mode 100644
index 000000000..2729261cc
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pathLenConstraint6subsubsubCA41XCert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/pre2000CRLnextUpdateCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/pre2000CRLnextUpdateCACert.crt
new file mode 100644
index 000000000..1a79b8158
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/pre2000CRLnextUpdateCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0CACert.crt
new file mode 100644
index 000000000..0eb93be3f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subCACert.crt
new file mode 100644
index 000000000..f96129ec7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subsubCACert.crt
new file mode 100644
index 000000000..c0d9b49c0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subsubsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subsubsubCACert.crt
new file mode 100644
index 000000000..497d53a8b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy0subsubsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10CACert.crt
new file mode 100644
index 000000000..b3406b122
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subCACert.crt
new file mode 100644
index 000000000..1544bbb75
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subsubCACert.crt
new file mode 100644
index 000000000..0166d99f3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subsubsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subsubsubCACert.crt
new file mode 100644
index 000000000..8018f6afa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy10subsubsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2CACert.crt
new file mode 100644
index 000000000..7ec4e4944
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2SelfIssuedCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2SelfIssuedCACert.crt
new file mode 100644
index 000000000..285a05c72
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2SelfIssuedCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2SelfIssuedsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2SelfIssuedsubCACert.crt
new file mode 100644
index 000000000..f29b37f27
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2SelfIssuedsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2subCACert.crt
new file mode 100644
index 000000000..a1f20a3a5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy2subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4CACert.crt
new file mode 100644
index 000000000..c2a9c464f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subCACert.crt
new file mode 100644
index 000000000..9f9ea5bff
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subsubCACert.crt
new file mode 100644
index 000000000..3d0f27852
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subsubsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subsubsubCACert.crt
new file mode 100644
index 000000000..a14f9d474
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy4subsubsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5CACert.crt
new file mode 100644
index 000000000..ef2010b4d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subCACert.crt
new file mode 100644
index 000000000..99d31d162
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subsubCACert.crt
new file mode 100644
index 000000000..99afa4d14
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subsubsubCACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subsubsubCACert.crt
new file mode 100644
index 000000000..9abe48ddd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy5subsubsubCACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7CACert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7CACert.crt
new file mode 100644
index 000000000..cac6bb62e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7CACert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subCARE2Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subCARE2Cert.crt
new file mode 100644
index 000000000..d55d884c5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subCARE2Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subsubCARE2RE4Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subsubCARE2RE4Cert.crt
new file mode 100644
index 000000000..1c9aec850
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subsubCARE2RE4Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subsubsubCARE2RE4Cert.crt b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subsubsubCARE2RE4Cert.crt
new file mode 100644
index 000000000..ecd5f45a4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/certs/requireExplicitPolicy7subsubsubCARE2RE4Cert.crt differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BadCRLIssuerNameCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadCRLIssuerNameCACRL.crl
new file mode 100644
index 000000000..d4871b55f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadCRLIssuerNameCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BadCRLSignatureCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadCRLSignatureCACRL.crl
new file mode 100644
index 000000000..b1658c34f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadCRLSignatureCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BadSignedCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadSignedCACRL.crl
new file mode 100644
index 000000000..e0ded9b4c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadSignedCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BadnotAfterDateCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadnotAfterDateCACRL.crl
new file mode 100644
index 000000000..1ec2a0e81
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadnotAfterDateCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BadnotBeforeDateCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadnotBeforeDateCACRL.crl
new file mode 100644
index 000000000..1a96d0f76
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BadnotBeforeDateCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedCRLSigningKeyCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedCRLSigningKeyCACRL.crl
new file mode 100644
index 000000000..fed48645b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedCRLSigningKeyCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.crl
new file mode 100644
index 000000000..053471f83
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedNewKeyCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedNewKeyCACRL.crl
new file mode 100644
index 000000000..7370ed295
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedNewKeyCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedOldKeyCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedOldKeyCACRL.crl
new file mode 100644
index 000000000..dee61837e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedOldKeyCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.crl
new file mode 100644
index 000000000..4e7e0145b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/DSACACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/DSACACRL.crl
new file mode 100644
index 000000000..46463c8a2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/DSACACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/DSAParametersInheritedCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/DSAParametersInheritedCACRL.crl
new file mode 100644
index 000000000..5bf724527
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/DSAParametersInheritedCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/GeneralizedTimeCRLnextUpdateCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/GeneralizedTimeCRLnextUpdateCACRL.crl
new file mode 100644
index 000000000..40387d3cf
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/GeneralizedTimeCRLnextUpdateCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodCACRL.crl
new file mode 100644
index 000000000..2fdc3cc1a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodsubCACRL.crl
new file mode 100644
index 000000000..963d7033b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodsubCAPanyPolicyMapping1to2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodsubCAPanyPolicyMapping1to2CACRL.crl
new file mode 100644
index 000000000..9c5d1b278
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/GoodsubCAPanyPolicyMapping1to2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/LongSerialNumberCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/LongSerialNumberCACRL.crl
new file mode 100644
index 000000000..55f39b844
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/LongSerialNumberCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/Mapping1to2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/Mapping1to2CACRL.crl
new file mode 100644
index 000000000..36e07e1e3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/Mapping1to2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/MappingFromanyPolicyCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/MappingFromanyPolicyCACRL.crl
new file mode 100644
index 000000000..025b6bbba
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/MappingFromanyPolicyCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/MappingToanyPolicyCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/MappingToanyPolicyCACRL.crl
new file mode 100644
index 000000000..99f12535d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/MappingToanyPolicyCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/MissingbasicConstraintsCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/MissingbasicConstraintsCACRL.crl
new file mode 100644
index 000000000..f91729cc1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/MissingbasicConstraintsCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/NameOrderCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/NameOrderCACRL.crl
new file mode 100644
index 000000000..4cd201583
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/NameOrderCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/NegativeSerialNumberCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/NegativeSerialNumberCACRL.crl
new file mode 100644
index 000000000..99514d7a4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/NegativeSerialNumberCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/NoPoliciesCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/NoPoliciesCACRL.crl
new file mode 100644
index 000000000..b77586bc6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/NoPoliciesCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/NoissuingDistributionPointCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/NoissuingDistributionPointCACRL.crl
new file mode 100644
index 000000000..c7d5b1d63
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/NoissuingDistributionPointCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/OldCRLnextUpdateCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/OldCRLnextUpdateCACRL.crl
new file mode 100644
index 000000000..f121dff3d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/OldCRLnextUpdateCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3CACRL.crl
new file mode 100644
index 000000000..451d1986e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3subCACRL.crl
new file mode 100644
index 000000000..b063e6bce
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3subsubCACRL.crl
new file mode 100644
index 000000000..6dcdf05d9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/P12Mapping1to3subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/P1Mapping1to234CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/P1Mapping1to234CACRL.crl
new file mode 100644
index 000000000..70febec74
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/P1Mapping1to234CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/P1Mapping1to234subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/P1Mapping1to234subCACRL.crl
new file mode 100644
index 000000000..8ee779929
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/P1Mapping1to234subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/P1anyPolicyMapping1to2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/P1anyPolicyMapping1to2CACRL.crl
new file mode 100644
index 000000000..8cf52dc8f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/P1anyPolicyMapping1to2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PanyPolicyMapping1to2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PanyPolicyMapping1to2CACRL.crl
new file mode 100644
index 000000000..51482debf
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PanyPolicyMapping1to2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234CACRL.crl
new file mode 100644
index 000000000..48c6b1a6f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234subCAP123CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234subCAP123CRL.crl
new file mode 100644
index 000000000..aa8426312
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234subCAP123CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234subsubCAP123P12CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234subsubCAP123P12CRL.crl
new file mode 100644
index 000000000..ae1a01941
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP1234subsubCAP123P12CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123CACRL.crl
new file mode 100644
index 000000000..deb37062e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subCAP12CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subCAP12CRL.crl
new file mode 100644
index 000000000..ecd65f87b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subCAP12CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubCAP12P1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubCAP12P1CRL.crl
new file mode 100644
index 000000000..51f09f6d7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubCAP12P1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubCAP2P2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubCAP2P2CRL.crl
new file mode 100644
index 000000000..5d6fb365d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubCAP2P2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubsubCAP12P2P1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubsubCAP12P2P1CRL.crl
new file mode 100644
index 000000000..07908f67d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP123subsubsubCAP12P2P1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12CACRL.crl
new file mode 100644
index 000000000..5b090b05b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12subCAP1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12subCAP1CRL.crl
new file mode 100644
index 000000000..d2f29b792
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12subCAP1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12subsubCAP1P2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12subsubCAP1P2CRL.crl
new file mode 100644
index 000000000..bd4cf7576
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP12subsubCAP1P2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP2subCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP2subCA2CRL.crl
new file mode 100644
index 000000000..774bc7325
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP2subCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP2subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP2subCACRL.crl
new file mode 100644
index 000000000..7d7ba76b0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP2subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP3CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP3CACRL.crl
new file mode 100644
index 000000000..9d81c6da9
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/PoliciesP3CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/RFC3280MandatoryAttributeTypesCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/RFC3280MandatoryAttributeTypesCACRL.crl
new file mode 100644
index 000000000..63ed6556f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/RFC3280MandatoryAttributeTypesCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/RFC3280OptionalAttributeTypesCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/RFC3280OptionalAttributeTypesCACRL.crl
new file mode 100644
index 000000000..e088ab148
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/RFC3280OptionalAttributeTypesCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/RevokedsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/RevokedsubCACRL.crl
new file mode 100644
index 000000000..c77ffa358
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/RevokedsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/RolloverfromPrintableStringtoUTF8StringCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/RolloverfromPrintableStringtoUTF8StringCACRL.crl
new file mode 100644
index 000000000..c7f5c7acd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/RolloverfromPrintableStringtoUTF8StringCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/SeparateCertificateandCRLKeysCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/SeparateCertificateandCRLKeysCA2CRL.crl
new file mode 100644
index 000000000..a85f99f7c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/SeparateCertificateandCRLKeysCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/SeparateCertificateandCRLKeysCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/SeparateCertificateandCRLKeysCRL.crl
new file mode 100644
index 000000000..4d159dd45
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/SeparateCertificateandCRLKeysCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/TrustAnchorRootCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/TrustAnchorRootCRL.crl
new file mode 100644
index 000000000..3ba3df615
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/TrustAnchorRootCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/TwoCRLsCABadCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/TwoCRLsCABadCRL.crl
new file mode 100644
index 000000000..fba92fae4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/TwoCRLsCABadCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/TwoCRLsCAGoodCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/TwoCRLsCAGoodCRL.crl
new file mode 100644
index 000000000..fcb7488a6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/TwoCRLsCAGoodCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/UIDCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/UIDCACRL.crl
new file mode 100644
index 000000000..0da091a66
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/UIDCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/UTF8StringCaseInsensitiveMatchCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/UTF8StringCaseInsensitiveMatchCACRL.crl
new file mode 100644
index 000000000..9ee2a2354
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/UTF8StringCaseInsensitiveMatchCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/UTF8StringEncodedNamesCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/UTF8StringEncodedNamesCACRL.crl
new file mode 100644
index 000000000..3d7de0022
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/UTF8StringEncodedNamesCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/UnknownCRLEntryExtensionCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/UnknownCRLEntryExtensionCACRL.crl
new file mode 100644
index 000000000..efbdae412
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/UnknownCRLEntryExtensionCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/UnknownCRLExtensionCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/UnknownCRLExtensionCACRL.crl
new file mode 100644
index 000000000..de7111393
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/UnknownCRLExtensionCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/WrongCRLCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/WrongCRLCACRL.crl
new file mode 100644
index 000000000..3ba3df615
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/WrongCRLCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/anyPolicyCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/anyPolicyCACRL.crl
new file mode 100644
index 000000000..8506ea112
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/anyPolicyCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsCriticalcAFalseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsCriticalcAFalseCACRL.crl
new file mode 100644
index 000000000..15a7e3d10
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsCriticalcAFalseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsNotCriticalCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsNotCriticalCACRL.crl
new file mode 100644
index 000000000..9e5ac6215
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsNotCriticalCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsNotCriticalcAFalseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsNotCriticalcAFalseCACRL.crl
new file mode 100644
index 000000000..dfbbec9f8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/basicConstraintsNotCriticalcAFalseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA1CRL.crl
new file mode 100644
index 000000000..fb562aaa6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA1deltaCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA1deltaCRL.crl
new file mode 100644
index 000000000..9a76c5cf1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA1deltaCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA2CRL.crl
new file mode 100644
index 000000000..36d66fe6d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA2deltaCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA2deltaCRL.crl
new file mode 100644
index 000000000..713d54a4c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA2deltaCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA3CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA3CRL.crl
new file mode 100644
index 000000000..4527c9a8f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA3CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA3deltaCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA3deltaCRL.crl
new file mode 100644
index 000000000..bfb3c1d63
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLCA3deltaCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLIndicatorNoBaseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLIndicatorNoBaseCACRL.crl
new file mode 100644
index 000000000..b9a591e1a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/deltaCRLIndicatorNoBaseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/distributionPoint1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/distributionPoint1CACRL.crl
new file mode 100644
index 000000000..1be74de20
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/distributionPoint1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/distributionPoint2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/distributionPoint2CACRL.crl
new file mode 100644
index 000000000..5bdc14914
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/distributionPoint2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA1CRL.crl
new file mode 100644
index 000000000..6eed456cb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA3CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA3CRL.crl
new file mode 100644
index 000000000..02be17931
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA3CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA3cRLIssuerCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA3cRLIssuerCRL.crl
new file mode 100644
index 000000000..166a457ea
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA3cRLIssuerCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA4cRLIssuerCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA4cRLIssuerCRL.crl
new file mode 100644
index 000000000..b870a7fa5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA4cRLIssuerCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA5CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA5CRL.crl
new file mode 100644
index 000000000..25c2e8145
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/indirectCRLCA5CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy0CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy0CACRL.crl
new file mode 100644
index 000000000..301f7456a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy0CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1CACRL.crl
new file mode 100644
index 000000000..ab1364573
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCA1CRL.crl
new file mode 100644
index 000000000..46c0e0c00
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCA2CRL.crl
new file mode 100644
index 000000000..1ee4b77ea
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCAIAP5CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCAIAP5CRL.crl
new file mode 100644
index 000000000..af4fff09e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subCAIAP5CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subsubCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subsubCA2CRL.crl
new file mode 100644
index 000000000..3b6b35c33
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy1subsubCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5CACRL.crl
new file mode 100644
index 000000000..07f5e3de2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5subCACRL.crl
new file mode 100644
index 000000000..373bdebbe
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5subsubCACRL.crl
new file mode 100644
index 000000000..e56b61c9a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitAnyPolicy5subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping0CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping0CACRL.crl
new file mode 100644
index 000000000..1ebad7097
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping0CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping0subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping0subCACRL.crl
new file mode 100644
index 000000000..e3f4f9712
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping0subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12CACRL.crl
new file mode 100644
index 000000000..5291d66d1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subCACRL.crl
new file mode 100644
index 000000000..9c155a976
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subCAIPM5CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subCAIPM5CRL.crl
new file mode 100644
index 000000000..dc7fe6836
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subCAIPM5CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subsubCACRL.crl
new file mode 100644
index 000000000..9fcab42c6
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.crl
new file mode 100644
index 000000000..ebcdc5bb7
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1CACRL.crl
new file mode 100644
index 000000000..36c2b7918
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1subCACRL.crl
new file mode 100644
index 000000000..1fa7ac98f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1subsubCACRL.crl
new file mode 100644
index 000000000..3b1ac99ff
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping1P1subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5CACRL.crl
new file mode 100644
index 000000000..a19deb75f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subCACRL.crl
new file mode 100644
index 000000000..c3ef69116
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subsubCACRL.crl
new file mode 100644
index 000000000..45df218ac
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subsubsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subsubsubCACRL.crl
new file mode 100644
index 000000000..3ca93d4d0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/inhibitPolicyMapping5subsubsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageCriticalcRLSignFalseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageCriticalcRLSignFalseCACRL.crl
new file mode 100644
index 000000000..6f02f8089
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageCriticalcRLSignFalseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageCriticalkeyCertSignFalseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageCriticalkeyCertSignFalseCACRL.crl
new file mode 100644
index 000000000..4abda7660
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageCriticalkeyCertSignFalseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalCACRL.crl
new file mode 100644
index 000000000..358e4e6a0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalcRLSignFalseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalcRLSignFalseCACRL.crl
new file mode 100644
index 000000000..707c73c2c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalcRLSignFalseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.crl
new file mode 100644
index 000000000..5e817b640
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1CACRL.crl
new file mode 100644
index 000000000..10c7389ed
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA1CRL.crl
new file mode 100644
index 000000000..9d33b7c9f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA2CRL.crl
new file mode 100644
index 000000000..7a3949e5a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA3CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA3CRL.crl
new file mode 100644
index 000000000..22aa2f9b1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN1subCA3CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN2CACRL.crl
new file mode 100644
index 000000000..da6fe6f80
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3CACRL.crl
new file mode 100644
index 000000000..83fd3a5e1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3subCA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3subCA1CRL.crl
new file mode 100644
index 000000000..8c6fb506d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3subCA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3subCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3subCA2CRL.crl
new file mode 100644
index 000000000..1797663c3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN3subCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN4CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN4CACRL.crl
new file mode 100644
index 000000000..ae9f73a86
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN4CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN5CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN5CACRL.crl
new file mode 100644
index 000000000..46dbb8812
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDN5CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDNS1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDNS1CACRL.crl
new file mode 100644
index 000000000..94fa45e45
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDNS1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDNS2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDNS2CACRL.crl
new file mode 100644
index 000000000..214093179
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsDNS2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA1CRL.crl
new file mode 100644
index 000000000..a4b047314
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA2CRL.crl
new file mode 100644
index 000000000..2042f6fc8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA3CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA3CRL.crl
new file mode 100644
index 000000000..8f207e51e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsRFC822CA3CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsURI1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsURI1CACRL.crl
new file mode 100644
index 000000000..b19c9de77
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsURI1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsURI2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsURI2CACRL.crl
new file mode 100644
index 000000000..3dbc011d4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/nameConstraintsURI2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsAttributeCertsCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsAttributeCertsCACRL.crl
new file mode 100644
index 000000000..0993754a2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsAttributeCertsCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsCACertsCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsCACertsCACRL.crl
new file mode 100644
index 000000000..621dfa9bc
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsCACertsCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsUserCertsCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsUserCertsCACRL.crl
new file mode 100644
index 000000000..1aee7c2ba
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlyContainsUserCertsCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA1compromiseCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA1compromiseCRL.crl
new file mode 100644
index 000000000..3d5ff65da
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA1compromiseCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA1otherreasonsCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA1otherreasonsCRL.crl
new file mode 100644
index 000000000..83cce82f0
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA1otherreasonsCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA2CRL1.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA2CRL1.crl
new file mode 100644
index 000000000..eb408f271
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA2CRL1.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA2CRL2.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA2CRL2.crl
new file mode 100644
index 000000000..e333d2650
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA2CRL2.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA3compromiseCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA3compromiseCRL.crl
new file mode 100644
index 000000000..6837068b2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA3compromiseCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA3otherreasonsCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA3otherreasonsCRL.crl
new file mode 100644
index 000000000..ef4ee3e05
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA3otherreasonsCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA4compromiseCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA4compromiseCRL.crl
new file mode 100644
index 000000000..45fcc0bbd
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA4compromiseCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA4otherreasonsCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA4otherreasonsCRL.crl
new file mode 100644
index 000000000..0fca68195
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/onlySomeReasonsCA4otherreasonsCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0CACRL.crl
new file mode 100644
index 000000000..1e52e650f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0subCA2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0subCA2CRL.crl
new file mode 100644
index 000000000..69488c7ba
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0subCA2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0subCACRL.crl
new file mode 100644
index 000000000..00295797c
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint0subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint1CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint1CACRL.crl
new file mode 100644
index 000000000..9c069801e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint1CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint1subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint1subCACRL.crl
new file mode 100644
index 000000000..61d007640
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint1subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6CACRL.crl
new file mode 100644
index 000000000..779c2b785
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA0CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA0CRL.crl
new file mode 100644
index 000000000..30fee13e3
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA0CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA1CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA1CRL.crl
new file mode 100644
index 000000000..71eafbb42
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA1CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA4CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA4CRL.crl
new file mode 100644
index 000000000..8d14b0c8a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subCA4CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA00CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA00CRL.crl
new file mode 100644
index 000000000..24ecdde93
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA00CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA11CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA11CRL.crl
new file mode 100644
index 000000000..51b4ab70e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA11CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA41CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA41CRL.crl
new file mode 100644
index 000000000..9e4e18181
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubCA41CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubsubCA11XCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubsubCA11XCRL.crl
new file mode 100644
index 000000000..5891e6308
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubsubCA11XCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubsubCA41XCRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubsubCA41XCRL.crl
new file mode 100644
index 000000000..217e5e57e
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pathLenConstraint6subsubsubCA41XCRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/pre2000CRLnextUpdateCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/pre2000CRLnextUpdateCACRL.crl
new file mode 100644
index 000000000..6315186f4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/pre2000CRLnextUpdateCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0CACRL.crl
new file mode 100644
index 000000000..5ac2d2764
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subCACRL.crl
new file mode 100644
index 000000000..569ff3e26
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subsubCACRL.crl
new file mode 100644
index 000000000..c614cbb1d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subsubsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subsubsubCACRL.crl
new file mode 100644
index 000000000..910c035ff
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy0subsubsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10CACRL.crl
new file mode 100644
index 000000000..7bfbf7634
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subCACRL.crl
new file mode 100644
index 000000000..bc4845d41
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subsubCACRL.crl
new file mode 100644
index 000000000..802a6520d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subsubsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subsubsubCACRL.crl
new file mode 100644
index 000000000..6f84d3972
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy10subsubsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy2CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy2CACRL.crl
new file mode 100644
index 000000000..e14cdaa0b
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy2CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy2subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy2subCACRL.crl
new file mode 100644
index 000000000..883091750
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy2subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4CACRL.crl
new file mode 100644
index 000000000..c6817a34a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subCACRL.crl
new file mode 100644
index 000000000..d1f5ad1e4
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subsubCACRL.crl
new file mode 100644
index 000000000..7203b19af
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subsubsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subsubsubCACRL.crl
new file mode 100644
index 000000000..467e00472
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy4subsubsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5CACRL.crl
new file mode 100644
index 000000000..96848db79
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subCACRL.crl
new file mode 100644
index 000000000..8bb7c1dbb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subsubCACRL.crl
new file mode 100644
index 000000000..143dab515
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subsubsubCACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subsubsubCACRL.crl
new file mode 100644
index 000000000..8a9c8b363
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy5subsubsubCACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7CACRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7CACRL.crl
new file mode 100644
index 000000000..43870fb63
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7CACRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subCARE2CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subCARE2CRL.crl
new file mode 100644
index 000000000..48c70c0fa
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subCARE2CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subsubCARE2RE4CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subsubCARE2RE4CRL.crl
new file mode 100644
index 000000000..3808af657
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subsubCARE2RE4CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.crl b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.crl
new file mode 100644
index 000000000..4ed5b0a13
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/PKITS/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.crl differ
diff --git a/libraries/spongycastle/core/src/test/data/cmp/sample_cr.der b/libraries/spongycastle/core/src/test/data/cmp/sample_cr.der
new file mode 100644
index 000000000..6322bee05
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/cmp/sample_cr.der differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/README.txt b/libraries/spongycastle/core/src/test/data/openpgp/dsa/README.txt
new file mode 100644
index 000000000..9abffbf27
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/openpgp/dsa/README.txt
@@ -0,0 +1,36 @@
+This archive contains material to help verify interoperability to the
+OpenPGP DSA2 design as implemented in GnuPG.
+
+Keys are located in the keys directory. Included are:
+
+ 1024 bits, 160 bit q size (i.e. regular old DSA)
+ 2048 bits, 224 bit q size
+ 3072 bits, 256 bit q size
+ 7680 bits, 384 bit q size
+15360 bits, 512 bit q size
+
+All secret keys have the passphrase "test".
+
+Note the inclusion of 7680/384 and 15360/512 keys. They're large,
+inconvenient and absurdly slow. GnuPG will accept any size key, but
+will not generate DSA keys over 3072 bits. I include these keys
+mainly for be-liberal-in-what-you-accept testing.
+
+There are are signatures issued by these keys in the sigs directory.
+The filenames indicate the key used to make the signature, and the
+number of bits of the hash. In the case of the 1024-bit DSA key
+(160-bit q size), there are 5 signatures using different hashes. This
+is to demonstrate hash truncation to fit in the 160-bit hash size of
+that key.
+
+File Key size Hash
+---------------------- ---------- -------
+dsa-1024-160-sign.gpg 1024 bits SHA-1
+dsa-1024-224-sign.gpg 1024 bits SHA-224 (truncated to 160 bits)
+dsa-1024-256-sign.gpg 1024 bits SHA-256 (truncated to 160 bits)
+dsa-1024-384-sign.gpg 1024 bits SHA-384 (truncated to 160 bits)
+dsa-1024-512-sign.gpg 1024 bits SHA-512 (truncated to 160 bits)
+dsa-2048-224-sign.gpg 2048 bits SHA-224
+dsa-3072-256-sign.gpg 3072 bits SHA-256
+dsa-7680-384-sign.gpg 7680 bits SHA-384
+dsa-15360-512-sign.gpg 15360 bits SHA-512
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-1024-160.pub b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-1024-160.pub
new file mode 100644
index 000000000..3fec64eb8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-1024-160.pub differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-1024-160.sec b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-1024-160.sec
new file mode 100644
index 000000000..8ee1179a8
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-1024-160.sec differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-15360-512.pub b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-15360-512.pub
new file mode 100644
index 000000000..4f931c657
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-15360-512.pub differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-15360-512.sec b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-15360-512.sec
new file mode 100644
index 000000000..3c204bff2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-15360-512.sec differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-2048-224.pub b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-2048-224.pub
new file mode 100644
index 000000000..1c8dc6984
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-2048-224.pub differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-2048-224.sec b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-2048-224.sec
new file mode 100644
index 000000000..776edfcb5
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-2048-224.sec differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-3072-256.pub b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-3072-256.pub
new file mode 100644
index 000000000..307205cf2
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-3072-256.pub differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-3072-256.sec b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-3072-256.sec
new file mode 100644
index 000000000..66008cb29
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-3072-256.sec differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-7680-384.pub b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-7680-384.pub
new file mode 100644
index 000000000..6c888a44d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-7680-384.pub differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-7680-384.sec b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-7680-384.sec
new file mode 100644
index 000000000..b57f5d9ea
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/keys/DSA-7680-384.sec differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-160-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-160-sign.gpg
new file mode 100644
index 000000000..c90658540
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-160-sign.gpg
@@ -0,0 +1 @@
+ŁÀËÌÈ$xpË=)ûO«Š'êèègŠçčT<ÚêŻ_ÂËŐáÆÂ(ÈÄÀÆÊbàâ°Ša~îșIbSZ/ŒčœêČ Őșe,óőÇÚï(ŽöpÌ=sôáÚÿïK
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-224-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-224-sign.gpg
new file mode 100644
index 000000000..8485bd335
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-224-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-256-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-256-sign.gpg
new file mode 100644
index 000000000..bebe1fdbb
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-256-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-384-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-384-sign.gpg
new file mode 100644
index 000000000..f84c3035d
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-384-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-512-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-512-sign.gpg
new file mode 100644
index 000000000..f700ce473
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-1024-512-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-15360-512-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-15360-512-sign.gpg
new file mode 100644
index 000000000..bbdb44c2a
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-15360-512-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-2048-224-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-2048-224-sign.gpg
new file mode 100644
index 000000000..d64c8176f
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-2048-224-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-3072-256-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-3072-256-sign.gpg
new file mode 100644
index 000000000..2da027129
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-3072-256-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-7680-384-sign.gpg b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-7680-384-sign.gpg
new file mode 100644
index 000000000..f313c6aec
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/dsa/sigs/dsa-7680-384-sign.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/unicode/passphrase_cyr.txt b/libraries/spongycastle/core/src/test/data/openpgp/unicode/passphrase_cyr.txt
new file mode 100644
index 000000000..702c84a18
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/openpgp/unicode/passphrase_cyr.txt
@@ -0,0 +1 @@
+йДŃŃĐŻ
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/unicode/passphrase_for_test.txt b/libraries/spongycastle/core/src/test/data/openpgp/unicode/passphrase_for_test.txt
new file mode 100644
index 000000000..157d99dad
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/openpgp/unicode/passphrase_for_test.txt
@@ -0,0 +1 @@
+Händle
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/unicode/secring.gpg b/libraries/spongycastle/core/src/test/data/openpgp/unicode/secring.gpg
new file mode 100644
index 000000000..fec9dd5b1
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/openpgp/unicode/secring.gpg differ
diff --git a/libraries/spongycastle/core/src/test/data/openpgp/unicode/test.asc b/libraries/spongycastle/core/src/test/data/openpgp/unicode/test.asc
new file mode 100644
index 000000000..73a3e7f58
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/openpgp/unicode/test.asc
@@ -0,0 +1,33 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2.0.17 (MingW32)
+
+lQO+BFBcYqwBCAC53iWjgnHOw5eo2N+OWhyz17AnEh45aRtvs/U2cIU+aCM/VXx5
+ig7GN6RcDirgnR/CTAwtdy6V/TFJ9Ej/hKu8hGsL/HCVegzs6hQo3rqXSLaAuH5i
+prdBQ0fFzCB08kbr1VkP9TyTGU6xfoEiDpk33TCbqM5Cx5+7gM5uuquTxE1SkyqV
+hd7M2p6LhvhtlHo5yx/mfPQhBCuRd/HtAXQux+UwFEeVh+1rxcKfygEeMHHkNg3F
+LJBLJW95XxTIxuScJADKhrFPjwtzVWh/chYOoK61O5rvbyRE5epHEOQYCD5X4+IN
+G22eInPaVkx9SS93Wm9UcjWEwfRY/kLDp3TjABEBAAH+AwMCSD3h6GM3cH63FXiH
+nknGYv5N7GZlI+F4m3k2+MbK/OcU2sv98Fa4b78Z5ONLH3oFwIm7NFa7fobmIHyv
+Xmcx9W06CrxpLUroqoRtEFGFrmap6yqAtnqDwtBqk6sar8QSH5HKX4xvBd1AOndk
+Htwk3cD5uN/VaIPEwgOlC+LpvQLQpMTNRpXn2NEvsj6RIEkyWxx/N7+w0B+pfeOY
+dhp8ra6kNs+1N5joMlA7tdBL9pMIiyHVfd077N2A/Fc7ONhDdIJBh9u72nTUa63H
++2jE0LzwFQQrsnz2PRvyWa4XmXVFHOg1DRuoClZ1HXZseOAYtY4u9v+62I3SjVvG
+fVALDVMjwlw1omRupsq5Mn9kuvUcpmc+fcqNJIViO/tm0mFV6Brb802oq5xkstEz
+iEF38cpJJe2WcVwABEEd6T7SZTgzakRMaQAWZ6Avb/yRzBtQ0Nq1mpn22EYHphNY
+JJtNJ3qdtIIV0TR6X034px41Kp97ZFwVPMWsR0NeM+qOQ9w3vixFt9TGdBI8rOYh
+8BSjaglz7FG8svOTfGp/Ja5nLgf3eO4hidQOQkNcRRZ9x+d/ajmZtCm6PBIfTfvH
+R9E7sMjt7CY5QAgqMK4ZwrK9BMrHlk5PLMF0/db53KTgAQcfO/skubU5ko/eWMFX
+gkPxAfCIbN8XP8DjzynxG7V80rngwtcOXLnWOfTce2fDiO1BGCnyu/S1JjRfCA3Y
+IuS5ZVpoIdssPrfXrMEKT2CP9w4R+ERsd869+bYAckaXZ6V7D6rjLYBn4LXCElmJ
+WUvevOIDRIxAUYoFuTY6jnAkQyu3/2bDwXOcGJQ3GDxMojXr8uejyeAW8NUa634C
+hJ8kuFxMXfNVhR9JnodSwe20QsFy7IUnVXergAPEVMSBhsDqFCnWuvgC8pb2dbh+
+u7QgdGVzdCB1c2VyICh0ZXN0KSA8dGVzdEB0ZXN0LmNvbT6JATgEEwECACIFAlBc
+YqwCGw8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOyHJy78uYbSqWQH/11k
+itAUrb6aUKHVyvO0r6NEbQ6TSJCstfJ6N+Euhs14od7dWgPWfkaYh9BE0j6xTrAZ
+CxP8v0Swgha7b2AVNqxf5jxAJ7xNGNY/jdzeiB9Cp5ShrFGHFGmzCYUSe2hvyBX4
+9cl9W6nKSflG+lFfcmp2wcynk/aRO0H5ieXw3eD+3SB9snAWEZzDHfUj2ifTbzPD
+80Yd2mWz9pe1xyqxgnWQkAOIWUxWpECFz8wjA9U3257gEVgfN21Ng/vaVbxa1R4Z
+2A+bLjt0jgdXw0XX69FDolko3cWuiWfJNbxsrfSCRYwFUxNVxK9rtm5padL/kZ8W
+l9icSUSiIoEfXj1iDh4=
+=2Azi
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/attachonly.eml b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/attachonly.eml
new file mode 100644
index 000000000..9ce23f790
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/attachonly.eml
@@ -0,0 +1,374 @@
+Date: Sun, 22 Feb 2009 16:27:30 +0100
+From: foo@bar.org
+User-Agent: Thunderbird 2.0.0.19 (Windows/20081209)
+MIME-Version: 1.0
+To: foo@bar.org
+Subject: testfile.doc
+X-Enigmail-Version: 0.95.7
+Content-Type: application/msword;
+ name="testfile.doc"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="testfile.doc"
+
+0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAIQAAAAAA
+AAAAEAAAIwAAAAEAAAD+////AAAAACAAAAD/////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+///////////////////////////////////spcEAOSAJBAAA8BK/AAAAAAAAEAAAAAAABAAA
+BQQAAA4AYmpiav3P/c8AAAAAAAAAAAAAAAAAAAAAAAAHBBYALgwAAJ+lAACfpQAABQAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAD//w8A
+AAAAAAAAAAAAAAAAAAAAAGwAAAAAALIAAAAAAAAAsgAAALIAAAAAAAAAsgAAAAAAAACyAAAA
+AAAAALIAAAAAAAAAsgAAABQAAAAAAAAAAAAAAMYAAAAAAAAA4AAAAAAAAADgAAAAAAAAAOAA
+AAAAAAAA4AAAAAwAAADsAAAADAAAAMYAAAAAAAAAgQEAALYAAAAEAQAAAAAAAAQBAAAAAAAA
+BAEAAAAAAAAEAQAAAAAAAAQBAAAAAAAABAEAAAAAAAAEAQAAAAAAAAQBAAAAAAAAJAEAAAIA
+AAAmAQAAAAAAACYBAAAAAAAAJgEAAAAAAAAmAQAAAAAAACYBAAAAAAAAJgEAAAAAAAA3AgAA
+IAIAAFcEAABAAAAAJgEAABUAAAAAAAAAAAAAAAAAAAAAAAAAsgAAAAAAAAAEAQAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAEAQAAAAAAAAQBAAAAAAAABAEAAAAAAAAEAQAAAAAAACYBAAAAAAAA
+JAEAAAAAAACyAAAAAAAAALIAAAAAAAAABAEAAAAAAAAAAAAAAAAAAAQBAAAAAAAAOwEAABYA
+AAAkAQAAAAAAACQBAAAAAAAAJAEAAAAAAAAEAQAAEAAAALIAAAAAAAAABAEAAAAAAACyAAAA
+AAAAAAQBAAAAAAAAJAEAAAAAAAAAAAAAAAAAACQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAEAAAAAAAAkAQAAAAAAACQBAAAAAAAA
+JAEAAAAAAAAAAAAAAAAAACQBAAAAAAAAsgAAAAAAAACyAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAEAAAAAAAAEAQAA
+AAAAAPgAAAAMAAAAMPa/DAKVyQHGAAAAGgAAAOAAAAAAAAAAFAEAAAAAAAAkAQAAAAAAAAAA
+AAAAAAAAJAEAAAAAAABRAQAAMAAAAIEBAAAAAAAAJAEAAAAAAACXBAAAAAAAABQBAAAQAAAA
+lwQAAAAAAAAkAQAAAAAAACQBAAAAAAAAxgAAAAAAAADGAAAAAAAAALIAAAAAAAAAsgAAAAAA
+AACyAAAAAAAAALIAAAAAAAAAAgDZAAAAVGVzdA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAA
+BQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAEAAAFBAAA/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAQAAAUE
+AAD+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAIBAQEsADGQaAEfsIIuILDGQSGwiQUisIkFI5CJBSSQbgQlsAAA
+F7DEAhiwxAIMkMQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAFAAPAAoAAQBpAA8AAwAAAAAAAAAAADwAAEDx/wIAPAAMAAgA
+UwB0AGEAbgBkAGEAcgBkAAAAAgAAABgAQ0oYAF9IAQRhShgAbUgHBHNIBwR0SAcEAAAAAAAA
+AAAAAAAAAAAAAAAAQgBBQPL/oQBCAAwAGQBBAGIAcwBhAHQAegAtAFMAdABhAG4AZABhAHIA
+ZABzAGMAaAByAGkAZgB0AGEAcgB0AAAAAAAAAAAAAAAAAAAAAAAFAAAABAAADAAAAAD/////
+AAAAAAcAAACaQAAAADAAAAAAAAAAgAAAAIAABAAABQQAAAMAAAAABAAABQQAAAQAAAAABAAA
+BQQAAAUAAAAAAAAABAAAAAcAAAAEAAcAAAAAAAQAAAAHAAAABQAHAP9AAhAAAAAAAAAABQAA
+AEAAAAgAQAAA//8BAAAABwBVAG4AawBuAG8AdwBuAP//AQAIAAAAAAAAAAAAAAD//wEAAAAA
+AP//AAACAP//AAAAAP//AAACAP//AAAAAAMAAABHFpABAAACAgYDBQQFAgMEh3oAIAAAAIAI
+AAAAAAAAAP8BAAAAAAAAVABpAG0AZQBzACAATgBlAHcAIABSAG8AbQBhAG4AAAA1FpABAgAF
+BQECAQcGAgUHAAAAAAAAABAAAAAAAAAAAAAAAIAAAAAAUwB5AG0AYgBvAGwAAAAzJpABAAAC
+CwYEAgICAgIEh3oAIAAAAIAIAAAAAAAAAP8BAAAAAAAAQQByAGkAYQBsAAAAIgAEADEIiBgA
+8MQCAACpAQAAAAAatNIGAAAAAAAAAAABAAAAAAAAAAAABAAAAAEAAQAAAAQAAxABAAAAAAAA
+AAAAAAABAAEAAAABAAAAAAAAAAAAAPAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAIkFiQW0ALQAgYESMAAAEAAZAGQAAAAZAAAABAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC
+AAAAAAAAAAAAAQAAAADwEAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//EgAA
+AAAAAAAAAAAAAAAAAAsASQBsAGwAdQBtAGkAbgBhAHQAaQBlAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAP7/AAAFAQIAAAAAAAAAAAAAAAAAAAAAAAEAAADghZ/y+U9oEKuRCAArJ7PZ
+MAAAACwBAAAOAAAAAQAAAHgAAAACAAAAgAAAAAMAAACMAAAABAAAAJgAAAAFAAAArAAAAAcA
+AAC4AAAACAAAAMwAAAAJAAAA2AAAABIAAADkAAAADAAAAAABAAAOAAAADAEAAA8AAAAUAQAA
+EAAAABwBAAATAAAAJAEAAAIAAADkBAAAHgAAAAEAAAAAAHMAHgAAAAEAAAAAAHMAHgAAAAwA
+AABJbGx1bWluYXRpZQAeAAAAAQAAAABsbHUeAAAACwAAAE5vcm1hbC5kb3QAAB4AAAABAAAA
+AG9ybR4AAAACAAAAMQBybR4AAAATAAAATWljcm9zb2Z0IFdvcmQgOS4wAABAAAAAAHR53QGV
+yQEDAAAAAQAAAAMAAAAAAAAAAwAAAAQAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAD+/wAABQECAAAAAAAAAAAAAAAAAAAAAAABAAAAAtXN1ZwuGxCTlwgAKyz5rjAAAADoAAAA
+DAAAAAEAAABoAAAADwAAAHAAAAAFAAAAfAAAAAYAAACEAAAAEQAAAIwAAAAXAAAAlAAAAAsA
+AACcAAAAEAAAAKQAAAATAAAArAAAABYAAAC0AAAADQAAALwAAAAMAAAAyQAAAAIAAADkBAAA
+HgAAAAIAAAAgAAAAAwAAAAEAAAADAAAAAQAAAAMAAAAEAAAAAwAAAPwKCQALAAAAAAAAAAsA
+AAAAAAAACwAAAAAAAAALAAAAAAAAAB4QAAABAAAAAQAAAAAMEAAAAgAAAB4AAAAGAAAAVGl0
+ZWwAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIA
+AAADAAAABAAAAAUAAAAGAAAABwAAAP7///8JAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAA
+/v///xEAAAASAAAAEwAAABQAAAAVAAAAFgAAABcAAAD+////GQAAABoAAAAbAAAAHAAAAB0A
+AAAeAAAAHwAAAP7////9////IgAAAP7////+/////v//////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//////////////////////////9SAG8AbwB0ACAARQBuAHQAcgB5AAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAFAf//////////AwAAAAYJAgAAAAAA
+wAAAAAAAAEYAAAAAAAAAAAAAAAAgk8kMApXJASQAAACAAAAAAAAAADEAVABhAGIAbABlAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAIA
+////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAQ
+AAAAAAAAVwBvAHIAZABEAG8AYwB1AG0AZQBuAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAABoAAgEFAAAA//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAFAFMAdQBtAG0AYQByAHkASQBuAGYAbwByAG0A
+YQB0AGkAbwBuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAACAQIAAAAEAAAA/////wAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAEAAAAAAAAAUARABvAGMA
+dQBtAGUAbgB0AFMAdQBtAG0AYQByAHkASQBuAGYAbwByAG0AYQB0AGkAbwBuAAAAAAAAAAAA
+AAA4AAIB////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+GAAAAAAQAAAAAAAAAQBDAG8AbQBwAE8AYgBqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAgEBAAAABgAAAP////8AAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAagAAAAAAAABPAGIAagBlAGMAdABQAG8AbwBsAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgABAP//////////
+/////wAAAAAAAAAAAAAAAAAAAAAAAAAAIJPJDAKVyQEgk8kMApXJAQAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAA////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAQAAAP7/////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////8BAP7/AwoAAP//
+//8GCQIAAAAAAMAAAAAAAABGGAAAAE1pY3Jvc29mdCBXb3JkLURva3VtZW50AAoAAABNU1dv
+cmREb2MAEAAAAFdvcmQuRG9jdW1lbnQuOAD0ObJxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAA==
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/basicAS2.message b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/basicAS2.message
new file mode 100644
index 000000000..b4e52c848
Binary files /dev/null and b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/basicAS2.message differ
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/brokenEnv.message b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/brokenEnv.message
new file mode 100644
index 000000000..ccf730689
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/brokenEnv.message
@@ -0,0 +1,33 @@
+Content-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIAwggP3MIID
+YKADAgECAgEHMA0GCSqGSIb3DQEBBAUAMIGoMQswCQYDVQQGEwJHQjEWMBQGA1UECBMNR3Vlcm5z
+ZXkgQy5JLjEWMBQGA1UEBxMNU3QgUGV0ZXIgUG9ydDEsMCoGA1UEChMjQ2hhbm5lbCBJc2xhbmRz
+IFN0b2NrIEV4Y2hhbmdlLCBMQkcxFjAUBgNVBAsTDUlUIERlcGFydG1lbnQxIzAhBgNVBAMTGkNJ
+U1ggQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTExMDQyODEyNDUzMloXDTIxMDQyNTEyNDUzMlow
+gaExCzAJBgNVBAYTAkdCMRYwFAYDVQQIEw1HdWVybnNleSBDLkkuMSwwKgYDVQQKEyNDaGFubmVs
+IElzbGFuZHMgU3RvY2sgRXhjaGFuZ2UsIExCRzEWMBQGA1UECxMNSVQgRGVwYXJ0bWVudDERMA8G
+A1UEAxMIY2lzeC5jb20xITAfBgkqhkiG9w0BCQEWEmNpc3hhZG1pbkBjaXN4LmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA4uONPbLSMV7EAisV+Gjcwt/M2pIycT5YJNSvJM9OKoBq2zY3
+uXKLHVAo66azMSj+SkEctcT6oIKoGa5UxJxfRU+Ofi6ncn/aLL9ktOsqGPdJHmasXQuqor/WA6vg
+UNUjphpCnwdeTteEIHbeITdZIpxJHF2cRKrD8RxppvqYDqcCAwEAAaOCATQwggEwMAkGA1UdEwQC
+MAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW
+BBRuc4NA/ZPwo7PhqJL3obdMXoacNjCB1QYDVR0jBIHNMIHKgBQiC3CXV10nqe2CmJZc9k9i+Kx9
+DaGBrqSBqzCBqDELMAkGA1UEBhMCR0IxFjAUBgNVBAgTDUd1ZXJuc2V5IEMuSS4xFjAUBgNVBAcT
+DVN0IFBldGVyIFBvcnQxLDAqBgNVBAoTI0NoYW5uZWwgSXNsYW5kcyBTdG9jayBFeGNoYW5nZSwg
+TEJHMRYwFAYDVQQLEw1JVCBEZXBhcnRtZW50MSMwIQYDVQQDExpDSVNYIENlcnRpZmljYXRlIEF1
+dGhvcml0eYIBADANBgkqhkiG9w0BAQQFAAOBgQDbA6qbz79aqrFl9jmVXFSutxUYuwweX61zRNLp
+vXWmXaGbUUcxSbQXoDJKrSr3vaDLNeqqLB/KjtpS8mestF4iQ7oT2bUjKSLeyrVDml/2sKS4oC8F
+lxYSnps6dAlph91DrayJ8c+oi7yD5uR5RUrwJaqwZVaAcCaC1whjoWt2tQAAMYIB6zCCAecCAQEw
+ga4wgagxCzAJBgNVBAYTAkdCMRYwFAYDVQQIEw1HdWVybnNleSBDLkkuMRYwFAYDVQQHEw1TdCBQ
+ZXRlciBQb3J0MSwwKgYDVQQKEyNDaGFubmVsIElzbGFuZHMgU3RvY2sgRXhjaGFuZ2UsIExCRzEW
+MBQGA1UECxMNSVQgRGVwYXJ0bWVudDEjMCEGA1UEAxMaQ0lTWCBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkCAQcwCQYFKw4DAhoFAKCBkzAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ
+BTEPFw0xMTA1MDYxMzI0MjNaMCMGCSqGSIb3DQEJBDEWBBSbT4isXzF6feE9PkJwETxWzE1MrTA0
+BgkqhkiG9w0BCQ8xJzAlMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIAgDAHBgUrDgMCBzANBgkq
+hkiG9w0BAQEFAASBgKOxZ1YVjPcCD4QEVRxjym9+8NBJrf1ZuIU1XfBmTewQDT5ZdGlfISkg5AJq
+hlkYi7yTj9nbj1lZBluaVZjOfbguiWaOXcZRxC1MxrzlCaKOEqq/6ZiZwEV1TCrbM8ooTGrItnrR
+grXnenuy14P+N/QBlfYUyJIEv5eP+xAeBYnuAAAAAAAA
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/cert.pem b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/cert.pem
new file mode 100644
index 000000000..13d908ba5
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/cert.pem
@@ -0,0 +1,73 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 9 (0x9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AT, ST=Austria, L=Vienna, O=Tiani Spirit GmbH, OU=Demo Environment, CN=Test CA/emailAddress=massimiliano.masi@tiani-spirit.com
+ Validity
+ Not Before: Oct 30 14:57:38 2012 GMT
+ Not After : Apr 5 14:57:38 2192 GMT
+ Subject: C=AT, ST=Austria, O=Tiani Spirit GmbH, OU=Test Environment, CN=massi@direct.tiani-spirit.net/emailAddress=massi@direct.tiani-spirit.net
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:c0:be:a0:14:7e:2a:5a:32:7d:b2:6c:4d:01:d2:
+ ae:92:4a:1b:26:00:9a:78:99:bd:e6:17:38:75:a2:
+ ab:25:63:0c:19:e6:87:75:cb:16:99:84:97:30:c8:
+ ca:fe:35:ec:3f:68:c5:7a:2b:22:34:ed:7b:79:c0:
+ ed:7d:66:94:ba:6e:c7:d4:f1:0c:53:76:63:cf:ec:
+ 9a:f5:bd:4d:97:19:4d:88:ab:c9:1d:6d:84:95:75:
+ ed:7f:f4:43:57:19:7c:b3:c5:2e:4e:79:38:e7:9e:
+ d4:24:ed:eb:b8:89:d4:0e:13:8c:04:c4:2d:f0:e2:
+ ee:1f:92:be:30:44:11:6d:d1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 67:2B:7D:3B:53:94:55:6F:A8:54:C9:E9:45:E0:F8:39:BA:DB:E7:76
+ X509v3 Authority Key Identifier:
+ keyid:64:C2:28:6E:51:39:46:A4:D0:F1:AE:76:45:6D:6F:38:15:78:FB:DA
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 5b:05:41:59:db:b6:7b:a8:ce:73:7f:ce:23:d5:79:5e:90:a1:
+ 65:ba:b9:69:c5:92:80:dd:4d:58:0f:68:91:57:e0:6e:71:3e:
+ 0e:c8:67:45:9d:4b:8d:bc:14:de:c4:41:76:7d:2a:c3:42:f7:
+ 3d:a0:a7:4f:32:26:d1:bb:05:84:6f:d3:f1:89:bd:1d:6e:ff:
+ d2:37:4a:e4:f3:8f:14:02:5d:71:59:cd:e0:0c:f8:20:29:45:
+ f2:d4:5e:0c:a5:71:d6:64:ea:97:1e:b6:55:ba:01:3a:1d:33:
+ 84:aa:5c:3c:c0:57:5f:6e:23:86:15:7d:92:41:5b:88:8e:a1:
+ cb:f1:03:eb:00:be:9e:f8:4d:df:7c:91:d9:a8:65:6d:d1:92:
+ f6:03:b9:22:f0:9a:5b:d9:cb:32:4f:d9:39:d9:2b:54:c9:46:
+ ae:ce:a3:98:62:82:82:23:c4:c2:ac:3d:85:b1:ed:33:52:92:
+ 02:7b:4b:75:67:2c:6f:d0:39:cc:b1:25:8b:2f:72:f2:0e:35:
+ 13:49:48:20:26:fc:98:8b:40:7e:19:4c:6b:37:39:45:d8:e5:
+ 56:55:ff:d1:58:3b:b0:f0:53:96:71:d7:6e:29:f8:29:33:e9:
+ 86:ee:34:29:b9:1a:30:6d:b9:ac:32:cf:a2:de:48:27:8b:6b:
+ 8b:e9:9c:a4
+-----BEGIN CERTIFICATE-----
+MIIDzjCCAragAwIBAgIBCTANBgkqhkiG9w0BAQUFADCBrDELMAkGA1UEBhMCQVQx
+EDAOBgNVBAgTB0F1c3RyaWExDzANBgNVBAcTBlZpZW5uYTEaMBgGA1UEChMRVGlh
+bmkgU3Bpcml0IEdtYkgxGTAXBgNVBAsTEERlbW8gRW52aXJvbm1lbnQxEDAOBgNV
+BAMTB1Rlc3QgQ0ExMTAvBgkqhkiG9w0BCQEWIm1hc3NpbWlsaWFuby5tYXNpQHRp
+YW5pLXNwaXJpdC5jb20wIBcNMTIxMDMwMTQ1NzM4WhgPMjE5MjA0MDUxNDU3Mzha
+MIGsMQswCQYDVQQGEwJBVDEQMA4GA1UECBMHQXVzdHJpYTEaMBgGA1UEChMRVGlh
+bmkgU3Bpcml0IEdtYkgxGTAXBgNVBAsTEFRlc3QgRW52aXJvbm1lbnQxJjAkBgNV
+BAMUHW1hc3NpQGRpcmVjdC50aWFuaS1zcGlyaXQubmV0MSwwKgYJKoZIhvcNAQkB
+Fh1tYXNzaUBkaXJlY3QudGlhbmktc3Bpcml0Lm5ldDCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAwL6gFH4qWjJ9smxNAdKukkobJgCaeJm95hc4daKrJWMMGeaH
+dcsWmYSXMMjK/jXsP2jFeisiNO17ecDtfWaUum7H1PEMU3Zjz+ya9b1NlxlNiKvJ
+HW2ElXXtf/RDVxl8s8UuTnk4557UJO3ruInUDhOMBMQt8OLuH5K+MEQRbdECAwEA
+AaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0
+ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFGcrfTtTlFVvqFTJ6UXg+Dm62+d2MB8G
+A1UdIwQYMBaAFGTCKG5ROUak0PGudkVtbzgVePvaMA0GCSqGSIb3DQEBBQUAA4IB
+AQBbBUFZ27Z7qM5zf84j1XlekKFlurlpxZKA3U1YD2iRV+BucT4OyGdFnUuNvBTe
+xEF2fSrDQvc9oKdPMibRuwWEb9Pxib0dbv/SN0rk848UAl1xWc3gDPggKUXy1F4M
+pXHWZOqXHrZVugE6HTOEqlw8wFdfbiOGFX2SQVuIjqHL8QPrAL6e+E3ffJHZqGVt
+0ZL2A7ki8Jpb2csyT9k52StUyUauzqOYYoKCI8TCrD2Fse0zUpICe0t1Zyxv0DnM
+sSWLL3LyDjUTSUggJvyYi0B+GUxrNzlF2OVWVf/RWDuw8FOWcdduKfgpM+mG7jQp
+uRowbbmsMs+i3kgni2uL6Zyk
+-----END CERTIFICATE-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_end1.crt b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_end1.crt
new file mode 100644
index 000000000..ffa77bc6a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_end1.crt
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAl6gAwIBAgIBBjANBgkqhkiG9w0BAQUFADBeMRwwGgYDVQQDExNDcmVh
+dGVDZXJ0UGF0aEludGVyMQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMSAw
+HgYDVQQKExdTaWduZWRNYWlsVmFsaWRhdG9yVGVzdDAeFw0wNzA1MDkxMjI1MDJa
+Fw0yNzA1MDQxMjE2MTdaMFsxGTAXBgNVBAMTEENlcnRQYXRoVGVzdEVuZDExCzAJ
+BgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAeBgNVBAoTF1NpZ25lZE1haWxW
+YWxpZGF0b3JUZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVSKmlYHT7
+B4hiai8RLLiJqunR20xwud9dg4DzY9EIq0iq7+UoY7dFfxEC33yrn7CXZmbIf7oG
+VpdB7qbX2je0Ic1dFTWA+MCilZRG9/iI368LCmh2jk+oI0LvWRmAdu9wmYt7QQbd
+hXfaXgz+6hoMesEIdlHC8Eo5HMQy+6oPrQIDAQABo4HFMIHCMAwGA1UdEwEB/wQC
+MAAwHQYDVR0OBBYEFEHktAYE3g2aYdpVV6rM+xFZ4/ytMIGFBgNVHSMEfjB8gBT+
+nhkY6yRaAlPnpWbb5oEUftM14KFhpF8wXTEbMBkGA1UEAxMSQ3JlYXRlQ2VydFBh
+dGhSb290MQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMSAwHgYDVQQKExdT
+aWduZWRNYWlsVmFsaWRhdG9yVGVzdIIBBDALBgNVHQ8EBAMCBLAwDQYJKoZIhvcN
+AQEFBQADgYEAoZgkLYp4RUW4tLqEbaP1QS9oETGMwBU/wZUzgl1Z2TsNJGe6BqNe
+VaflMyEb01j+wQ429+v05+wuc1wB5WcDFfGO8mi/g8ak4ha0m9V36QPJB5fhcPTu
+rwYPhu22Fy0CKDlAnuyEiGtzL79XSyYRkFr0Yqm/EXP+N/Fl+gQNgds=
+-----END CERTIFICATE-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_end2.crt b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_end2.crt
new file mode 100644
index 000000000..2689c9374
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_end2.crt
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAlugAwIBAgIBBDANBgkqhkiG9w0BAQUFADBeMRwwGgYDVQQDExNDcmVh
+dGVDZXJ0UGF0aEludGVyMQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMSAw
+HgYDVQQKExdTaWduZWRNYWlsVmFsaWRhdG9yVGVzdDAeFw0wNzA1MDkxMjI2Mjda
+Fw0yNzA1MDQxMjE2MTdaMFsxGTAXBgNVBAMTEENlcnRQYXRoVGVzdEVuZDIxCzAJ
+BgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAeBgNVBAoTF1NpZ25lZE1haWxW
+YWxpZGF0b3JUZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQdNwZUoe/
+12YNmNDZdtNA1CVw2hAAqLAfiGWFITigAFGVt09Ex7m/HOvhw9/ogUpC5/0k0skC
+MqhjSAJ50hypTV3zo+N6Mzq3ZFhvMHGp06T9HlbbFnGENOH3W6cDPbLnIwoIP7sI
+JNyEGlo8JP7OROamxvGvhJJuk/IUcPXFIwIDAQABo4HCMIG/MAkGA1UdEwQCMAAw
+HQYDVR0OBBYEFFfOzh3DrqqTiNSFSE5lKqylwWNKMIGFBgNVHSMEfjB8gBRPzRwP
+mvTWH+vqRDcCc45+nksyVKFhpF8wXTEbMBkGA1UEAxMSQ3JlYXRlQ2VydFBhdGhS
+b290MQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMSAwHgYDVQQKExdTaWdu
+ZWRNYWlsVmFsaWRhdG9yVGVzdIIBAzALBgNVHQ8EBAMCBLAwDQYJKoZIhvcNAQEF
+BQADgYEAk7VP0T1EfmHfUeQLbkr4KpbM+6mqtwuMZkDeoamJ35RkCsHM6rGiXpgS
+OVHayJTYgOOHoxDgNIwD+nelHdL/4EFp+Mctpubbhgp3ZZEJ1gGmc8mQ2jmp1XP6
+XY6yRAm914AOVAcceeZ3D+a8ViG808EbZHehhlirbBWxkI6FApo=
+-----END CERTIFICATE-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_inter1.crt b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_inter1.crt
new file mode 100644
index 000000000..a17b2fc37
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_inter1.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAmagAwIBAgIBBDANBgkqhkiG9w0BAQUFADBdMRswGQYDVQQDExJDcmVh
+dGVDZXJ0UGF0aFJvb3QxCzAJBgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAe
+BgNVBAoTF1NpZ25lZE1haWxWYWxpZGF0b3JUZXN0MB4XDTA3MDUwOTEyMjEyNloX
+DTI3MDUwNDEyMTYxN1owXjEcMBoGA1UEAxMTQ3JlYXRlQ2VydFBhdGhJbnRlcjEL
+MAkGA1UEBhMCQ0gxDzANBgNVBAcTBlp1cmljaDEgMB4GA1UEChMXU2lnbmVkTWFp
+bFZhbGlkYXRvclRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOezbRC1
+7BAAdFckLevyWLGj+3kDQ1DRRBaxGf7Ym40v1OZtUc4FhnFfGTHh5D0XMfvM3Wwq
+To5xi/iho7vI16s1ZAfnWN5pXubb9ZqNU7uYQdjmk50glTa2U28Hiskr1pxHis9F
+8cyz5cTH2RG00LfLWMfGVHsrFIN3naeCupbdAgMBAAGjgcswgcgwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQU/p4ZGOskWgJT56Vm2+aBFH7TNeAwgYUGA1UdIwR+
+MHyAFCQH6UCakD8+qWKy8yY61DMSABfdoWGkXzBdMRswGQYDVQQDExJDcmVhdGVD
+ZXJ0UGF0aFJvb3QxCzAJBgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAeBgNV
+BAoTF1NpZ25lZE1haWxWYWxpZGF0b3JUZXN0ggEBMA4GA1UdDwEB/wQEAwIBBjAN
+BgkqhkiG9w0BAQUFAAOBgQDBeipc+tTpOau+SOihZcCDFjZG3W8Q4iewmCzf3UHd
+7tK85wVUts0BeAgDzeqD8b+oV+Q4HIv1RkSt+4foPgRu6ujljMkS9cQTYFkaogGp
+qwM4BeVFInXdjV4MfzCVJkIA2RZ56dKGGuZrMAtfHQl/9M6jlYs9uYHnj0BAVc+0
+8Q==
+-----END CERTIFICATE-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_inter2.crt b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_inter2.crt
new file mode 100644
index 000000000..3ed78e1bf
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_inter2.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAmagAwIBAgIBAzANBgkqhkiG9w0BAQUFADBdMRswGQYDVQQDExJDcmVh
+dGVDZXJ0UGF0aFJvb3QxCzAJBgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAe
+BgNVBAoTF1NpZ25lZE1haWxWYWxpZGF0b3JUZXN0MB4XDTA3MDUwOTEyMTkzOVoX
+DTI3MDUwNDEyMTYxN1owXjEcMBoGA1UEAxMTQ3JlYXRlQ2VydFBhdGhJbnRlcjEL
+MAkGA1UEBhMCQ0gxDzANBgNVBAcTBlp1cmljaDEgMB4GA1UEChMXU2lnbmVkTWFp
+bFZhbGlkYXRvclRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJnJs3kA
+HttsQSJA9gxe0og85sX469Vq0xY9gfEBrp0EGUH50HSLVNHRhZnXlibcya7/XWwV
+pESg+HTc/XhOQtwGn68eJ/K1ka7JqeFnN3VzwduqNignnc6WEXndrZIPLP+7a7VZ
+zWoWxa+C2mwjFHHza3WZmP7d6fbu+DFsXsA1AgMBAAGjgcswgcgwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUT80cD5r01h/r6kQ3AnOOfp5LMlQwgYUGA1UdIwR+
+MHyAFCQH6UCakD8+qWKy8yY61DMSABfdoWGkXzBdMRswGQYDVQQDExJDcmVhdGVD
+ZXJ0UGF0aFJvb3QxCzAJBgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAeBgNV
+BAoTF1NpZ25lZE1haWxWYWxpZGF0b3JUZXN0ggEBMA4GA1UdDwEB/wQEAwIBBjAN
+BgkqhkiG9w0BAQUFAAOBgQAZBu5xl7ZhMJkMmwXH3Tm8XCqllzXXW0GyZ8jfuMLT
+msnmC9aPnz6NjH6FVHnM9SA+twsx1gaTfbq6zKSTN03lV31ltG1qa5SUuOvOggdR
+qM15JwzRaGgGGuIc3pswid5tUpKv0koVMNmvIrL3G3pvfOggYjhLAhOuf9WC8AX6
+4w==
+-----END CERTIFICATE-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_root.crt b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_root.crt
new file mode 100644
index 000000000..7b70b1a52
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/certpath_root.crt
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC/DCCAmWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBdMRswGQYDVQQDExJDcmVh
+dGVDZXJ0UGF0aFJvb3QxCzAJBgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxIDAe
+BgNVBAoTF1NpZ25lZE1haWxWYWxpZGF0b3JUZXN0MB4XDTA3MDUwOTEyMTYxN1oX
+DTI3MDUwNDEyMTYxN1owXTEbMBkGA1UEAxMSQ3JlYXRlQ2VydFBhdGhSb290MQsw
+CQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMSAwHgYDVQQKExdTaWduZWRNYWls
+VmFsaWRhdG9yVGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxjkirNds
+Zlw3k5xwPixlMYJmvlbh+Sj7W3NRrDFcvOLQ40NxvNbcv5V+gHfvBwlqegYHJQGj
+iKYOF6tr7o1oAkKTdBfwZ2bjLs4bwmNjhUbHJQhR6aWE1yIogInHcj3vA3KcmSID
+Gr8bo7jYPxuut+xbPoTNEcmDmUsXIhzVk+kCAwEAAaOByzCByDAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBQkB+lAmpA/PqlisvMmOtQzEgAX3TCBhQYDVR0jBH4w
+fIAUJAfpQJqQPz6pYrLzJjrUMxIAF92hYaRfMF0xGzAZBgNVBAMTEkNyZWF0ZUNl
+cnRQYXRoUm9vdDELMAkGA1UEBhMCQ0gxDzANBgNVBAcTBlp1cmljaDEgMB4GA1UE
+ChMXU2lnbmVkTWFpbFZhbGlkYXRvclRlc3SCAQEwDgYDVR0PAQH/BAQDAgEGMA0G
+CSqGSIb3DQEBBQUAA4GBAA2aYvz6B+SjgAdFnF3IBVISKOR++QAu3zv6+r+4FBhm
+6sWwxlS6gX60nH0D7j/0DxAihS9IaSJhreWf2GCJp5JNj9gEEaLJkx39SaO3Ibds
+TY7A6QwA3DUIphISxhNz1ExHQISBSgfVFlx/9EzCOpCFpJbFyGn5WeyZJpKQTpqX
+-----END CERTIFICATE-----
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/circular.eml b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/circular.eml
new file mode 100644
index 000000000..4820fbe15
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/circular.eml
@@ -0,0 +1,109 @@
+Date: Tue, 03 Apr 2007 09:02:23 +0200
+From: =?ISO-8859-1?Q?Armin_H=E4berling?=
+
+
+
+
+
+Datei
+
+fil=
+eKomprimierung
+
+com=
+pressionVerschl=FCsselung
+
+enc=
+ryptionSignaturdatei/Kommentar
+sig=
+naturefile/comment
+
+
+CONTRL__9904144000002_9900496000005_20080409_141316.txt
+keine/none
+keine/none
+
+
+
+--
autogenerated email by AKTIF-EDIService (aedic.sm).
+Please do not respond to this emailaddress.
+
+
+
+--------alternative_boundary--
+--------------=_NextPart_000_000_000
+Content-Type: application/octet-stream;
+ name="CONTRL__9904144000002_9900496000005_20080409_141316.txt"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment;
+ filename="CONTRL__9904144000002_9900496000005_20080409_141316.txt"
+
+UNA:+.? 'UNB+UNOC:3+9904144000002:500+9900496000005:500+080409:1024+141316'=
+UNH+129534+CONTRL:D:3:UN:1.3'UCI+90410000031762+9900496000005:500+990414400=
+0002:500+8'UNT+3+129534'UNZ+1+141316'
+--------------=_NextPart_000_000_000--
+
+------F439FEDC23FA084281EC7880F017B061
+Content-Type: application/x-pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+
+MIIHSwYJKoZIhvcNAQcCoIIHPDCCBzgCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
+DQEHAaCCBSswggUnMIIED6ADAgECAg59rQABAALWS8Z6CaQRPzANBgkqhkiG9w0B
+AQUFADB7MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
+SDElMCMGA1UECxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBMMSBDQTEnMCUGA1UE
+AxMeVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBMMSBDQSBWMB4XDTA4MDIwNDE0NDY1
+MFoXDTExMDIwNDE0NDY1MFowgcoxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCYXll
+cm4xHDAaBgNVBAcTE05ldWJ1cmcgYS4gZC4gRG9uYXUxKDAmBgNVBAoTH01hc2No
+aW5lbnJpbmdlIERldXRzY2hsYW5kIEdtYkgxFDASBgNVBAsTC0xhbmRlbmVyZ2ll
+MSUwIwYDVQQDExxMYW5kZW5lcmdpZSBUZWFtIENlcnRpZmljYXRlMSUwIwYJKoZI
+hvcNAQkBFhZFRElGQUNUQGxhbmRlbmVyZ2llLmRlMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDIx6ipt3UiRoRiqxFNMdPGxfmV/8J/tTvdsYUbJiHPTeSXTjTN
+aqBJuGRLT+Lctq+DhLN+Y3ZRv2H/xelSFrjxM670e2jL4WjbnMZ/5LkptruZ+8qZ
+1fGNrqBRVDr8QPXCC9bf+V+0AP9FkzYbuf/BRrPt9B2rvdwnieVpze1CXQIDAQAB
+o4IB2zCCAdcwgZUGCCsGAQUFBwEBBIGIMIGFMFAGCCsGAQUFBzAChkRodHRwOi8v
+d3d3LnRydXN0Y2VudGVyLmRlL2NlcnRzZXJ2aWNlcy9jYWNlcnRzL3RjX2NsYXNz
+M19MMV9DQV9WLmNydDAxBggrBgEFBQcwAYYlaHR0cDovL29jc3AuVi50Y2NsYXNz
+My50cnVzdGNlbnRlci5kZTAfBgNVHSMEGDAWgBTrZQCH1qACAaoa11MeclBuaJwb
+/jAMBgNVHRMBAf8EAjAAMEoGA1UdIARDMEEwPwYJKoIUACwBAQEDMDIwMAYIKwYB
+BQUHAgEWJGh0dHA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvZ3VpZGVsaW5lczAOBgNV
+HQ8BAf8EBAMCBeAwHQYDVR0OBBYEFOXs++f6hPt293ZHilOZciZ8lPy1MEcGA1Ud
+HwRAMD4wPKA6oDiGNmh0dHA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvY3JsL3YyL3Rj
+X2NsYXNzM19MMV9DQV9WLmNybDAnBgNVHSUEIDAeBggrBgEFBQcDAgYIKwYBBQUH
+AwQGCCsGAQUFBwMHMCEGA1UdEQQaMBiBFkVESUZBQ1RAbGFuZGVuZXJnaWUuZGUw
+DQYJKoZIhvcNAQEFBQADggEBAORAxacE5bh+9uDyf97ctXXuOOTchhyffARgMueo
+kNIPjwcM+czYLsUc6nkvkbvKJqU1cVt2jbcM/JL6W4nllAEtONyiSiFrJQWp4dQ8
+paOAjerqXrkiPvARpfYTsHSuUFrFvzIk8lOyqGeur+tHhOG7PQOAdnlFKkZofGqt
+Wrhu605UsOBICgpkyjXQFEAK5rS7t7r8ddlfu+ijj9le1uzSFlnpljmARX2gfRR5
+/E6J101HgNWX7VyS6XxCgod/AzXk+Z5WQwz0PTkpN1ccCXgN0LQzRCW+n4S8XqvQ
+y5P5AqUeWSbsD2YQffnsC+l1fwk/XrJhMkILal84qgluC3cxggHoMIIB5AIBATCB
+jTB7MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21iSDEl
+MCMGA1UECxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBMMSBDQTEnMCUGA1UEAxMe
+VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBMMSBDQSBWAg59rQABAALWS8Z6CaQRPzAJ
+BgUrDgMCGgUAoIGxMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcN
+AQkFMQ8XDTA4MDQwOTA4MjgyOFowIwYJKoZIhvcNAQkEMRYEFM8H2qjq/duh/IHZ
+nha4pDA3Qi3KMFIGCSqGSIb3DQEJDzFFMEMwCgYIKoZIhvcNAwcwDgYIKoZIhvcN
+AwICAgCAMA0GCCqGSIb3DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0G
+CSqGSIb3DQEBAQUABIGANd6GNxqQcegeoN6+CTV5SyfE7aD8k00uiUV6zPFlwM3p
+2bbIPthbCsAHyRx/sUngRlP2XfMuPR+vc8/ACbhGWdKQqhQDFHQgbZALiLhyHJrp
+kMNr5ZWzKce1iM15GhsKUViiuTBhhK2LQolwB9gnKkBMwbF9bLPuMXNvallydtM=
+
+------F439FEDC23FA084281EC7880F017B061--
+
diff --git a/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/outlook_2010_beta_sime_msg.eml b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/outlook_2010_beta_sime_msg.eml
new file mode 100644
index 000000000..3de619b7c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/data/org/spongycastle/mail/smime/test/outlook_2010_beta_sime_msg.eml
@@ -0,0 +1,25 @@
+To:
+ * The vectors are Base 64 encoded and encrypted using the password "password"
+ * (without quotes). They should all yield the same PrivateKeyInfo object.
+ */
+public class EncryptedPrivateKeyInfoTest
+ extends SimpleTest
+{
+ static byte[] sample1 = Base64.decode(
+ "MIIBozA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIfWBDXwLp4K4CAggA"
+ + "MBEGBSsOAwIHBAiaCF/AvOgQ6QSCAWDWX4BdAzCRNSQSANSuNsT5X8mWYO27mr3Y"
+ + "9c9LoBVXGNmYWKA77MI4967f7SmjNcgXj3xNE/jmnVz6hhsjS8E5VPT3kfyVkpdZ"
+ + "0lr5e9Yk2m3JWpPU7++v5zBkZmC4V/MwV/XuIs6U+vykgzMgpxQg0oZKS9zgmiZo"
+ + "f/4dOCL0UtCDnyOSvqT7mCVIcMDIEKu8QbVlgZYBop08l60EuEU3gARUo8WsYQmO"
+ + "Dz/ldx0Z+znIT0SXVuOwc+RVItC5T/Qx+aijmmpt+9l14nmaGBrEkmuhmtdvU/4v"
+ + "aptewGRgmjOfD6cqK+zs0O5NrrJ3P/6ZSxXj91CQgrThGfOv72bUncXEMNtc8pks"
+ + "2jpHFjGMdKufnadAD7XuMgzkkaklEXZ4f5tU6heIIwr51g0GBEGF96gYPFnjnSQM"
+ + "75JE02Clo+DfcfXpcybPTwwFg2jd6JTTOfkdf6OdSlA/1XNK43FA");
+
+ static byte[] sample2 = Base64.decode(
+ "MIIBpjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIeFeOWl1jywYCAggA"
+ + "MBQGCCqGSIb3DQMHBAjUJ5eGBhQGtQSCAWBrHrRgqO8UUMLcWzZEtpk1l3mjxiF/"
+ + "koCMkHsFwowgyWhEbgIkTgbSViK54LVK8PskekcGNLph+rB6bGZ7pPbL5pbXASJ8"
+ + "+MkQcG3FZdlS4Ek9tTJDApj3O1UubZGFG4uvTlJJFbF1BOJ3MkY3XQ9Gl1qwv7j5"
+ + "6e103Da7Cq9+oIDKmznza78XXQYrUsPo8mJGjUxPskEYlzwvHjKubRnYm/K6RKhi"
+ + "5f4zX4BQ/Dt3H812ZjRXrsjAJP0KrD/jyD/jCT7zNBVPH1izBds+RwizyQAHwfNJ"
+ + "BFR78TH4cgzB619X47FDVOnT0LqQNVd0O3cSwnPrXE9XR3tPayE+iOB15llFSmi8"
+ + "z0ByOXldEpkezCn92Umk++suzIVj1qfsK+bv2phZWJPbLEIWPDRHUbYf76q5ArAr"
+ + "u4xtxT/hoK3krEs/IN3d70qjlUJ36SEw1UaZ82PWhakQbdtu39ZraMJB");
+
+ static byte[] sample3 = Base64.decode(
+ "MIIBrjBIBgkqhkiG9w0BBQ0wOzAeBgkqhkiG9w0BBQwwEQQIrHyQPBZqWLUCAggA"
+ + "AgEQMBkGCCqGSIb3DQMCMA0CAToECEhbh7YZKiPSBIIBYCT1zp6o5jpFlIkgwPop"
+ + "7bW1+8ACr4exqzkeb3WflQ8cWJ4cURxzVdvxUnXeW1VJdaQZtjS/QHs5GhPTG/0f"
+ + "wtvnaPfwrIJ3FeGaZfcg2CrYhalOFmEb4xrE4KyoEQmUN8tb/Cg94uzd16BOPw21"
+ + "RDnE8bnPdIGY7TyL95kbkqH23mK53pi7h+xWIgduW+atIqDyyt55f7WMZcvDvlj6"
+ + "VpN/V0h+qxBHL274WA4dj6GYgeyUFpi60HdGCK7By2TBy8h1ZvKGjmB9h8jZvkx1"
+ + "MkbRumXxyFsowTZawyYvO8Um6lbfEDP9zIEUq0IV8RqH2MRyblsPNSikyYhxX/cz"
+ + "tdDxRKhilySbSBg5Kr8OfcwKp9bpinN96nmG4xr3Tch1bnVvqJzOQ5+Vva2WwVvH"
+ + "2JkWvYm5WaANg4Q6bRxu9vz7DuhbJjQdZbxFezIAgrJdSe92B00jO/0Kny1WjiVO"
+ + "6DA=");
+
+ public String getName()
+ {
+ return "EncryptedPrivateKeyInfoTest";
+ }
+
+ private void test(
+ int id,
+ byte[] sample)
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(sample);
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+ EncryptedPrivateKeyInfo info = null;
+
+ try
+ {
+ info = EncryptedPrivateKeyInfo.getInstance(aIn.readObject());
+ }
+ catch (Exception e)
+ {
+ fail("test " + id + " failed construction - exception " + e.toString(), e);
+ }
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ try
+ {
+ dOut.writeObject(info);
+ }
+ catch (Exception e)
+ {
+ fail("test " + id + " failed writing - exception " + e.toString(), e);
+ }
+
+ byte[] bytes = bOut.toByteArray();
+
+ if (bytes.length != sample.length)
+ {
+ try
+ {
+ bIn = new ByteArrayInputStream(bytes);
+ aIn = new ASN1InputStream(bIn);
+
+ ASN1Primitive obj = aIn.readObject();
+
+ fail("test " + id + " length mismatch - expected " + sample.length + System.getProperty("line.separator") + ASN1Dump.dumpAsString(info) + " got " + bytes.length + System.getProperty("line.separator") + ASN1Dump.dumpAsString(obj));
+ }
+ catch (Exception e)
+ {
+ fail("test " + id + " length mismatch - exception " + e.toString());
+ }
+ }
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ if (bytes[i] != sample[i])
+ {
+ fail("test " + id + " data mismatch");
+ }
+ }
+ }
+
+ public void performTest()
+ {
+ test(0, sample1);
+ test(1, sample2);
+ test(2, sample3);
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new EncryptedPrivateKeyInfoTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/EnumeratedTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/EnumeratedTest.java
new file mode 100644
index 000000000..90391ef59
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/EnumeratedTest.java
@@ -0,0 +1,115 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.spongycastle.asn1.ASN1Boolean;
+import org.spongycastle.asn1.ASN1Enumerated;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.util.encoders.Hex;
+
+/**
+ * Tests used to verify correct decoding of the ENUMERATED type.
+ */
+public class EnumeratedTest
+ extends TestCase
+{
+ /**
+ * Test vector used to test decoding of multiple items. This sample uses an ENUMERATED and a BOOLEAN.
+ */
+ private static final byte[] MultipleSingleByteItems = Hex.decode("30060a01010101ff");
+
+ /**
+ * Test vector used to test decoding of multiple items. This sample uses two ENUMERATEDs.
+ */
+ private static final byte[] MultipleDoubleByteItems = Hex.decode("30080a0201010a020202");
+
+ /**
+ * Test vector used to test decoding of multiple items. This sample uses an ENUMERATED and an OBJECT IDENTIFIER.
+ */
+ private static final byte[] MultipleTripleByteItems = Hex.decode("300a0a0301010106032b0601");
+
+ /**
+ * Makes sure multiple identically sized values are parsed correctly.
+ */
+ public void testReadingMultipleSingleByteItems()
+ throws IOException
+ {
+ ASN1Primitive obj = ASN1Primitive.fromByteArray(MultipleSingleByteItems);
+
+ assertTrue("Null ASN.1 SEQUENCE", obj instanceof ASN1Sequence);
+
+ ASN1Sequence sequence = (ASN1Sequence)obj;
+
+ assertEquals("2 items expected", 2, sequence.size());
+
+ ASN1Enumerated enumerated = ASN1Enumerated.getInstance(sequence.getObjectAt(0));
+
+ assertNotNull("ENUMERATED expected", enumerated);
+
+ assertEquals("Unexpected ENUMERATED value", 1, enumerated.getValue().intValue());
+
+ ASN1Boolean b = ASN1Boolean.getInstance(sequence.getObjectAt(1));
+
+ assertNotNull("BOOLEAN expected", b);
+
+ assertTrue("Unexpected BOOLEAN value", b.isTrue());
+ }
+
+ /**
+ * Makes sure multiple identically sized values are parsed correctly.
+ */
+ public void testReadingMultipleDoubleByteItems()
+ throws IOException
+ {
+ ASN1Primitive obj = ASN1Primitive.fromByteArray(MultipleDoubleByteItems);
+
+ assertTrue("Null ASN.1 SEQUENCE", obj instanceof ASN1Sequence);
+
+ ASN1Sequence sequence = (ASN1Sequence)obj;
+
+ assertEquals("2 items expected", 2, sequence.size());
+
+ ASN1Enumerated enumerated1 = ASN1Enumerated.getInstance(sequence.getObjectAt(0));
+
+ assertNotNull("ENUMERATED expected", enumerated1);
+
+ assertEquals("Unexpected ENUMERATED value", 257, enumerated1.getValue().intValue());
+
+ ASN1Enumerated enumerated2 = ASN1Enumerated.getInstance(sequence.getObjectAt(1));
+
+ assertNotNull("ENUMERATED expected", enumerated2);
+
+ assertEquals("Unexpected ENUMERATED value", 514, enumerated2.getValue().intValue());
+ }
+
+ /**
+ * Makes sure multiple identically sized values are parsed correctly.
+ */
+ public void testReadingMultipleTripleByteItems()
+ throws IOException
+ {
+ ASN1Primitive obj = ASN1Primitive.fromByteArray(MultipleTripleByteItems);
+
+ assertTrue("Null ASN.1 SEQUENCE", obj instanceof ASN1Sequence);
+
+ ASN1Sequence sequence = (ASN1Sequence)obj;
+
+ assertEquals("2 items expected", 2, sequence.size());
+
+ ASN1Enumerated enumerated = ASN1Enumerated.getInstance(sequence.getObjectAt(0));
+
+ assertNotNull("ENUMERATED expected", enumerated);
+
+ assertEquals("Unexpected ENUMERATED value", 65793, enumerated.getValue().intValue());
+
+ ASN1ObjectIdentifier objectId = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(1));
+
+ assertNotNull("OBJECT IDENTIFIER expected", objectId);
+
+ assertEquals("Unexpected OBJECT IDENTIFIER value", "1.3.6.1", objectId.getId());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/EqualsAndHashCodeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/EqualsAndHashCodeTest.java
new file mode 100644
index 000000000..be901caa0
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/EqualsAndHashCodeTest.java
@@ -0,0 +1,127 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Date;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.BERConstructedOctetString;
+import org.spongycastle.asn1.BERSequence;
+import org.spongycastle.asn1.BERSet;
+import org.spongycastle.asn1.BERTaggedObject;
+import org.spongycastle.asn1.DERApplicationSpecific;
+import org.spongycastle.asn1.DERBMPString;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERBoolean;
+import org.spongycastle.asn1.DEREnumerated;
+import org.spongycastle.asn1.DERGeneralString;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERInteger;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DERNumericString;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERT61String;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.DERUTCTime;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.DERUniversalString;
+import org.spongycastle.asn1.DERVisibleString;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class EqualsAndHashCodeTest
+ implements Test
+{
+ public TestResult perform()
+ {
+ byte[] data = { 0, 1, 0, 1, 0, 0, 1 };
+
+ ASN1Primitive values[] = {
+ new BERConstructedOctetString(data),
+ new BERSequence(new DERPrintableString("hello world")),
+ new BERSet(new DERPrintableString("hello world")),
+ new BERTaggedObject(0, new DERPrintableString("hello world")),
+ new DERApplicationSpecific(0, data),
+ new DERBitString(data),
+ new DERBMPString("hello world"),
+ new DERBoolean(true),
+ new DERBoolean(false),
+ new DEREnumerated(100),
+ new DERGeneralizedTime("20070315173729Z"),
+ new DERGeneralString("hello world"),
+ new DERIA5String("hello"),
+ new DERInteger(1000),
+ new DERNull(),
+ new DERNumericString("123456"),
+ new DERObjectIdentifier("1.1.1.10000.1"),
+ new DEROctetString(data),
+ new DERPrintableString("hello world"),
+ new DERSequence(new DERPrintableString("hello world")),
+ new DERSet(new DERPrintableString("hello world")),
+ new DERT61String("hello world"),
+ new DERTaggedObject(0, new DERPrintableString("hello world")),
+ new DERUniversalString(data),
+ new DERUTCTime(new Date()),
+ new DERUTF8String("hello world"),
+ new DERVisibleString("hello world")
+ };
+
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ for (int i = 0; i != values.length; i++)
+ {
+ aOut.writeObject(values[i]);
+ }
+
+ ASN1Primitive[] readValues = new ASN1Primitive[values.length];
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+
+ for (int i = 0; i != values.length; i++)
+ {
+ ASN1Primitive o = aIn.readObject();
+ if (!o.equals(values[i]))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed equality test for " + o.getClass());
+ }
+
+ if (o.hashCode() != values[i].hashCode())
+ {
+ return new SimpleTestResult(false, getName() + ": Failed hashCode test for " + o.getClass());
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": Failed - exception " + e.toString(), e);
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "EqualsAndHashCode";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ EqualsAndHashCodeTest test = new EqualsAndHashCodeTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GeneralNameTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GeneralNameTest.java
new file mode 100644
index 000000000..dcc2e8b71
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GeneralNameTest.java
@@ -0,0 +1,143 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class GeneralNameTest
+ extends SimpleTest
+{
+ private static final byte[] ipv4 = Hex.decode("87040a090800");
+ private static final byte[] ipv4WithMask1 = Hex.decode("87080a090800ffffff00");
+ private static final byte[] ipv4WithMask2 = Hex.decode("87080a090800ffff8000");
+ private static final byte[] ipv4WithMask3 = Hex.decode("87080a090800ffffc000");
+
+ private static final byte[] ipv6a = Hex.decode("871020010db885a308d313198a2e03707334");
+ private static final byte[] ipv6b = Hex.decode("871020010db885a3000013198a2e03707334");
+ private static final byte[] ipv6c = Hex.decode("871000000000000000000000000000000001");
+ private static final byte[] ipv6d = Hex.decode("871020010db885a3000000008a2e03707334");
+ private static final byte[] ipv6e = Hex.decode("871020010db885a3000000008a2e0a090800");
+ private static final byte[] ipv6f = Hex.decode("872020010db885a3000000008a2e0a090800ffffffffffff00000000000000000000");
+ private static final byte[] ipv6g = Hex.decode("872020010db885a3000000008a2e0a090800ffffffffffffffffffffffffffffffff");
+ private static final byte[] ipv6h = Hex.decode("872020010db885a300000000000000000000ffffffffffff00000000000000000000");
+ private static final byte[] ipv6i = Hex.decode("872020010db885a300000000000000000000fffffffffffe00000000000000000000");
+ private static final byte[] ipv6j = Hex.decode("872020010db885a300000000000000000000ffffffffffff80000000000000000000");
+
+ public String getName()
+ {
+ return "GeneralName";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ GeneralName nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4))
+ {
+ fail("ipv4 encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0/255.255.255.0");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4WithMask1))
+ {
+ fail("ipv4 with netmask 1 encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0/24");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4WithMask1))
+ {
+ fail("ipv4 with netmask 2 encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0/255.255.128.0");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4WithMask2))
+ {
+ fail("ipv4 with netmask 3a encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0/17");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4WithMask2))
+ {
+ fail("ipv4 with netmask 3b encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0/255.255.192.0");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4WithMask3))
+ {
+ fail("ipv4 with netmask 3a encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "10.9.8.0/18");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv4WithMask3))
+ {
+ fail("ipv4 with netmask 3b encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3:08d3:1319:8a2e:0370:7334");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6a))
+ {
+ fail("ipv6 with netmask encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::1319:8a2e:0370:7334");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6b))
+ {
+ fail("ipv6b encoding failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "::1");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6c))
+ {
+ fail("ipv6c failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::8a2e:0370:7334");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6d))
+ {
+ fail("ipv6d failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::8a2e:10.9.8.0");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6e))
+ {
+ fail("ipv6e failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::8a2e:10.9.8.0/ffff:ffff:ffff::0000");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6f))
+ {
+ fail("ipv6f failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::8a2e:10.9.8.0/128");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6g))
+ {
+ fail("ipv6g failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::/48");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6h))
+ {
+ fail("ipv6h failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::/47");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6i))
+ {
+ fail("ipv6i failed");
+ }
+
+ nm = new GeneralName(GeneralName.iPAddress, "2001:0db8:85a3::/49");
+ if (!Arrays.areEqual(nm.getEncoded(), ipv6j))
+ {
+ fail("ipv6j failed");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GeneralNameTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GeneralizedTimeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GeneralizedTimeTest.java
new file mode 100644
index 000000000..3dce5b3ca
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GeneralizedTimeTest.java
@@ -0,0 +1,201 @@
+package org.spongycastle.asn1.test;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * X.690 test example
+ */
+public class GeneralizedTimeTest
+ extends SimpleTest
+{
+ String[] input =
+ {
+ "20020122122220",
+ "20020122122220Z",
+ "20020122122220-1000",
+ "20020122122220+00",
+ "20020122122220.1",
+ "20020122122220.1Z",
+ "20020122122220.1-1000",
+ "20020122122220.1+00",
+ "20020122122220.01",
+ "20020122122220.01Z",
+ "20020122122220.01-1000",
+ "20020122122220.01+00",
+ "20020122122220.001",
+ "20020122122220.001Z",
+ "20020122122220.001-1000",
+ "20020122122220.001+00",
+ "20020122122220.0001",
+ "20020122122220.0001Z",
+ "20020122122220.0001-1000",
+ "20020122122220.0001+00",
+ "20020122122220.0001+1000"
+ };
+
+ String[] output = {
+ "20020122122220",
+ "20020122122220GMT+00:00",
+ "20020122122220GMT-10:00",
+ "20020122122220GMT+00:00",
+ "20020122122220.1",
+ "20020122122220.1GMT+00:00",
+ "20020122122220.1GMT-10:00",
+ "20020122122220.1GMT+00:00",
+ "20020122122220.01",
+ "20020122122220.01GMT+00:00",
+ "20020122122220.01GMT-10:00",
+ "20020122122220.01GMT+00:00",
+ "20020122122220.001",
+ "20020122122220.001GMT+00:00",
+ "20020122122220.001GMT-10:00",
+ "20020122122220.001GMT+00:00",
+ "20020122122220.0001",
+ "20020122122220.0001GMT+00:00",
+ "20020122122220.0001GMT-10:00",
+ "20020122122220.0001GMT+00:00",
+ "20020122122220.0001GMT+10:00" };
+
+ String[] zOutput = {
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122122220Z",
+ "20020122022220Z"
+ };
+
+ String[] mzOutput = {
+ "20020122122220.000Z",
+ "20020122122220.000Z",
+ "20020122222220.000Z",
+ "20020122122220.000Z",
+ "20020122122220.100Z",
+ "20020122122220.100Z",
+ "20020122222220.100Z",
+ "20020122122220.100Z",
+ "20020122122220.010Z",
+ "20020122122220.010Z",
+ "20020122222220.010Z",
+ "20020122122220.010Z",
+ "20020122122220.001Z",
+ "20020122122220.001Z",
+ "20020122222220.001Z",
+ "20020122122220.001Z",
+ "20020122122220.000Z",
+ "20020122122220.000Z",
+ "20020122222220.000Z",
+ "20020122122220.000Z",
+ "20020122022220.000Z"
+ };
+
+ public String getName()
+ {
+ return "GeneralizedTime";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ for (int i = 0; i != input.length; i++)
+ {
+ DERGeneralizedTime t = new DERGeneralizedTime(input[i]);
+
+ if (output[i].indexOf('G') > 0) // don't check local time the same way
+ {
+ if (!t.getTime().equals(output[i]))
+ {
+ fail("failed conversion test");
+ }
+ if (!dateF.format(t.getDate()).equals(zOutput[i]))
+ {
+ fail("failed date conversion test");
+ }
+ }
+ else
+ {
+ String offset = calculateGMTOffset(t.getDate());
+ if (!t.getTime().equals(output[i] + offset))
+ {
+ fail("failed conversion test");
+ }
+ }
+ }
+
+ dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ for (int i = 0; i != input.length; i++)
+ {
+ DERGeneralizedTime t = new DERGeneralizedTime(input[i]);
+
+ if (!dateF.format(t.getDate()).equals(mzOutput[i]))
+ {
+ fail("failed long date conversion test");
+ }
+ }
+ }
+
+ private String calculateGMTOffset(Date date)
+ {
+ String sign = "+";
+ TimeZone timeZone = TimeZone.getDefault();
+ int offset = timeZone.getRawOffset();
+ if (offset < 0)
+ {
+ sign = "-";
+ offset = -offset;
+ }
+ int hours = offset / (60 * 60 * 1000);
+ int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000);
+
+ if (timeZone.useDaylightTime() && timeZone.inDaylightTime(date))
+ {
+ hours += sign.equals("+") ? 1 : -1;
+ }
+
+ return "GMT" + sign + convert(hours) + ":" + convert(minutes);
+ }
+
+ private String convert(int time)
+ {
+ if (time < 10)
+ {
+ return "0" + time;
+ }
+
+ return Integer.toString(time);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GeneralizedTimeTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GenerationTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GenerationTest.java
new file mode 100644
index 000000000..6febc0583
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GenerationTest.java
@@ -0,0 +1,428 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.oiw.ElGamalParameter;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.CRLReason;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.IssuingDistributionPoint;
+import org.spongycastle.asn1.x509.KeyUsage;
+import org.spongycastle.asn1.x509.RSAPublicKeyStructure;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.asn1.x509.TBSCertificate;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.asn1.x509.V1TBSCertificateGenerator;
+import org.spongycastle.asn1.x509.V2TBSCertListGenerator;
+import org.spongycastle.asn1.x509.V3TBSCertificateGenerator;
+import org.spongycastle.asn1.x509.X509Extension;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTest;
+
+public class GenerationTest
+ extends SimpleTest
+{
+ private byte[] v1Cert = Base64.decode(
+ "MIGtAgEBMA0GCSqGSIb3DQEBBAUAMCUxCzAJBgNVBAMMAkFVMRYwFAYDVQQKDA1Cb"
+ + "3VuY3kgQ2FzdGxlMB4XDTcwMDEwMTAwMDAwMVoXDTcwMDEwMTAwMDAxMlowNjELMA"
+ + "kGA1UEAwwCQVUxFjAUBgNVBAoMDUJvdW5jeSBDYXN0bGUxDzANBgNVBAsMBlRlc3Q"
+ + "gMTAaMA0GCSqGSIb3DQEBAQUAAwkAMAYCAQECAQI=");
+
+ private byte[] v3Cert = Base64.decode(
+ "MIIBSKADAgECAgECMA0GCSqGSIb3DQEBBAUAMCUxCzAJBgNVBAMMAkFVMRYwFAYD"
+ + "VQQKDA1Cb3VuY3kgQ2FzdGxlMB4XDTcwMDEwMTAwMDAwMVoXDTcwMDEwMTAwMDAw"
+ + "MlowNjELMAkGA1UEAwwCQVUxFjAUBgNVBAoMDUJvdW5jeSBDYXN0bGUxDzANBgNV"
+ + "BAsMBlRlc3QgMjAYMBAGBisOBwIBATAGAgEBAgECAwQAAgEDo4GVMIGSMGEGA1Ud"
+ + "IwEB/wRXMFWAFDZPdpHPzKi7o8EJokkQU2uqCHRRoTqkODA2MQswCQYDVQQDDAJB"
+ + "VTEWMBQGA1UECgwNQm91bmN5IENhc3RsZTEPMA0GA1UECwwGVGVzdCAyggECMCAG"
+ + "A1UdDgEB/wQWBBQ2T3aRz8you6PBCaJJEFNrqgh0UTALBgNVHQ8EBAMCBBA=");
+
+ private byte[] v3CertNullSubject = Base64.decode(
+ "MIHGoAMCAQICAQIwDQYJKoZIhvcNAQEEBQAwJTELMAkGA1UEAwwCQVUxFjAUBgNVB"
+ + "AoMDUJvdW5jeSBDYXN0bGUwHhcNNzAwMTAxMDAwMDAxWhcNNzAwMTAxMDAwMDAyWj"
+ + "AAMBgwEAYGKw4HAgEBMAYCAQECAQIDBAACAQOjSjBIMEYGA1UdEQEB/wQ8MDqkODA"
+ + "2MQswCQYDVQQDDAJBVTEWMBQGA1UECgwNQm91bmN5IENhc3RsZTEPMA0GA1UECwwG"
+ + "VGVzdCAy");
+
+ private byte[] v2CertList = Base64.decode(
+ "MIIBQwIBATANBgkqhkiG9w0BAQUFADAlMQswCQYDVQQDDAJBVTEWMBQGA1UECgwN" +
+ "Qm91bmN5IENhc3RsZRcNNzAwMTAxMDAwMDAwWhcNNzAwMTAxMDAwMDAyWjAiMCAC" +
+ "AQEXDTcwMDEwMTAwMDAwMVowDDAKBgNVHRUEAwoBCqCBxTCBwjBhBgNVHSMBAf8E" +
+ "VzBVgBQ2T3aRz8you6PBCaJJEFNrqgh0UaE6pDgwNjELMAkGA1UEAwwCQVUxFjAU" +
+ "BgNVBAoMDUJvdW5jeSBDYXN0bGUxDzANBgNVBAsMBlRlc3QgMoIBAjBDBgNVHRIE" +
+ "PDA6pDgwNjELMAkGA1UEAwwCQVUxFjAUBgNVBAoMDUJvdW5jeSBDYXN0bGUxDzAN" +
+ "BgNVBAsMBlRlc3QgMzAKBgNVHRQEAwIBATAMBgNVHRwBAf8EAjAA");
+
+ private void tbsV1CertGen()
+ throws IOException
+ {
+ V1TBSCertificateGenerator gen = new V1TBSCertificateGenerator();
+ Date startDate = new Date(1000);
+ Date endDate = new Date(12000);
+
+ gen.setSerialNumber(new ASN1Integer(1));
+
+ gen.setStartDate(new Time(startDate));
+ gen.setEndDate(new Time(endDate));
+
+ gen.setIssuer(new X500Name("CN=AU,O=Bouncy Castle"));
+ gen.setSubject(new X500Name("CN=AU,O=Bouncy Castle,OU=Test 1"));
+
+ gen.setSignature(new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption, DERNull.INSTANCE));
+
+ SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE),
+ new RSAPublicKeyStructure(BigInteger.valueOf(1), BigInteger.valueOf(2)));
+
+ gen.setSubjectPublicKeyInfo(info);
+
+ TBSCertificate tbs = gen.generateTBSCertificate();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(tbs);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v1Cert))
+ {
+ fail("failed v1 cert generation");
+ }
+
+ //
+ // read back test
+ //
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(v1Cert));
+ ASN1Primitive o = aIn.readObject();
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(o);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v1Cert))
+ {
+ fail("failed v1 cert read back test");
+ }
+ }
+
+ private AuthorityKeyIdentifier createAuthorityKeyId(
+ SubjectPublicKeyInfo info,
+ X500Name name,
+ int sNumber)
+ {
+ GeneralName genName = new GeneralName(name);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(genName);
+
+ return new AuthorityKeyIdentifier(
+ info, GeneralNames.getInstance(new DERSequence(v)), BigInteger.valueOf(sNumber));
+ }
+
+ private void tbsV3CertGen()
+ throws IOException
+ {
+ V3TBSCertificateGenerator gen = new V3TBSCertificateGenerator();
+ Date startDate = new Date(1000);
+ Date endDate = new Date(2000);
+
+ gen.setSerialNumber(new ASN1Integer(2));
+
+ gen.setStartDate(new Time(startDate));
+ gen.setEndDate(new Time(endDate));
+
+ gen.setIssuer(new X500Name("CN=AU,O=Bouncy Castle"));
+ gen.setSubject(new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"));
+
+ gen.setSignature(new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption, DERNull.INSTANCE));
+
+ SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(BigInteger.valueOf(1), BigInteger.valueOf(2))), new ASN1Integer(3));
+
+ gen.setSubjectPublicKeyInfo(info);
+
+ //
+ // add extensions
+ //
+ Vector order = new Vector();
+ Hashtable extensions = new Hashtable();
+
+ order.addElement(X509Extension.authorityKeyIdentifier);
+ order.addElement(X509Extension.subjectKeyIdentifier);
+ order.addElement(X509Extension.keyUsage);
+
+ extensions.put(X509Extension.authorityKeyIdentifier, new X509Extension(true, new DEROctetString(createAuthorityKeyId(info, new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"), 2))));
+ extensions.put(X509Extension.subjectKeyIdentifier, new X509Extension(true, new DEROctetString(new SubjectKeyIdentifier(info))));
+ extensions.put(X509Extension.keyUsage, new X509Extension(false, new DEROctetString(new KeyUsage(KeyUsage.dataEncipherment))));
+
+ X509Extensions ex = new X509Extensions(order, extensions);
+
+ gen.setExtensions(ex);
+
+ TBSCertificate tbs = gen.generateTBSCertificate();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(tbs);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v3Cert))
+ {
+ fail("failed v3 cert generation");
+ }
+
+ //
+ // read back test
+ //
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(v3Cert));
+ ASN1Primitive o = aIn.readObject();
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(o);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v3Cert))
+ {
+ fail("failed v3 cert read back test");
+ }
+ }
+
+ private void tbsV3CertGenWithNullSubject()
+ throws IOException
+ {
+ V3TBSCertificateGenerator gen = new V3TBSCertificateGenerator();
+ Date startDate = new Date(1000);
+ Date endDate = new Date(2000);
+
+ gen.setSerialNumber(new ASN1Integer(2));
+
+ gen.setStartDate(new Time(startDate));
+ gen.setEndDate(new Time(endDate));
+
+ gen.setIssuer(new X500Name("CN=AU,O=Bouncy Castle"));
+
+ gen.setSignature(new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption, DERNull.INSTANCE));
+
+ SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(BigInteger.valueOf(1), BigInteger.valueOf(2))), new ASN1Integer(3));
+
+ gen.setSubjectPublicKeyInfo(info);
+
+ try
+ {
+ gen.generateTBSCertificate();
+ fail("null subject not caught!");
+ }
+ catch (IllegalStateException e)
+ {
+ if (!e.getMessage().equals("not all mandatory fields set in V3 TBScertificate generator"))
+ {
+ fail("unexpected exception", e);
+ }
+ }
+
+ //
+ // add extensions
+ //
+ Vector order = new Vector();
+ Hashtable extensions = new Hashtable();
+
+ order.addElement(X509Extension.subjectAlternativeName);
+
+ extensions.put(X509Extension.subjectAlternativeName, new X509Extension(true, new DEROctetString(new GeneralNames(new GeneralName(new X509Name("CN=AU,O=Bouncy Castle,OU=Test 2"))))));
+
+ X509Extensions ex = new X509Extensions(order, extensions);
+
+ gen.setExtensions(ex);
+
+ TBSCertificate tbs = gen.generateTBSCertificate();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(tbs);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v3CertNullSubject))
+ {
+ fail("failed v3 null sub cert generation");
+ }
+
+ //
+ // read back test
+ //
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(v3CertNullSubject));
+ ASN1Primitive o = aIn.readObject();
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(o);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v3CertNullSubject))
+ {
+ fail("failed v3 null sub cert read back test");
+ }
+ }
+
+ private void tbsV2CertListGen()
+ throws IOException
+ {
+ V2TBSCertListGenerator gen = new V2TBSCertListGenerator();
+
+ gen.setIssuer(new X500Name("CN=AU,O=Bouncy Castle"));
+
+ gen.addCRLEntry(new ASN1Integer(1), new Time(new Date(1000)), CRLReason.aACompromise);
+
+ gen.setNextUpdate(new Time(new Date(2000)));
+
+ gen.setThisUpdate(new Time(new Date(500)));
+
+ gen.setSignature(new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption, DERNull.INSTANCE));
+
+ //
+ // extensions
+ //
+ SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(BigInteger.valueOf(1), BigInteger.valueOf(2))), new ASN1Integer(3));
+
+ ExtensionsGenerator extGen = new ExtensionsGenerator();
+
+ extGen.addExtension(Extension.authorityKeyIdentifier, true, createAuthorityKeyId(info, new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"), 2));
+ extGen.addExtension(Extension.issuerAlternativeName, false, new GeneralNames(new GeneralName(new X500Name("CN=AU,O=Bouncy Castle,OU=Test 3"))));
+ extGen.addExtension(Extension.cRLNumber, false, new ASN1Integer(1));
+ extGen.addExtension(Extension.issuingDistributionPoint, true, IssuingDistributionPoint.getInstance(new DERSequence()));
+
+ Extensions ex = extGen.generate();
+
+ gen.setExtensions(ex);
+
+ TBSCertList tbs = gen.generateTBSCertList();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(tbs);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v2CertList))
+ {
+ System.out.println(new String(Base64.encode(bOut.toByteArray())));
+ fail("failed v2 cert list generation");
+ }
+
+ //
+ // read back test
+ //
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(v2CertList));
+ ASN1Primitive o = aIn.readObject();
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(o);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), v2CertList))
+ {
+ fail("failed v2 cert list read back test");
+ }
+
+ //
+ // check we can add a custom reason
+ //
+ gen.addCRLEntry(new ASN1Integer(1), new Time(new Date(1000)), CRLReason.aACompromise);
+
+ //
+ // check invalidity date
+ gen.addCRLEntry(new ASN1Integer(2), new Time(new Date(1000)), CRLReason.affiliationChanged, new ASN1GeneralizedTime(new Date(2000)));
+
+ TBSCertList crl = gen.generateTBSCertList();
+
+ TBSCertList.CRLEntry[] entries = crl.getRevokedCertificates();
+ for (int i = 0; i != entries.length; i++)
+ {
+ TBSCertList.CRLEntry entry = entries[i];
+
+ if (entry.getUserCertificate().equals(new ASN1Integer(1)))
+ {
+ Extensions extensions = entry.getExtensions();
+ Extension ext = extensions.getExtension(Extension.reasonCode);
+
+ CRLReason r = CRLReason.getInstance(ext.getParsedValue());
+
+ if (r.getValue().intValue() != CRLReason.aACompromise)
+ {
+ fail("reason code mismatch");
+ }
+ }
+ else if (entry.getUserCertificate().equals(new ASN1Integer(2)))
+ {
+ Extensions extensions = entry.getExtensions();
+ Extension ext = extensions.getExtension(Extension.reasonCode);
+
+ CRLReason r = CRLReason.getInstance(ext.getParsedValue());
+
+ if (r.getValue().intValue() != CRLReason.affiliationChanged)
+ {
+ fail("reason code mismatch");
+ }
+
+ ext = extensions.getExtension(Extension.invalidityDate);
+
+ ASN1GeneralizedTime t = ASN1GeneralizedTime.getInstance(ext.getParsedValue());
+
+ try
+ {
+ if (!t.getDate().equals(new Date(2000)))
+ {
+ fail("invalidity date mismatch");
+ }
+ }
+ catch (ParseException e)
+ {
+ fail("can't parse date", e);
+ }
+ }
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ tbsV1CertGen();
+ tbsV3CertGen();
+ tbsV3CertGenWithNullSubject();
+ tbsV2CertListGen();
+ }
+
+ public String getName()
+ {
+ return "Generation";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GenerationTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GetInstanceTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GetInstanceTest.java
new file mode 100644
index 000000000..05652173c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/GetInstanceTest.java
@@ -0,0 +1,888 @@
+package org.spongycastle.asn1.test;
+
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Vector;
+
+import junit.framework.TestCase;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Enumerated;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DERBMPString;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERGeneralString;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DERNumericString;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERT61String;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.DERUniversalString;
+import org.spongycastle.asn1.DERVisibleString;
+import org.spongycastle.asn1.cmp.CAKeyUpdAnnContent;
+import org.spongycastle.asn1.cmp.CMPCertificate;
+import org.spongycastle.asn1.cmp.CRLAnnContent;
+import org.spongycastle.asn1.cmp.CertConfirmContent;
+import org.spongycastle.asn1.cmp.CertOrEncCert;
+import org.spongycastle.asn1.cmp.CertRepMessage;
+import org.spongycastle.asn1.cmp.CertResponse;
+import org.spongycastle.asn1.cmp.CertifiedKeyPair;
+import org.spongycastle.asn1.cmp.Challenge;
+import org.spongycastle.asn1.cmp.ErrorMsgContent;
+import org.spongycastle.asn1.cmp.GenMsgContent;
+import org.spongycastle.asn1.cmp.GenRepContent;
+import org.spongycastle.asn1.cmp.InfoTypeAndValue;
+import org.spongycastle.asn1.cmp.KeyRecRepContent;
+import org.spongycastle.asn1.cmp.OOBCertHash;
+import org.spongycastle.asn1.cmp.PBMParameter;
+import org.spongycastle.asn1.cmp.PKIBody;
+import org.spongycastle.asn1.cmp.PKIConfirmContent;
+import org.spongycastle.asn1.cmp.PKIFailureInfo;
+import org.spongycastle.asn1.cmp.PKIFreeText;
+import org.spongycastle.asn1.cmp.PKIHeader;
+import org.spongycastle.asn1.cmp.PKIMessage;
+import org.spongycastle.asn1.cmp.PKIMessages;
+import org.spongycastle.asn1.cmp.PKIStatus;
+import org.spongycastle.asn1.cmp.PKIStatusInfo;
+import org.spongycastle.asn1.cmp.POPODecKeyChallContent;
+import org.spongycastle.asn1.cmp.POPODecKeyRespContent;
+import org.spongycastle.asn1.cmp.PollRepContent;
+import org.spongycastle.asn1.cmp.PollReqContent;
+import org.spongycastle.asn1.cmp.ProtectedPart;
+import org.spongycastle.asn1.cmp.RevAnnContent;
+import org.spongycastle.asn1.cmp.RevDetails;
+import org.spongycastle.asn1.cmp.RevRepContent;
+import org.spongycastle.asn1.cmp.RevReqContent;
+import org.spongycastle.asn1.cms.Attribute;
+import org.spongycastle.asn1.cms.Attributes;
+import org.spongycastle.asn1.cms.AuthEnvelopedData;
+import org.spongycastle.asn1.cms.AuthenticatedData;
+import org.spongycastle.asn1.cms.CompressedData;
+import org.spongycastle.asn1.cms.ContentInfo;
+import org.spongycastle.asn1.cms.EncryptedContentInfo;
+import org.spongycastle.asn1.cms.EncryptedData;
+import org.spongycastle.asn1.cms.EnvelopedData;
+import org.spongycastle.asn1.cms.Evidence;
+import org.spongycastle.asn1.cms.IssuerAndSerialNumber;
+import org.spongycastle.asn1.cms.KEKIdentifier;
+import org.spongycastle.asn1.cms.KEKRecipientInfo;
+import org.spongycastle.asn1.cms.KeyAgreeRecipientIdentifier;
+import org.spongycastle.asn1.cms.KeyAgreeRecipientInfo;
+import org.spongycastle.asn1.cms.KeyTransRecipientInfo;
+import org.spongycastle.asn1.cms.MetaData;
+import org.spongycastle.asn1.cms.OriginatorIdentifierOrKey;
+import org.spongycastle.asn1.cms.OriginatorInfo;
+import org.spongycastle.asn1.cms.OriginatorPublicKey;
+import org.spongycastle.asn1.cms.OtherKeyAttribute;
+import org.spongycastle.asn1.cms.OtherRecipientInfo;
+import org.spongycastle.asn1.cms.PasswordRecipientInfo;
+import org.spongycastle.asn1.cms.RecipientEncryptedKey;
+import org.spongycastle.asn1.cms.RecipientIdentifier;
+import org.spongycastle.asn1.cms.RecipientInfo;
+import org.spongycastle.asn1.cms.RecipientKeyIdentifier;
+import org.spongycastle.asn1.cms.SignerIdentifier;
+import org.spongycastle.asn1.cms.SignerInfo;
+import org.spongycastle.asn1.cms.TimeStampAndCRL;
+import org.spongycastle.asn1.cms.TimeStampTokenEvidence;
+import org.spongycastle.asn1.cms.TimeStampedData;
+import org.spongycastle.asn1.cms.ecc.MQVuserKeyingMaterial;
+import org.spongycastle.asn1.crmf.AttributeTypeAndValue;
+import org.spongycastle.asn1.crmf.CertId;
+import org.spongycastle.asn1.crmf.CertReqMessages;
+import org.spongycastle.asn1.crmf.CertReqMsg;
+import org.spongycastle.asn1.crmf.CertRequest;
+import org.spongycastle.asn1.crmf.CertTemplate;
+import org.spongycastle.asn1.crmf.Controls;
+import org.spongycastle.asn1.crmf.EncKeyWithID;
+import org.spongycastle.asn1.crmf.EncryptedKey;
+import org.spongycastle.asn1.crmf.EncryptedValue;
+import org.spongycastle.asn1.crmf.OptionalValidity;
+import org.spongycastle.asn1.crmf.PKIArchiveOptions;
+import org.spongycastle.asn1.crmf.PKIPublicationInfo;
+import org.spongycastle.asn1.crmf.PKMACValue;
+import org.spongycastle.asn1.crmf.POPOPrivKey;
+import org.spongycastle.asn1.crmf.POPOSigningKey;
+import org.spongycastle.asn1.crmf.POPOSigningKeyInput;
+import org.spongycastle.asn1.crmf.ProofOfPossession;
+import org.spongycastle.asn1.crmf.SinglePubInfo;
+import org.spongycastle.asn1.cryptopro.ECGOST3410ParamSetParameters;
+import org.spongycastle.asn1.cryptopro.GOST28147Parameters;
+import org.spongycastle.asn1.cryptopro.GOST3410ParamSetParameters;
+import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.spongycastle.asn1.eac.CVCertificate;
+import org.spongycastle.asn1.eac.CVCertificateRequest;
+import org.spongycastle.asn1.eac.CertificateBody;
+import org.spongycastle.asn1.eac.PublicKeyDataObject;
+import org.spongycastle.asn1.eac.RSAPublicKey;
+import org.spongycastle.asn1.eac.UnsignedInteger;
+import org.spongycastle.asn1.esf.CommitmentTypeIndication;
+import org.spongycastle.asn1.esf.CommitmentTypeQualifier;
+import org.spongycastle.asn1.esf.CompleteRevocationRefs;
+import org.spongycastle.asn1.esf.CrlIdentifier;
+import org.spongycastle.asn1.esf.CrlListID;
+import org.spongycastle.asn1.esf.CrlOcspRef;
+import org.spongycastle.asn1.esf.CrlValidatedID;
+import org.spongycastle.asn1.esf.OcspIdentifier;
+import org.spongycastle.asn1.esf.OcspListID;
+import org.spongycastle.asn1.esf.OcspResponsesID;
+import org.spongycastle.asn1.esf.OtherHash;
+import org.spongycastle.asn1.esf.OtherHashAlgAndValue;
+import org.spongycastle.asn1.esf.OtherRevRefs;
+import org.spongycastle.asn1.esf.OtherRevVals;
+import org.spongycastle.asn1.esf.RevocationValues;
+import org.spongycastle.asn1.esf.SPUserNotice;
+import org.spongycastle.asn1.esf.SPuri;
+import org.spongycastle.asn1.esf.SigPolicyQualifierInfo;
+import org.spongycastle.asn1.esf.SigPolicyQualifiers;
+import org.spongycastle.asn1.esf.SignaturePolicyId;
+import org.spongycastle.asn1.esf.SignaturePolicyIdentifier;
+import org.spongycastle.asn1.esf.SignerAttribute;
+import org.spongycastle.asn1.esf.SignerLocation;
+import org.spongycastle.asn1.ess.ContentHints;
+import org.spongycastle.asn1.ess.ContentIdentifier;
+import org.spongycastle.asn1.ess.ESSCertID;
+import org.spongycastle.asn1.ess.ESSCertIDv2;
+import org.spongycastle.asn1.ess.OtherCertID;
+import org.spongycastle.asn1.ess.OtherSigningCertificate;
+import org.spongycastle.asn1.ess.SigningCertificate;
+import org.spongycastle.asn1.ess.SigningCertificateV2;
+import org.spongycastle.asn1.icao.CscaMasterList;
+import org.spongycastle.asn1.icao.DataGroupHash;
+import org.spongycastle.asn1.icao.LDSSecurityObject;
+import org.spongycastle.asn1.icao.LDSVersionInfo;
+import org.spongycastle.asn1.isismtt.ocsp.CertHash;
+import org.spongycastle.asn1.isismtt.ocsp.RequestedCertificate;
+import org.spongycastle.asn1.isismtt.x509.AdditionalInformationSyntax;
+import org.spongycastle.asn1.isismtt.x509.AdmissionSyntax;
+import org.spongycastle.asn1.isismtt.x509.Admissions;
+import org.spongycastle.asn1.isismtt.x509.DeclarationOfMajority;
+import org.spongycastle.asn1.isismtt.x509.MonetaryLimit;
+import org.spongycastle.asn1.isismtt.x509.NamingAuthority;
+import org.spongycastle.asn1.isismtt.x509.ProcurationSyntax;
+import org.spongycastle.asn1.isismtt.x509.ProfessionInfo;
+import org.spongycastle.asn1.isismtt.x509.Restriction;
+import org.spongycastle.asn1.misc.CAST5CBCParameters;
+import org.spongycastle.asn1.misc.IDEACBCPar;
+import org.spongycastle.asn1.mozilla.PublicKeyAndChallenge;
+import org.spongycastle.asn1.ocsp.BasicOCSPResponse;
+import org.spongycastle.asn1.ocsp.CertID;
+import org.spongycastle.asn1.ocsp.CertStatus;
+import org.spongycastle.asn1.ocsp.CrlID;
+import org.spongycastle.asn1.ocsp.OCSPRequest;
+import org.spongycastle.asn1.ocsp.OCSPResponse;
+import org.spongycastle.asn1.ocsp.OCSPResponseStatus;
+import org.spongycastle.asn1.ocsp.Request;
+import org.spongycastle.asn1.ocsp.ResponderID;
+import org.spongycastle.asn1.ocsp.ResponseBytes;
+import org.spongycastle.asn1.ocsp.ResponseData;
+import org.spongycastle.asn1.ocsp.RevokedInfo;
+import org.spongycastle.asn1.ocsp.Signature;
+import org.spongycastle.asn1.ocsp.SingleResponse;
+import org.spongycastle.asn1.ocsp.TBSRequest;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.AuthenticatedSafe;
+import org.spongycastle.asn1.pkcs.CertificationRequest;
+import org.spongycastle.asn1.pkcs.CertificationRequestInfo;
+import org.spongycastle.asn1.pkcs.DHParameter;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.MacData;
+import org.spongycastle.asn1.pkcs.PBEParameter;
+import org.spongycastle.asn1.pkcs.PBES2Parameters;
+import org.spongycastle.asn1.pkcs.PBKDF2Params;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.pkcs.Pfx;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.RC2CBCParameter;
+import org.spongycastle.asn1.pkcs.RSAESOAEPparams;
+import org.spongycastle.asn1.pkcs.RSAPrivateKey;
+import org.spongycastle.asn1.pkcs.RSASSAPSSparams;
+import org.spongycastle.asn1.pkcs.SafeBag;
+import org.spongycastle.asn1.pkcs.SignedData;
+import org.spongycastle.asn1.sec.ECPrivateKey;
+import org.spongycastle.asn1.smime.SMIMECapabilities;
+import org.spongycastle.asn1.smime.SMIMECapability;
+import org.spongycastle.asn1.tsp.Accuracy;
+import org.spongycastle.asn1.tsp.MessageImprint;
+import org.spongycastle.asn1.tsp.TSTInfo;
+import org.spongycastle.asn1.tsp.TimeStampReq;
+import org.spongycastle.asn1.tsp.TimeStampResp;
+import org.spongycastle.asn1.x500.DirectoryString;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AccessDescription;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.AttCertIssuer;
+import org.spongycastle.asn1.x509.AttCertValidityPeriod;
+import org.spongycastle.asn1.x509.AttributeCertificate;
+import org.spongycastle.asn1.x509.AttributeCertificateInfo;
+import org.spongycastle.asn1.x509.AuthorityInformationAccess;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.BasicConstraints;
+import org.spongycastle.asn1.x509.CRLDistPoint;
+import org.spongycastle.asn1.x509.CRLNumber;
+import org.spongycastle.asn1.x509.CRLReason;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.asn1.x509.CertificatePair;
+import org.spongycastle.asn1.x509.CertificatePolicies;
+import org.spongycastle.asn1.x509.DSAParameter;
+import org.spongycastle.asn1.x509.DigestInfo;
+import org.spongycastle.asn1.x509.DisplayText;
+import org.spongycastle.asn1.x509.DistributionPoint;
+import org.spongycastle.asn1.x509.DistributionPointName;
+import org.spongycastle.asn1.x509.ExtendedKeyUsage;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.GeneralSubtree;
+import org.spongycastle.asn1.x509.Holder;
+import org.spongycastle.asn1.x509.IetfAttrSyntax;
+import org.spongycastle.asn1.x509.IssuerSerial;
+import org.spongycastle.asn1.x509.IssuingDistributionPoint;
+import org.spongycastle.asn1.x509.NameConstraints;
+import org.spongycastle.asn1.x509.NoticeReference;
+import org.spongycastle.asn1.x509.ObjectDigestInfo;
+import org.spongycastle.asn1.x509.PolicyInformation;
+import org.spongycastle.asn1.x509.PolicyMappings;
+import org.spongycastle.asn1.x509.PolicyQualifierInfo;
+import org.spongycastle.asn1.x509.PrivateKeyUsagePeriod;
+import org.spongycastle.asn1.x509.RSAPublicKeyStructure;
+import org.spongycastle.asn1.x509.RoleSyntax;
+import org.spongycastle.asn1.x509.SubjectDirectoryAttributes;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.asn1.x509.TBSCertificate;
+import org.spongycastle.asn1.x509.TBSCertificateStructure;
+import org.spongycastle.asn1.x509.Target;
+import org.spongycastle.asn1.x509.TargetInformation;
+import org.spongycastle.asn1.x509.Targets;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.asn1.x509.UserNotice;
+import org.spongycastle.asn1.x509.V2Form;
+import org.spongycastle.asn1.x509.X509CertificateStructure;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.asn1.x509.qualified.BiometricData;
+import org.spongycastle.asn1.x509.qualified.Iso4217CurrencyCode;
+import org.spongycastle.asn1.x509.qualified.MonetaryValue;
+import org.spongycastle.asn1.x509.qualified.QCStatement;
+import org.spongycastle.asn1.x509.qualified.SemanticsInformation;
+import org.spongycastle.asn1.x509.qualified.TypeOfBiometricData;
+import org.spongycastle.asn1.x509.sigi.NameOrPseudonym;
+import org.spongycastle.asn1.x509.sigi.PersonalData;
+import org.spongycastle.asn1.x9.DHDomainParameters;
+import org.spongycastle.asn1.x9.DHPublicKey;
+import org.spongycastle.asn1.x9.DHValidationParms;
+import org.spongycastle.asn1.x9.X962Parameters;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.encoders.Base64;
+
+public class GetInstanceTest
+ extends TestCase
+{
+ public static byte[] attrCert = Base64.decode(
+ "MIIHQDCCBqkCAQEwgZChgY2kgYowgYcxHDAaBgkqhkiG9w0BCQEWDW1sb3JjaEB2"
+ + "dC5lZHUxHjAcBgNVBAMTFU1hcmt1cyBMb3JjaCAobWxvcmNoKTEbMBkGA1UECxMS"
+ + "VmlyZ2luaWEgVGVjaCBVc2VyMRAwDgYDVQQLEwdDbGFzcyAyMQswCQYDVQQKEwJ2"
+ + "dDELMAkGA1UEBhMCVVMwgYmkgYYwgYMxGzAZBgkqhkiG9w0BCQEWDHNzaGFoQHZ0"
+ + "LmVkdTEbMBkGA1UEAxMSU3VtaXQgU2hhaCAoc3NoYWgpMRswGQYDVQQLExJWaXJn"
+ + "aW5pYSBUZWNoIFVzZXIxEDAOBgNVBAsTB0NsYXNzIDExCzAJBgNVBAoTAnZ0MQsw"
+ + "CQYDVQQGEwJVUzANBgkqhkiG9w0BAQQFAAIBBTAiGA8yMDAzMDcxODE2MDgwMloY"
+ + "DzIwMDMwNzI1MTYwODAyWjCCBU0wggVJBgorBgEEAbRoCAEBMYIFORaCBTU8UnVs"
+ + "ZSBSdWxlSWQ9IkZpbGUtUHJpdmlsZWdlLVJ1bGUiIEVmZmVjdD0iUGVybWl0Ij4K"
+ + "IDxUYXJnZXQ+CiAgPFN1YmplY3RzPgogICA8U3ViamVjdD4KICAgIDxTdWJqZWN0"
+ + "TWF0Y2ggTWF0Y2hJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpmdW5j"
+ + "dGlvbjpzdHJpbmctZXF1YWwiPgogICAgIDxBdHRyaWJ1dGVWYWx1ZSBEYXRhVHlw"
+ + "ZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjc3RyaW5nIj4KICAg"
+ + "ICAgIENOPU1hcmt1cyBMb3JjaDwvQXR0cmlidXRlVmFsdWU+CiAgICAgPFN1Ympl"
+ + "Y3RBdHRyaWJ1dGVEZXNpZ25hdG9yIEF0dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFt"
+ + "ZXM6dGM6eGFjbWw6MS4wOnN1YmplY3Q6c3ViamVjdC1pZCIgRGF0YVR5cGU9Imh0"
+ + "dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hI3N0cmluZyIgLz4gCiAgICA8"
+ + "L1N1YmplY3RNYXRjaD4KICAgPC9TdWJqZWN0PgogIDwvU3ViamVjdHM+CiAgPFJl"
+ + "c291cmNlcz4KICAgPFJlc291cmNlPgogICAgPFJlc291cmNlTWF0Y2ggTWF0Y2hJ"
+ + "ZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpmdW5jdGlvbjpzdHJpbmct"
+ + "ZXF1YWwiPgogICAgIDxBdHRyaWJ1dGVWYWx1ZSBEYXRhVHlwZT0iaHR0cDovL3d3"
+ + "dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjYW55VVJJIj4KICAgICAgaHR0cDovL3p1"
+ + "bmkuY3MudnQuZWR1PC9BdHRyaWJ1dGVWYWx1ZT4KICAgICA8UmVzb3VyY2VBdHRy"
+ + "aWJ1dGVEZXNpZ25hdG9yIEF0dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFtZXM6dGM6"
+ + "eGFjbWw6MS4wOnJlc291cmNlOnJlc291cmNlLWlkIiBEYXRhVHlwZT0iaHR0cDov"
+ + "L3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjYW55VVJJIiAvPiAKICAgIDwvUmVz"
+ + "b3VyY2VNYXRjaD4KICAgPC9SZXNvdXJjZT4KICA8L1Jlc291cmNlcz4KICA8QWN0"
+ + "aW9ucz4KICAgPEFjdGlvbj4KICAgIDxBY3Rpb25NYXRjaCBNYXRjaElkPSJ1cm46"
+ + "b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOmZ1bmN0aW9uOnN0cmluZy1lcXVhbCI+"
+ + "CiAgICAgPEF0dHJpYnV0ZVZhbHVlIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9y"
+ + "Zy8yMDAxL1hNTFNjaGVtYSNzdHJpbmciPgpEZWxlZ2F0ZSBBY2Nlc3MgICAgIDwv"
+ + "QXR0cmlidXRlVmFsdWU+CgkgIDxBY3Rpb25BdHRyaWJ1dGVEZXNpZ25hdG9yIEF0"
+ + "dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOmFjdGlvbjph"
+ + "Y3Rpb24taWQiIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNj"
+ + "aGVtYSNzdHJpbmciIC8+IAogICAgPC9BY3Rpb25NYXRjaD4KICAgPC9BY3Rpb24+"
+ + "CiAgPC9BY3Rpb25zPgogPC9UYXJnZXQ+CjwvUnVsZT4KMA0GCSqGSIb3DQEBBAUA"
+ + "A4GBAGiJSM48XsY90HlYxGmGVSmNR6ZW2As+bot3KAfiCIkUIOAqhcphBS23egTr"
+ + "6asYwy151HshbPNYz+Cgeqs45KkVzh7bL/0e1r8sDVIaaGIkjHK3CqBABnfSayr3"
+ + "Rd1yBoDdEv8Qb+3eEPH6ab9021AsLEnJ6LWTmybbOpMNZ3tv");
+
+ byte[] cert1 = Base64.decode(
+ "MIIDXjCCAsegAwIBAgIBBzANBgkqhkiG9w0BAQQFADCBtzELMAkGA1UEBhMCQVUx"
+ + "ETAPBgNVBAgTCFZpY3RvcmlhMRgwFgYDVQQHEw9Tb3V0aCBNZWxib3VybmUxGjAY"
+ + "BgNVBAoTEUNvbm5lY3QgNCBQdHkgTHRkMR4wHAYDVQQLExVDZXJ0aWZpY2F0ZSBB"
+ + "dXRob3JpdHkxFTATBgNVBAMTDENvbm5lY3QgNCBDQTEoMCYGCSqGSIb3DQEJARYZ"
+ + "d2VibWFzdGVyQGNvbm5lY3Q0LmNvbS5hdTAeFw0wMDA2MDIwNzU2MjFaFw0wMTA2"
+ + "MDIwNzU2MjFaMIG4MQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExGDAW"
+ + "BgNVBAcTD1NvdXRoIE1lbGJvdXJuZTEaMBgGA1UEChMRQ29ubmVjdCA0IFB0eSBM"
+ + "dGQxFzAVBgNVBAsTDldlYnNlcnZlciBUZWFtMR0wGwYDVQQDExR3d3cyLmNvbm5l"
+ + "Y3Q0LmNvbS5hdTEoMCYGCSqGSIb3DQEJARYZd2VibWFzdGVyQGNvbm5lY3Q0LmNv"
+ + "bS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArvDxclKAhyv7Q/Wmr2re"
+ + "Gw4XL9Cnh9e+6VgWy2AWNy/MVeXdlxzd7QAuc1eOWQkGQEiLPy5XQtTY+sBUJ3AO"
+ + "Rvd2fEVJIcjf29ey7bYua9J/vz5MG2KYo9/WCHIwqD9mmG9g0xLcfwq/s8ZJBswE"
+ + "7sb85VU+h94PTvsWOsWuKaECAwEAAaN3MHUwJAYDVR0RBB0wG4EZd2VibWFzdGVy"
+ + "QGNvbm5lY3Q0LmNvbS5hdTA6BglghkgBhvhCAQ0ELRYrbW9kX3NzbCBnZW5lcmF0"
+ + "ZWQgY3VzdG9tIHNlcnZlciBjZXJ0aWZpY2F0ZTARBglghkgBhvhCAQEEBAMCBkAw"
+ + "DQYJKoZIhvcNAQEEBQADgYEAotccfKpwSsIxM1Hae8DR7M/Rw8dg/RqOWx45HNVL"
+ + "iBS4/3N/TO195yeQKbfmzbAA2jbPVvIvGgTxPgO1MP4ZgvgRhasaa0qCJCkWvpM4"
+ + "yQf33vOiYQbpv4rTwzU8AmRlBG45WdjyNIigGV+oRc61aKCTnLq7zB8N3z1TF/bF"
+ + "5/8=");
+
+ private byte[] v2CertList = Base64.decode(
+ "MIICjTCCAfowDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoT"
+ + "F1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVy"
+ + "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw05NTA1MDIwMjEyMjZaFw05NTA2MDEw"
+ + "MDAxNDlaMIIBaDAWAgUCQQAABBcNOTUwMjAxMTcyNDI2WjAWAgUCQQAACRcNOTUw"
+ + "MjEwMDIxNjM5WjAWAgUCQQAADxcNOTUwMjI0MDAxMjQ5WjAWAgUCQQAADBcNOTUw"
+ + "MjI1MDA0NjQ0WjAWAgUCQQAAGxcNOTUwMzEzMTg0MDQ5WjAWAgUCQQAAFhcNOTUw"
+ + "MzE1MTkxNjU0WjAWAgUCQQAAGhcNOTUwMzE1MTk0MDQxWjAWAgUCQQAAHxcNOTUw"
+ + "MzI0MTk0NDMzWjAWAgUCcgAABRcNOTUwMzI5MjAwNzExWjAWAgUCcgAAERcNOTUw"
+ + "MzMwMDIzNDI2WjAWAgUCQQAAIBcNOTUwNDA3MDExMzIxWjAWAgUCcgAAHhcNOTUw"
+ + "NDA4MDAwMjU5WjAWAgUCcgAAQRcNOTUwNDI4MTcxNzI0WjAWAgUCcgAAOBcNOTUw"
+ + "NDI4MTcyNzIxWjAWAgUCcgAATBcNOTUwNTAyMDIxMjI2WjANBgkqhkiG9w0BAQIF"
+ + "AAN+AHqOEJXSDejYy0UwxxrH/9+N2z5xu/if0J6qQmK92W0hW158wpJg+ovV3+wQ"
+ + "wvIEPRL2rocL0tKfAsVq1IawSJzSNgxG0lrcla3MrJBnZ4GaZDu4FutZh72MR3Gt"
+ + "JaAL3iTJHJD55kK2D/VoyY1djlsPuNh6AEgdVwFAyp0v");
+
+ private static final Object[] NULL_ARGS = new Object[] { null };
+
+ private void doFullGetInstanceTest(Class clazz, ASN1Object o1)
+ throws Exception
+ {
+ Method m;
+
+ try
+ {
+ m = clazz.getMethod("getInstance", Object.class);
+ }
+ catch (NoSuchMethodException e)
+ {
+ fail("no getInstance method found");
+ return;
+ }
+
+ ASN1Object o2 = (ASN1Object)m.invoke(clazz, NULL_ARGS);
+ if (o2 != null)
+ {
+ fail(clazz.getName() + " null failed");
+ }
+
+ o2 = (ASN1Object)m.invoke(clazz, o1);
+
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " equality failed");
+ }
+
+ o2 = (ASN1Object)m.invoke(clazz, o1.getEncoded());
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " encoded equality failed");
+ }
+
+ o2 = (ASN1Object)m.invoke(clazz, o1.toASN1Primitive());
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " sequence equality failed");
+ }
+
+ try
+ {
+ m = clazz.getMethod("getInstance", ASN1TaggedObject.class, Boolean.TYPE);
+ }
+ catch (NoSuchMethodException e)
+ {
+ return;
+ }
+
+ ASN1TaggedObject t = new DERTaggedObject(true, 0, o1);
+ o2 = (ASN1Object)m.invoke(clazz, t, true);
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " tag equality failed");
+ }
+
+ t = new DERTaggedObject(true, 0, o1.toASN1Primitive());
+ o2 = (ASN1Object)m.invoke(clazz, t, true);
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " tag equality failed");
+ }
+
+ t = ASN1TaggedObject.getInstance(t.getEncoded());
+ o2 = (ASN1Object)m.invoke(clazz, t, true);
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " tag equality failed");
+ }
+
+ t = new DERTaggedObject(false, 0, o1);
+ o2 = (ASN1Object)m.invoke(clazz, t, false);
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " tag equality failed");
+ }
+
+ t = new DERTaggedObject(false, 0, o1.toASN1Primitive());
+ o2 = (ASN1Object)m.invoke(clazz, t, false);
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " tag equality failed");
+ }
+
+ t = ASN1TaggedObject.getInstance(t.getEncoded());
+ o2 = (ASN1Object)m.invoke(clazz, t, false);
+ if (!o1.equals(o2) || !clazz.isInstance(o2))
+ {
+ fail(clazz.getName() + " tag equality failed");
+ }
+ }
+
+ public void testGetInstance()
+ throws Exception
+ {
+ doFullGetInstanceTest(DERPrintableString.class, new DERPrintableString("hello world"));
+ doFullGetInstanceTest(DERBMPString.class, new DERBMPString("hello world"));
+ doFullGetInstanceTest(DERUTF8String.class, new DERUTF8String("hello world"));
+ doFullGetInstanceTest(DERUniversalString.class, new DERUniversalString(new byte[20]));
+ doFullGetInstanceTest(DERIA5String.class, new DERIA5String("hello world"));
+ doFullGetInstanceTest(DERGeneralString.class, new DERGeneralString("hello world"));
+ doFullGetInstanceTest(DERNumericString.class, new DERNumericString("hello world"));
+ doFullGetInstanceTest(DERNumericString.class, new DERNumericString("99999", true));
+ doFullGetInstanceTest(DERT61String.class, new DERT61String("hello world"));
+ doFullGetInstanceTest(DERVisibleString.class, new DERVisibleString("hello world"));
+
+ doFullGetInstanceTest(ASN1Integer.class, new ASN1Integer(1));
+ doFullGetInstanceTest(ASN1GeneralizedTime.class, new ASN1GeneralizedTime(new Date()));
+ doFullGetInstanceTest(ASN1UTCTime.class, new ASN1UTCTime(new Date()));
+ doFullGetInstanceTest(ASN1Enumerated.class, new ASN1Enumerated(1));
+
+ CMPCertificate cmpCert = new CMPCertificate(Certificate.getInstance(cert1));
+ CertificateList crl = CertificateList.getInstance(v2CertList);
+ AttributeCertificate attributeCert = AttributeCertificate.getInstance(attrCert);
+
+ doFullGetInstanceTest(CAKeyUpdAnnContent.class, new CAKeyUpdAnnContent(cmpCert, cmpCert, cmpCert));
+
+ CertConfirmContent.getInstance(null);
+ CertifiedKeyPair.getInstance(null);
+ CertOrEncCert.getInstance(null);
+ CertRepMessage.getInstance(null);
+ doFullGetInstanceTest(CertResponse.class, new CertResponse(new ASN1Integer(1), new PKIStatusInfo(PKIStatus.granted)));
+ doFullGetInstanceTest(org.spongycastle.asn1.cmp.CertStatus.class, new org.spongycastle.asn1.cmp.CertStatus(new byte[10], BigInteger.valueOf(1), new PKIStatusInfo(PKIStatus.granted)));
+ doFullGetInstanceTest(Challenge.class, new Challenge(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE), new byte[10], new byte[10]));
+
+ doFullGetInstanceTest(CMPCertificate.class, cmpCert);
+ doFullGetInstanceTest(CRLAnnContent.class, new CRLAnnContent(crl));
+ doFullGetInstanceTest(ErrorMsgContent.class, new ErrorMsgContent(new PKIStatusInfo(PKIStatus.granted), new ASN1Integer(1), new PKIFreeText("fred")));
+ GenMsgContent.getInstance(null);
+ GenRepContent.getInstance(null);
+ InfoTypeAndValue.getInstance(null);
+ KeyRecRepContent.getInstance(null);
+ OOBCertHash.getInstance(null);
+ PBMParameter.getInstance(null);
+ PKIBody.getInstance(null);
+ PKIConfirmContent.getInstance(null);
+ PKIFreeText.getInstance(null);
+ doFullGetInstanceTest(PKIFreeText.class, new PKIFreeText("hello world"));
+ doFullGetInstanceTest(PKIFreeText.class, new PKIFreeText(new String[]{"hello", "world"}));
+ doFullGetInstanceTest(PKIFreeText.class, new PKIFreeText(new DERUTF8String[]{new DERUTF8String("hello"), new DERUTF8String("world")}));
+ PKIHeader.getInstance(null);
+ PKIMessage.getInstance(null);
+ PKIMessages.getInstance(null);
+ doFullGetInstanceTest(PKIStatusInfo.class, new PKIStatusInfo(PKIStatus.rejection, new PKIFreeText("hello world"), new PKIFailureInfo(PKIFailureInfo.badAlg)));
+ doFullGetInstanceTest(PKIStatusInfo.class, new PKIStatusInfo(PKIStatus.granted, new PKIFreeText("hello world")));
+ PKIStatus.getInstance(null);
+ PollRepContent.getInstance(null);
+ PollReqContent.getInstance(null);
+ POPODecKeyChallContent.getInstance(null);
+ POPODecKeyRespContent.getInstance(null);
+ ProtectedPart.getInstance(null);
+ RevAnnContent.getInstance(null);
+ RevDetails.getInstance(null);
+ RevRepContent.getInstance(null);
+ RevReqContent.getInstance(null);
+ Attribute.getInstance(null);
+ Attributes.getInstance(null);
+ AuthenticatedData.getInstance(null);
+ AuthenticatedData.getInstance(null);
+ AuthEnvelopedData.getInstance(null);
+ AuthEnvelopedData.getInstance(null);
+ CompressedData.getInstance(null);
+ CompressedData.getInstance(null);
+ ContentInfo.getInstance(null);
+ EncryptedContentInfo.getInstance(null);
+ EncryptedData.getInstance(null);
+ EnvelopedData.getInstance(null);
+ EnvelopedData.getInstance(null);
+ Evidence.getInstance(null);
+ IssuerAndSerialNumber.getInstance(null);
+ KEKIdentifier.getInstance(null);
+ KEKIdentifier.getInstance(null);
+ KEKRecipientInfo.getInstance(null);
+ KEKRecipientInfo.getInstance(null);
+ KeyAgreeRecipientIdentifier.getInstance(null);
+ KeyAgreeRecipientIdentifier.getInstance(null);
+ KeyAgreeRecipientInfo.getInstance(null);
+ KeyAgreeRecipientInfo.getInstance(null);
+ KeyTransRecipientInfo.getInstance(null);
+ MetaData.getInstance(null);
+ OriginatorIdentifierOrKey.getInstance(null);
+ OriginatorIdentifierOrKey.getInstance(null);
+ OriginatorInfo.getInstance(null);
+ OriginatorInfo.getInstance(null);
+ OriginatorPublicKey.getInstance(null);
+ OriginatorPublicKey.getInstance(null);
+ OtherKeyAttribute.getInstance(null);
+ OtherRecipientInfo.getInstance(null);
+ OtherRecipientInfo.getInstance(null);
+ PasswordRecipientInfo.getInstance(null);
+ PasswordRecipientInfo.getInstance(null);
+ RecipientEncryptedKey.getInstance(null);
+ RecipientIdentifier.getInstance(null);
+ RecipientInfo.getInstance(null);
+ RecipientKeyIdentifier.getInstance(null);
+ RecipientKeyIdentifier.getInstance(null);
+ SignedData.getInstance(null);
+ SignerIdentifier.getInstance(null);
+ SignerInfo.getInstance(null);
+ Time.getInstance(null);
+ Time.getInstance(null);
+ TimeStampAndCRL.getInstance(null);
+ TimeStampedData.getInstance(null);
+ TimeStampTokenEvidence.getInstance(null);
+ AttributeTypeAndValue.getInstance(null);
+
+ doFullGetInstanceTest(CertId.class, new CertId(new GeneralName(new X500Name("CN=Test")), BigInteger.valueOf(1)));
+
+
+ CertReqMessages.getInstance(null);
+ CertReqMsg.getInstance(null);
+ CertRequest.getInstance(null);
+ CertTemplate.getInstance(null);
+ Controls.getInstance(null);
+ EncKeyWithID.getInstance(null);
+ EncryptedKey.getInstance(null);
+ EncryptedValue.getInstance(null);
+ OptionalValidity.getInstance(null);
+ PKIArchiveOptions.getInstance(null);
+ PKIPublicationInfo.getInstance(null);
+ PKMACValue.getInstance(null);
+ PKMACValue.getInstance(null);
+ POPOPrivKey.getInstance(null);
+ POPOSigningKeyInput.getInstance(null);
+ POPOSigningKey.getInstance(null);
+ POPOSigningKey.getInstance(null);
+ ProofOfPossession.getInstance(null);
+ SinglePubInfo.getInstance(null);
+ ECGOST3410ParamSetParameters.getInstance(null);
+ ECGOST3410ParamSetParameters.getInstance(null);
+ GOST28147Parameters.getInstance(null);
+ GOST28147Parameters.getInstance(null);
+ GOST3410ParamSetParameters.getInstance(null);
+ GOST3410ParamSetParameters.getInstance(null);
+ GOST3410PublicKeyAlgParameters.getInstance(null);
+ GOST3410PublicKeyAlgParameters.getInstance(null);
+ CertificateBody.getInstance(null);
+ CVCertificate.getInstance(null);
+ CVCertificateRequest.getInstance(null);
+ PublicKeyDataObject.getInstance(null);
+ UnsignedInteger.getInstance(null);
+ CommitmentTypeIndication.getInstance(null);
+ CommitmentTypeQualifier.getInstance(null);
+
+ OcspIdentifier ocspIdentifier = new OcspIdentifier(new ResponderID(new X500Name("CN=Test")), new ASN1GeneralizedTime(new Date()));
+ CrlListID crlListID = new CrlListID(new CrlValidatedID[]{new CrlValidatedID(new OtherHash(new byte[20]))});
+ OcspListID ocspListID = new OcspListID(new OcspResponsesID[] { new OcspResponsesID(ocspIdentifier) });
+ OtherRevRefs otherRevRefs = new OtherRevRefs(new ASN1ObjectIdentifier("1.2.1"), new DERSequence());
+ OtherRevVals otherRevVals = new OtherRevVals(new ASN1ObjectIdentifier("1.2.1"), new DERSequence());
+ CrlOcspRef crlOcspRef = new CrlOcspRef(crlListID, ocspListID, otherRevRefs);
+ doFullGetInstanceTest(CompleteRevocationRefs.class, new CompleteRevocationRefs(new CrlOcspRef[]{crlOcspRef, crlOcspRef}));
+
+ doFullGetInstanceTest(CrlIdentifier.class, new CrlIdentifier(new X500Name("CN=Test"), new ASN1UTCTime(new Date()), BigInteger.valueOf(1)));
+
+
+ doFullGetInstanceTest(CrlListID.class, crlListID);
+ doFullGetInstanceTest(CrlOcspRef.class, crlOcspRef);
+ doFullGetInstanceTest(CrlValidatedID.class, new CrlValidatedID(new OtherHash(new byte[20])));
+ doFullGetInstanceTest(OcspIdentifier.class, ocspIdentifier);
+ doFullGetInstanceTest(OcspListID.class, ocspListID);
+ doFullGetInstanceTest(OcspResponsesID.class, new OcspResponsesID(ocspIdentifier));
+
+ OtherHashAlgAndValue otherHashAlgAndValue = new OtherHashAlgAndValue(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE), new DEROctetString(new byte[10]));
+ doFullGetInstanceTest(OtherHashAlgAndValue.class, otherHashAlgAndValue);
+ OtherHash.getInstance(null);
+ doFullGetInstanceTest(OtherRevRefs.class, otherRevRefs);
+ doFullGetInstanceTest(OtherRevVals.class, otherRevVals);
+ doFullGetInstanceTest(RevocationValues.class, new RevocationValues(new CertificateList[]{crl}, null, otherRevVals));
+
+ SignaturePolicyId signaturePolicyId = new SignaturePolicyId(new ASN1ObjectIdentifier("1.2.1"), otherHashAlgAndValue);
+ doFullGetInstanceTest(SignaturePolicyIdentifier.class, new SignaturePolicyIdentifier());
+ doFullGetInstanceTest(SignaturePolicyIdentifier.class, new SignaturePolicyIdentifier(signaturePolicyId));
+ doFullGetInstanceTest(SignaturePolicyId.class, signaturePolicyId);
+ doFullGetInstanceTest(SignerAttribute.class, new SignerAttribute(new org.spongycastle.asn1.x509.Attribute[]{new org.spongycastle.asn1.x509.Attribute(new ASN1ObjectIdentifier("1.2.1"), new DERSet())}));
+ doFullGetInstanceTest(SignerAttribute.class, new SignerAttribute(attributeCert));
+
+ ASN1EncodableVector postalAddr = new ASN1EncodableVector();
+
+ postalAddr.add(new DERUTF8String("line 1"));
+ postalAddr.add(new DERUTF8String("line 2"));
+
+ doFullGetInstanceTest(SignerLocation.class, new SignerLocation(new DERUTF8String("AU"), new DERUTF8String("Melbourne"), new DERSequence(postalAddr)));
+ doFullGetInstanceTest(SigPolicyQualifierInfo.class, new SigPolicyQualifierInfo(new ASN1ObjectIdentifier("1.2.1"), new DERSequence()));
+ SigPolicyQualifiers.getInstance(null);
+ SPuri.getInstance(null);
+ Vector v = new Vector();
+
+ v.add(Integers.valueOf(1));
+ v.add(BigInteger.valueOf(2));
+ NoticeReference noticeReference = new NoticeReference("SC", v);
+ doFullGetInstanceTest(SPUserNotice.class, new SPUserNotice(noticeReference, new DisplayText("hello world")));
+ ContentHints.getInstance(null);
+ ContentIdentifier.getInstance(null);
+ ESSCertID.getInstance(null);
+ ESSCertIDv2.getInstance(null);
+ OtherCertID.getInstance(null);
+ OtherSigningCertificate.getInstance(null);
+ SigningCertificate.getInstance(null);
+ SigningCertificateV2.getInstance(null);
+ CscaMasterList.getInstance(null);
+ DataGroupHash.getInstance(null);
+ LDSSecurityObject.getInstance(null);
+ LDSVersionInfo.getInstance(null);
+ CAST5CBCParameters.getInstance(null);
+ IDEACBCPar.getInstance(null);
+ PublicKeyAndChallenge.getInstance(null);
+ BasicOCSPResponse.getInstance(null);
+ BasicOCSPResponse.getInstance(null);
+
+ doFullGetInstanceTest(CertID.class, new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE), new DEROctetString(new byte[1]), new DEROctetString(new byte[1]), new ASN1Integer(1)));
+
+ CertStatus.getInstance(null);
+ CertStatus.getInstance(null);
+ CrlID.getInstance(null);
+ OCSPRequest.getInstance(null);
+ OCSPRequest.getInstance(null);
+ OCSPResponse.getInstance(null);
+ OCSPResponse.getInstance(null);
+ OCSPResponseStatus.getInstance(null);
+ Request.getInstance(null);
+ Request.getInstance(null);
+ ResponderID.getInstance(null);
+ ResponderID.getInstance(null);
+ ResponseBytes.getInstance(null);
+ ResponseBytes.getInstance(null);
+ ResponseData.getInstance(null);
+ ResponseData.getInstance(null);
+ RevokedInfo.getInstance(null);
+ RevokedInfo.getInstance(null);
+ Signature.getInstance(null);
+ Signature.getInstance(null);
+ SingleResponse.getInstance(null);
+ SingleResponse.getInstance(null);
+ TBSRequest.getInstance(null);
+ TBSRequest.getInstance(null);
+ Attribute.getInstance(null);
+ AuthenticatedSafe.getInstance(null);
+ CertificationRequestInfo.getInstance(null);
+ CertificationRequest.getInstance(null);
+ ContentInfo.getInstance(null);
+ DHParameter.getInstance(null);
+ EncryptedData.getInstance(null);
+ EncryptedPrivateKeyInfo.getInstance(null);
+ AlgorithmIdentifier.getInstance(null);
+ IssuerAndSerialNumber.getInstance(null);
+ MacData.getInstance(null);
+ PBEParameter.getInstance(null);
+ PBES2Parameters.getInstance(null);
+ PBKDF2Params.getInstance(null);
+ Pfx.getInstance(null);
+ PKCS12PBEParams.getInstance(null);
+ PrivateKeyInfo.getInstance(null);
+ PrivateKeyInfo.getInstance(null);
+ RC2CBCParameter.getInstance(null);
+ RSAESOAEPparams.getInstance(null);
+ RSAPrivateKey.getInstance(null);
+ RSAPrivateKey.getInstance(null);
+ RSAPublicKey.getInstance(null);
+ RSAPublicKey.getInstance(null);
+ RSASSAPSSparams.getInstance(null);
+ SafeBag.getInstance(null);
+ SignedData.getInstance(null);
+ SignerInfo.getInstance(null);
+ ECPrivateKey.getInstance(null);
+ SMIMECapabilities.getInstance(null);
+ SMIMECapability.getInstance(null);
+ Accuracy.getInstance(null);
+ MessageImprint.getInstance(null);
+ TimeStampReq.getInstance(null);
+ TimeStampResp.getInstance(null);
+ TSTInfo.getInstance(null);
+ AttributeTypeAndValue.getInstance(null);
+ DirectoryString.getInstance(null);
+ DirectoryString.getInstance(null);
+ RDN.getInstance(null);
+ X500Name.getInstance(null);
+ X500Name.getInstance(null);
+ AccessDescription.getInstance(null);
+ AlgorithmIdentifier.getInstance(null);
+ AlgorithmIdentifier.getInstance(null);
+ AttCertIssuer.getInstance(null);
+ AttCertIssuer.getInstance(null);
+ AttCertValidityPeriod.getInstance(null);
+ AttributeCertificateInfo.getInstance(null);
+ AttributeCertificateInfo.getInstance(null);
+ AttributeCertificate.getInstance(null);
+ Attribute.getInstance(null);
+ AuthorityInformationAccess.getInstance(null);
+ AuthorityKeyIdentifier.getInstance(null);
+ AuthorityKeyIdentifier.getInstance(null);
+ BasicConstraints.getInstance(null);
+ BasicConstraints.getInstance(null);
+ Certificate.getInstance(null);
+ Certificate.getInstance(null);
+ CertificateList.getInstance(null);
+ CertificateList.getInstance(null);
+ CertificatePair.getInstance(null);
+ CertificatePolicies.getInstance(null);
+ CertificatePolicies.getInstance(null);
+ CRLDistPoint.getInstance(null);
+ CRLDistPoint.getInstance(null);
+ CRLNumber.getInstance(null);
+ CRLReason.getInstance(null);
+ DigestInfo.getInstance(null);
+ DigestInfo.getInstance(null);
+ DisplayText.getInstance(null);
+ DisplayText.getInstance(null);
+ DistributionPoint.getInstance(null);
+ DistributionPoint.getInstance(null);
+ DistributionPointName.getInstance(null);
+ DistributionPointName.getInstance(null);
+ DSAParameter.getInstance(null);
+ DSAParameter.getInstance(null);
+ ExtendedKeyUsage.getInstance(null);
+ ExtendedKeyUsage.getInstance(null);
+ Extensions.getInstance(null);
+ Extensions.getInstance(null);
+ GeneralName.getInstance(null);
+ GeneralName.getInstance(null);
+ GeneralNames.getInstance(null);
+ GeneralNames.getInstance(null);
+
+ GeneralSubtree generalSubtree = new GeneralSubtree(new GeneralName(new X500Name("CN=Test")));
+ ASN1ObjectIdentifier algOid = new ASN1ObjectIdentifier("1.2.1");
+ ObjectDigestInfo objectDigestInfo = new ObjectDigestInfo(ObjectDigestInfo.otherObjectDigest, algOid, new AlgorithmIdentifier(algOid), new byte[20]);
+
+ doFullGetInstanceTest(GeneralSubtree.class, generalSubtree);
+ doFullGetInstanceTest(Holder.class, new Holder(objectDigestInfo));
+ IetfAttrSyntax.getInstance(null);
+ IssuerSerial.getInstance(null);
+ IssuerSerial.getInstance(null);
+ IssuingDistributionPoint.getInstance(null);
+ IssuingDistributionPoint.getInstance(null);
+ DERBitString.getInstance(null);
+
+ v.clear();
+ v.add(generalSubtree);
+
+ doFullGetInstanceTest(NameConstraints.class, new NameConstraints(null, null));
+ doFullGetInstanceTest(NoticeReference.class, noticeReference);
+ doFullGetInstanceTest(ObjectDigestInfo.class, objectDigestInfo);
+
+ PolicyInformation.getInstance(null);
+ PolicyMappings.getInstance(null);
+ PolicyQualifierInfo.getInstance(null);
+ PrivateKeyUsagePeriod.getInstance(null);
+ doFullGetInstanceTest(RoleSyntax.class, new RoleSyntax(new GeneralNames(new GeneralName(new X500Name("CN=Test"))), new GeneralName(GeneralName.uniformResourceIdentifier, "http://bc")));
+ RSAPublicKeyStructure.getInstance(null);
+ RSAPublicKeyStructure.getInstance(null);
+ SubjectDirectoryAttributes.getInstance(null);
+ SubjectKeyIdentifier.getInstance(null);
+ SubjectKeyIdentifier.getInstance(null);
+ SubjectPublicKeyInfo.getInstance(null);
+ SubjectPublicKeyInfo.getInstance(null);
+ TargetInformation.getInstance(null);
+ Target.getInstance(null);
+ Targets.getInstance(null);
+ TBSCertificate.getInstance(null);
+ TBSCertificate.getInstance(null);
+ TBSCertificateStructure.getInstance(null);
+ TBSCertificateStructure.getInstance(null);
+ TBSCertList.CRLEntry.getInstance(null);
+ TBSCertList.getInstance(null);
+ TBSCertList.getInstance(null);
+ Time.getInstance(null);
+ Time.getInstance(null);
+ doFullGetInstanceTest(UserNotice.class, new UserNotice(noticeReference, "hello world"));
+ V2Form.getInstance(null);
+ V2Form.getInstance(null);
+ X509CertificateStructure.getInstance(null);
+ X509CertificateStructure.getInstance(null);
+ X509Extensions.getInstance(null);
+ X509Extensions.getInstance(null);
+ X509Name.getInstance(null);
+ X509Name.getInstance(null);
+ DHDomainParameters.getInstance(null);
+ DHDomainParameters.getInstance(null);
+ DHPublicKey.getInstance(null);
+ DHPublicKey.getInstance(null);
+ DHValidationParms.getInstance(null);
+ DHValidationParms.getInstance(null);
+ X962Parameters.getInstance(null);
+ X962Parameters.getInstance(null);
+ X9ECParameters.getInstance(null);
+ MQVuserKeyingMaterial.getInstance(null);
+ MQVuserKeyingMaterial.getInstance(null);
+ CertHash.getInstance(null);
+ RequestedCertificate.getInstance(null);
+ RequestedCertificate.getInstance(null);
+ AdditionalInformationSyntax.getInstance(null);
+ Admissions.getInstance(null);
+ AdmissionSyntax.getInstance(null);
+ DeclarationOfMajority.getInstance(null);
+ MonetaryLimit.getInstance(null);
+ NamingAuthority.getInstance(null);
+ NamingAuthority.getInstance(null);
+ ProcurationSyntax.getInstance(null);
+ ProfessionInfo.getInstance(null);
+ Restriction.getInstance(null);
+ BiometricData.getInstance(null);
+ Iso4217CurrencyCode.getInstance(null);
+ MonetaryValue.getInstance(null);
+ QCStatement.getInstance(null);
+ SemanticsInformation.getInstance(null);
+ TypeOfBiometricData.getInstance(null);
+ NameOrPseudonym.getInstance(null);
+ PersonalData.getInstance(null);
+ }
+
+ public String getName()
+ {
+ return "GetInstanceNullTest";
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/InputStreamTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/InputStreamTest.java
new file mode 100644
index 000000000..000455d86
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/InputStreamTest.java
@@ -0,0 +1,75 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.util.test.SimpleTest;
+
+public class InputStreamTest
+ extends SimpleTest
+{
+ private static final byte[] outOfBoundsLength = new byte[] { (byte)0x30, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
+ private static final byte[] negativeLength = new byte[] { (byte)0x30, (byte)0x84, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
+ private static final byte[] outsideLimitLength = new byte[] { (byte)0x30, (byte)0x83, (byte)0x0f, (byte)0xff, (byte)0xff };
+
+
+ public String getName()
+ {
+ return "InputStream";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ ASN1InputStream aIn = new ASN1InputStream(outOfBoundsLength);
+
+ try
+ {
+ aIn.readObject();
+ fail("out of bounds length not detected.");
+ }
+ catch (IOException e)
+ {
+ if (!e.getMessage().startsWith("DER length more than 4 bytes"))
+ {
+ fail("wrong exception: " + e.getMessage());
+ }
+ }
+
+ aIn = new ASN1InputStream(negativeLength);
+
+ try
+ {
+ aIn.readObject();
+ fail("negative length not detected.");
+ }
+ catch (IOException e)
+ {
+ if (!e.getMessage().equals("corrupted stream - negative length found"))
+ {
+ fail("wrong exception: " + e.getMessage());
+ }
+ }
+
+ aIn = new ASN1InputStream(outsideLimitLength);
+
+ try
+ {
+ aIn.readObject();
+ fail("outside limit length not detected.");
+ }
+ catch (IOException e)
+ {
+ if (!e.getMessage().equals("corrupted stream - out of bounds length found"))
+ {
+ fail("wrong exception: " + e.getMessage());
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new InputStreamTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/Iso4217CurrencyCodeUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/Iso4217CurrencyCodeUnitTest.java
new file mode 100644
index 000000000..1c1978044
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/Iso4217CurrencyCodeUnitTest.java
@@ -0,0 +1,142 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.x509.qualified.Iso4217CurrencyCode;
+import org.spongycastle.util.test.SimpleTest;
+
+public class Iso4217CurrencyCodeUnitTest
+ extends SimpleTest
+{
+ private static final String ALPHABETIC_CURRENCY_CODE = "AUD";
+ private static final int NUMERIC_CURRENCY_CODE = 1;
+
+ public String getName()
+ {
+ return "Iso4217CurrencyCode";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ //
+ // alphabetic
+ //
+ Iso4217CurrencyCode cc = new Iso4217CurrencyCode(ALPHABETIC_CURRENCY_CODE);
+
+ checkNumeric(cc, ALPHABETIC_CURRENCY_CODE);
+
+ cc = Iso4217CurrencyCode.getInstance(cc);
+
+ checkNumeric(cc, ALPHABETIC_CURRENCY_CODE);
+
+ ASN1Primitive obj = cc.toASN1Object();
+
+ cc = Iso4217CurrencyCode.getInstance(obj);
+
+ checkNumeric(cc, ALPHABETIC_CURRENCY_CODE);
+
+ //
+ // numeric
+ //
+ cc = new Iso4217CurrencyCode(NUMERIC_CURRENCY_CODE);
+
+ checkNumeric(cc, NUMERIC_CURRENCY_CODE);
+
+ cc = Iso4217CurrencyCode.getInstance(cc);
+
+ checkNumeric(cc, NUMERIC_CURRENCY_CODE);
+
+ obj = cc.toASN1Object();
+
+ cc = Iso4217CurrencyCode.getInstance(obj);
+
+ checkNumeric(cc, NUMERIC_CURRENCY_CODE);
+
+ cc = Iso4217CurrencyCode.getInstance(null);
+
+ if (cc != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ Iso4217CurrencyCode.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new Iso4217CurrencyCode("ABCD");
+
+ fail("constructor failed to detect out of range currencycode.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new Iso4217CurrencyCode(0);
+
+ fail("constructor failed to detect out of range small numeric code.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new Iso4217CurrencyCode(1000);
+
+ fail("constructor failed to detect out of range large numeric code.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkNumeric(
+ Iso4217CurrencyCode cc,
+ String code)
+ {
+ if (!cc.isAlphabetic())
+ {
+ fail("non-alphabetic code found when one expected.");
+ }
+
+ if (!cc.getAlphabetic().equals(code))
+ {
+ fail("string codes don't match.");
+ }
+ }
+
+ private void checkNumeric(
+ Iso4217CurrencyCode cc,
+ int code)
+ {
+ if (cc.isAlphabetic())
+ {
+ fail("alphabetic code found when one not expected.");
+ }
+
+ if (cc.getNumeric() != code)
+ {
+ fail("numeric codes don't match.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Iso4217CurrencyCodeUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/IssuingDistributionPointUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/IssuingDistributionPointUnitTest.java
new file mode 100644
index 000000000..6453d7079
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/IssuingDistributionPointUnitTest.java
@@ -0,0 +1,122 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.x509.DistributionPointName;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.IssuingDistributionPoint;
+import org.spongycastle.asn1.x509.ReasonFlags;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.util.test.SimpleTest;
+
+public class IssuingDistributionPointUnitTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "IssuingDistributionPoint";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DistributionPointName name = new DistributionPointName(
+ new GeneralNames(new GeneralName(new X509Name("cn=test"))));
+ ReasonFlags reasonFlags = new ReasonFlags(ReasonFlags.cACompromise);
+
+ checkPoint(6, name, true, true, reasonFlags, true, true);
+
+ checkPoint(2, name, false, false, reasonFlags, false, false);
+
+ checkPoint(0, null, false, false, null, false, false);
+
+ try
+ {
+ IssuingDistributionPoint.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkPoint(
+ int size,
+ DistributionPointName distributionPoint,
+ boolean onlyContainsUserCerts,
+ boolean onlyContainsCACerts,
+ ReasonFlags onlySomeReasons,
+ boolean indirectCRL,
+ boolean onlyContainsAttributeCerts)
+ throws IOException
+ {
+ IssuingDistributionPoint point = new IssuingDistributionPoint(distributionPoint, onlyContainsUserCerts, onlyContainsCACerts, onlySomeReasons, indirectCRL, onlyContainsAttributeCerts);
+
+ checkValues(point, distributionPoint, onlyContainsUserCerts, onlyContainsCACerts, onlySomeReasons, indirectCRL, onlyContainsAttributeCerts);
+
+ ASN1Sequence seq = ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(point.getEncoded()));
+
+ if (seq.size() != size)
+ {
+ fail("size mismatch");
+ }
+
+ point = IssuingDistributionPoint.getInstance(seq);
+
+ checkValues(point, distributionPoint, onlyContainsUserCerts, onlyContainsCACerts, onlySomeReasons, indirectCRL, onlyContainsAttributeCerts);
+ }
+
+ private void checkValues(IssuingDistributionPoint point, DistributionPointName distributionPoint, boolean onlyContainsUserCerts, boolean onlyContainsCACerts, ReasonFlags onlySomeReasons, boolean indirectCRL, boolean onlyContainsAttributeCerts)
+ {
+ if (point.onlyContainsUserCerts() != onlyContainsUserCerts)
+ {
+ fail("mismatch on onlyContainsUserCerts");
+ }
+
+ if (point.onlyContainsCACerts() != onlyContainsCACerts)
+ {
+ fail("mismatch on onlyContainsCACerts");
+ }
+
+ if (point.isIndirectCRL() != indirectCRL)
+ {
+ fail("mismatch on indirectCRL");
+ }
+
+ if (point.onlyContainsAttributeCerts() != onlyContainsAttributeCerts)
+ {
+ fail("mismatch on onlyContainsAttributeCerts");
+ }
+
+ if (!isEquiv(onlySomeReasons, point.getOnlySomeReasons()))
+ {
+ fail("mismatch on onlySomeReasons");
+ }
+
+ if (!isEquiv(distributionPoint, point.getDistributionPoint()))
+ {
+ fail("mismatch on distributionPoint");
+ }
+ }
+
+ private boolean isEquiv(Object o1, Object o2)
+ {
+ if (o1 == null)
+ {
+ return o2 == null;
+ }
+
+ return o1.equals(o2);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new IssuingDistributionPointUnitTest());
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/KeyUsageTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/KeyUsageTest.java
new file mode 100644
index 000000000..2de2f3d54
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/KeyUsageTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.x509.KeyUsage;
+import org.spongycastle.util.test.SimpleTest;
+
+public class KeyUsageTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "KeyUsage";
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ BitStringConstantTester.testFlagValueCorrect(0, KeyUsage.digitalSignature);
+ BitStringConstantTester.testFlagValueCorrect(1, KeyUsage.nonRepudiation);
+ BitStringConstantTester.testFlagValueCorrect(2, KeyUsage.keyEncipherment);
+ BitStringConstantTester.testFlagValueCorrect(3, KeyUsage.dataEncipherment);
+ BitStringConstantTester.testFlagValueCorrect(4, KeyUsage.keyAgreement);
+ BitStringConstantTester.testFlagValueCorrect(5, KeyUsage.keyCertSign);
+ BitStringConstantTester.testFlagValueCorrect(6, KeyUsage.cRLSign);
+ BitStringConstantTester.testFlagValueCorrect(7, KeyUsage.encipherOnly);
+ BitStringConstantTester.testFlagValueCorrect(8, KeyUsage.decipherOnly);
+
+ if (!new KeyUsage(KeyUsage.keyCertSign).hasUsages(KeyUsage.keyCertSign))
+ {
+ fail("usages bit test failed 1");
+ }
+
+ if (new KeyUsage(KeyUsage.cRLSign).hasUsages(KeyUsage.keyCertSign))
+ {
+ fail("usages bit test failed 2");
+ }
+
+ if (!new KeyUsage(KeyUsage.cRLSign | KeyUsage.decipherOnly).hasUsages(KeyUsage.cRLSign | KeyUsage.decipherOnly))
+ {
+ fail("usages bit test failed 3");
+ }
+
+ if (new KeyUsage(KeyUsage.cRLSign | KeyUsage.decipherOnly).hasUsages(KeyUsage.cRLSign | KeyUsage.decipherOnly | KeyUsage.keyCertSign))
+ {
+ fail("usages bit test failed 4");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KeyUsageTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/LDSSecurityObjectUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/LDSSecurityObjectUnitTest.java
new file mode 100644
index 000000000..0cbb44763
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/LDSSecurityObjectUnitTest.java
@@ -0,0 +1,214 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+import java.util.Random;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.icao.DataGroupHash;
+import org.spongycastle.asn1.icao.LDSSecurityObject;
+import org.spongycastle.asn1.icao.LDSVersionInfo;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.util.test.SimpleTest;
+
+public class LDSSecurityObjectUnitTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "LDSSecurityObject";
+ }
+
+ private byte[] generateHash()
+ {
+ Random rand = new Random();
+ byte[] bytes = new byte[20];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ bytes[i] = (byte)rand.nextInt();
+ }
+
+ return bytes;
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ AlgorithmIdentifier algoId = new AlgorithmIdentifier("1.3.14.3.2.26");
+ DataGroupHash[] datas = new DataGroupHash[2];
+
+ datas[0] = new DataGroupHash(1, new DEROctetString(generateHash()));
+ datas[1] = new DataGroupHash(2, new DEROctetString(generateHash()));
+
+ LDSSecurityObject so = new LDSSecurityObject(algoId, datas);
+
+ checkConstruction(so, algoId, datas);
+
+ LDSVersionInfo versionInfo = new LDSVersionInfo("Hello", "world");
+
+ so = new LDSSecurityObject(algoId, datas, versionInfo);
+
+ checkConstruction(so, algoId, datas, versionInfo);
+
+ try
+ {
+ LDSSecurityObject.getInstance(null);
+ }
+ catch (Exception e)
+ {
+ fail("getInstance() failed to handle null.");
+ }
+
+ try
+ {
+ LDSSecurityObject.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ LDSSecurityObject.getInstance(new DERSequence(v));
+
+ fail("constructor failed to detect empty sequence.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new LDSSecurityObject(algoId, new DataGroupHash[1]);
+
+ fail("constructor failed to detect small DataGroupHash array.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new LDSSecurityObject(algoId, new DataGroupHash[LDSSecurityObject.ub_DataGroups + 1]);
+
+ fail("constructor failed to out of bounds DataGroupHash array.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ LDSSecurityObject so,
+ AlgorithmIdentifier digestAlgorithmIdentifier,
+ DataGroupHash[] datagroupHash)
+ throws IOException
+ {
+ checkStatement(so, digestAlgorithmIdentifier, datagroupHash, null);
+
+ so = LDSSecurityObject.getInstance(so);
+
+ checkStatement(so, digestAlgorithmIdentifier, datagroupHash, null);
+
+ ASN1InputStream aIn = new ASN1InputStream(so.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ so = LDSSecurityObject.getInstance(seq);
+
+ checkStatement(so, digestAlgorithmIdentifier, datagroupHash, null);
+ }
+
+ private void checkConstruction(
+ LDSSecurityObject so,
+ AlgorithmIdentifier digestAlgorithmIdentifier,
+ DataGroupHash[] datagroupHash,
+ LDSVersionInfo versionInfo)
+ throws IOException
+ {
+ if (so.getVersion() != 1)
+ {
+ fail("version number not 1");
+ }
+
+ checkStatement(so, digestAlgorithmIdentifier, datagroupHash, versionInfo);
+
+ so = LDSSecurityObject.getInstance(so);
+
+ checkStatement(so, digestAlgorithmIdentifier, datagroupHash, versionInfo);
+
+ ASN1InputStream aIn = new ASN1InputStream(so.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ so = LDSSecurityObject.getInstance(seq);
+
+ checkStatement(so, digestAlgorithmIdentifier, datagroupHash, versionInfo);
+ }
+
+ private void checkStatement(
+ LDSSecurityObject so,
+ AlgorithmIdentifier digestAlgorithmIdentifier,
+ DataGroupHash[] datagroupHash,
+ LDSVersionInfo versionInfo)
+ {
+ if (digestAlgorithmIdentifier != null)
+ {
+ if (!so.getDigestAlgorithmIdentifier().equals(digestAlgorithmIdentifier))
+ {
+ fail("ids don't match.");
+ }
+ }
+ else if (so.getDigestAlgorithmIdentifier() != null)
+ {
+ fail("digest algorithm Id found when none expected.");
+ }
+
+ if (datagroupHash != null)
+ {
+ DataGroupHash[] datas = so.getDatagroupHash();
+
+ for (int i = 0; i != datas.length; i++)
+ {
+ if (!datagroupHash[i].equals(datas[i]))
+ {
+ fail("name registration authorities don't match.");
+ }
+ }
+ }
+ else if (so.getDatagroupHash() != null)
+ {
+ fail("data hash groups found when none expected.");
+ }
+
+ if (versionInfo != null)
+ {
+ if (!versionInfo.equals(so.getVersionInfo()))
+ {
+ fail("versionInfo doesn't match");
+ }
+ }
+ else if (so.getVersionInfo() != null)
+ {
+ fail("version info found when none expected.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new LDSSecurityObjectUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MiscTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MiscTest.java
new file mode 100644
index 000000000..3f1274794
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MiscTest.java
@@ -0,0 +1,113 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.misc.CAST5CBCParameters;
+import org.spongycastle.asn1.misc.IDEACBCPar;
+import org.spongycastle.asn1.misc.NetscapeCertType;
+import org.spongycastle.asn1.misc.NetscapeRevocationURL;
+import org.spongycastle.asn1.misc.VerisignCzagExtension;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class MiscTest
+ implements Test
+{
+ private boolean isSameAs(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public TestResult perform()
+ {
+ byte[] testIv = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ ASN1Encodable[] values = {
+ new CAST5CBCParameters(testIv, 128),
+ new NetscapeCertType(NetscapeCertType.smime),
+ new VerisignCzagExtension(new DERIA5String("hello")),
+ new IDEACBCPar(testIv),
+ new NetscapeRevocationURL(new DERIA5String("http://test"))
+ };
+
+ byte[] data = Base64.decode("MA4ECAECAwQFBgcIAgIAgAMCBSAWBWhlbGxvMAoECAECAwQFBgcIFgtodHRwOi8vdGVzdA==");
+
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ for (int i = 0; i != values.length; i++)
+ {
+ aOut.writeObject(values[i]);
+ }
+
+ ASN1Primitive[] readValues = new ASN1Primitive[values.length];
+
+ if (!isSameAs(bOut.toByteArray(), data))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed data check");
+ }
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+
+ for (int i = 0; i != values.length; i++)
+ {
+ ASN1Primitive o = aIn.readObject();
+ if (!values[i].equals(o))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed equality test for " + o);
+ }
+
+ if (o.hashCode() != values[i].hashCode())
+ {
+ return new SimpleTestResult(false, getName() + ": Failed hashCode test for " + o);
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": Failed - exception " + e.toString(), e);
+ }
+ }
+
+ public String getName()
+ {
+ return "Misc";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ MiscTest test = new MiscTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MonetaryLimitUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MonetaryLimitUnitTest.java
new file mode 100644
index 000000000..02a59b4ba
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MonetaryLimitUnitTest.java
@@ -0,0 +1,85 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.isismtt.x509.MonetaryLimit;
+
+import java.io.IOException;
+
+public class MonetaryLimitUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "MonetaryLimit";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ String currency = "AUD";
+ int amount = 1;
+ int exponent = 2;
+
+ MonetaryLimit limit = new MonetaryLimit(currency, amount, exponent);
+
+ checkConstruction(limit, currency, amount, exponent);
+
+ limit = MonetaryLimit.getInstance(null);
+
+ if (limit != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ MonetaryLimit.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ MonetaryLimit limit,
+ String currency,
+ int amount,
+ int exponent)
+ throws IOException
+ {
+ checkValues(limit, currency, amount, exponent);
+
+ limit = MonetaryLimit.getInstance(limit);
+
+ checkValues(limit, currency, amount, exponent);
+
+ ASN1InputStream aIn = new ASN1InputStream(limit.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ limit = MonetaryLimit.getInstance(seq);
+
+ checkValues(limit, currency, amount, exponent);
+ }
+
+ private void checkValues(
+ MonetaryLimit limit,
+ String currency,
+ int amount,
+ int exponent)
+ {
+ checkMandatoryField("currency", currency, limit.getCurrency());
+ checkMandatoryField("amount", amount, limit.getAmount().intValue());
+ checkMandatoryField("exponent", exponent, limit.getExponent().intValue());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MonetaryLimitUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MonetaryValueUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MonetaryValueUnitTest.java
new file mode 100644
index 000000000..541dc4978
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/MonetaryValueUnitTest.java
@@ -0,0 +1,88 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.x509.qualified.Iso4217CurrencyCode;
+import org.spongycastle.asn1.x509.qualified.MonetaryValue;
+import org.spongycastle.util.test.SimpleTest;
+
+public class MonetaryValueUnitTest
+ extends SimpleTest
+{
+ private static final int TEST_AMOUNT = 100;
+ private static final int ZERO_EXPONENT = 0;
+
+ private static final String CURRENCY_CODE = "AUD";
+
+ public String getName()
+ {
+ return "MonetaryValue";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ MonetaryValue mv = new MonetaryValue(new Iso4217CurrencyCode(CURRENCY_CODE), TEST_AMOUNT, ZERO_EXPONENT);
+
+ checkValues(mv, TEST_AMOUNT, ZERO_EXPONENT);
+
+ mv = MonetaryValue.getInstance(mv);
+
+ checkValues(mv, TEST_AMOUNT, ZERO_EXPONENT);
+
+ ASN1InputStream aIn = new ASN1InputStream(mv.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ mv = MonetaryValue.getInstance(seq);
+
+ checkValues(mv, TEST_AMOUNT, ZERO_EXPONENT);
+
+ mv = MonetaryValue.getInstance(null);
+
+ if (mv != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ MonetaryValue.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkValues(
+ MonetaryValue mv,
+ int amount,
+ int exponent)
+ {
+ if (mv.getAmount().intValue() != amount)
+ {
+ fail("amounts don't match.");
+ }
+
+ if (mv.getExponent().intValue() != exponent)
+ {
+ fail("exponents don't match.");
+ }
+
+ Iso4217CurrencyCode cc = mv.getCurrency();
+
+ if (!cc.getAlphabetic().equals(CURRENCY_CODE))
+ {
+ fail("currency code wrong");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MonetaryValueUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NameOrPseudonymUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NameOrPseudonymUnitTest.java
new file mode 100644
index 000000000..fdab08f67
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NameOrPseudonymUnitTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x500.DirectoryString;
+import org.spongycastle.asn1.x509.sigi.NameOrPseudonym;
+
+public class NameOrPseudonymUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "NameOrPseudonym";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ String pseudonym = "pseudonym";
+ DirectoryString surname = new DirectoryString("surname");
+ ASN1Sequence givenName = new DERSequence(new DirectoryString("givenName"));
+
+ NameOrPseudonym id = new NameOrPseudonym(pseudonym);
+
+ checkConstruction(id, pseudonym, null, null);
+
+ id = new NameOrPseudonym(surname, givenName);
+
+ checkConstruction(id, null, surname, givenName);
+
+ id = NameOrPseudonym.getInstance(null);
+
+ if (id != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ NameOrPseudonym.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ NameOrPseudonym id,
+ String pseudonym,
+ DirectoryString surname,
+ ASN1Sequence givenName)
+ throws IOException
+ {
+ checkValues(id, pseudonym, surname, givenName);
+
+ id = NameOrPseudonym.getInstance(id);
+
+ checkValues(id, pseudonym, surname, givenName);
+
+ ASN1InputStream aIn = new ASN1InputStream(id.toASN1Object().getEncoded());
+
+ if (surname != null)
+ {
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ id = NameOrPseudonym.getInstance(seq);
+ }
+ else
+ {
+ ASN1String s = (ASN1String)aIn.readObject();
+
+ id = NameOrPseudonym.getInstance(s);
+ }
+
+ checkValues(id, pseudonym, surname, givenName);
+ }
+
+ private void checkValues(
+ NameOrPseudonym id,
+ String pseudonym,
+ DirectoryString surname,
+ ASN1Sequence givenName)
+ {
+
+ if (surname != null)
+ {
+ checkMandatoryField("surname", surname, id.getSurname());
+ checkMandatoryField("givenName", givenName, new DERSequence(id.getGivenName()[0]));
+ }
+ else
+ {
+ checkOptionalField("pseudonym", new DirectoryString(pseudonym), id.getPseudonym());
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NameOrPseudonymUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NamingAuthorityUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NamingAuthorityUnitTest.java
new file mode 100644
index 000000000..321158d88
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NamingAuthorityUnitTest.java
@@ -0,0 +1,99 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.isismtt.x509.NamingAuthority;
+import org.spongycastle.asn1.x500.DirectoryString;
+
+import java.io.IOException;
+
+public class NamingAuthorityUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "NamingAuthority";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DERObjectIdentifier namingAuthorityID = new DERObjectIdentifier("1.2.3");
+ String namingAuthorityURL = "url";
+ DirectoryString namingAuthorityText = new DirectoryString("text");
+
+ NamingAuthority auth = new NamingAuthority(namingAuthorityID, namingAuthorityURL, namingAuthorityText);
+
+ checkConstruction(auth, namingAuthorityID, namingAuthorityURL, namingAuthorityText);
+
+ auth = new NamingAuthority(null, namingAuthorityURL, namingAuthorityText);
+
+ checkConstruction(auth, null, namingAuthorityURL, namingAuthorityText);
+
+ auth = new NamingAuthority(namingAuthorityID, null, namingAuthorityText);
+
+ checkConstruction(auth, namingAuthorityID, null, namingAuthorityText);
+
+ auth = new NamingAuthority(namingAuthorityID, namingAuthorityURL, null);
+
+ checkConstruction(auth, namingAuthorityID, namingAuthorityURL, null);
+
+ auth = NamingAuthority.getInstance(null);
+
+ if (auth != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ NamingAuthority.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ NamingAuthority auth,
+ DERObjectIdentifier namingAuthorityID,
+ String namingAuthorityURL,
+ DirectoryString namingAuthorityText)
+ throws IOException
+ {
+ checkValues(auth, namingAuthorityID, namingAuthorityURL, namingAuthorityText);
+
+ auth = NamingAuthority.getInstance(auth);
+
+ checkValues(auth, namingAuthorityID, namingAuthorityURL, namingAuthorityText);
+
+ ASN1InputStream aIn = new ASN1InputStream(auth.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ auth = NamingAuthority.getInstance(seq);
+
+ checkValues(auth, namingAuthorityID, namingAuthorityURL, namingAuthorityText);
+ }
+
+ private void checkValues(
+ NamingAuthority auth,
+ DERObjectIdentifier namingAuthorityId,
+ String namingAuthorityURL,
+ DirectoryString namingAuthorityText)
+ {
+ checkOptionalField("namingAuthorityId", namingAuthorityId, auth.getNamingAuthorityId());
+ checkOptionalField("namingAuthorityURL", namingAuthorityURL, auth.getNamingAuthorityUrl());
+ checkOptionalField("namingAuthorityText", namingAuthorityText, auth.getNamingAuthorityText());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NamingAuthorityUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NetscapeCertTypeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NetscapeCertTypeTest.java
new file mode 100644
index 000000000..9d3b09934
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/NetscapeCertTypeTest.java
@@ -0,0 +1,34 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.misc.NetscapeCertType;
+import org.spongycastle.util.test.SimpleTest;
+
+public class NetscapeCertTypeTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "NetscapeCertType";
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ BitStringConstantTester.testFlagValueCorrect(0, NetscapeCertType.sslClient);
+ BitStringConstantTester.testFlagValueCorrect(1, NetscapeCertType.sslServer);
+ BitStringConstantTester.testFlagValueCorrect(2, NetscapeCertType.smime);
+ BitStringConstantTester.testFlagValueCorrect(3, NetscapeCertType.objectSigning);
+ BitStringConstantTester.testFlagValueCorrect(4, NetscapeCertType.reserved);
+ BitStringConstantTester.testFlagValueCorrect(5, NetscapeCertType.sslCA);
+ BitStringConstantTester.testFlagValueCorrect(6, NetscapeCertType.smimeCA);
+ BitStringConstantTester.testFlagValueCorrect(7, NetscapeCertType.objectSigningCA);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NetscapeCertTypeTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OCSPTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OCSPTest.java
new file mode 100644
index 000000000..15b330cef
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OCSPTest.java
@@ -0,0 +1,193 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.ocsp.BasicOCSPResponse;
+import org.spongycastle.asn1.ocsp.OCSPRequest;
+import org.spongycastle.asn1.ocsp.OCSPResponse;
+import org.spongycastle.asn1.ocsp.ResponseBytes;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+import org.spongycastle.util.test.SimpleTestResult;
+
+public class OCSPTest
+ implements Test
+{
+ private byte[] unsignedReq = Base64.decode(
+ "MEIwQDA+MDwwOjAJBgUrDgMCGgUABBRDb9GODnq7lRhSkEqw4XX24huERwQUkY4j"
+ + "a6eKuDlkVP9hRgkEvIWqHPECAQE=");
+
+ private byte[] signedReq = Base64.decode(
+ "MIIC9jBAMD4wPDA6MAkGBSsOAwIaBQAEFENv0Y4OeruVGFKQSrDhdfbiG4RHBBTc"
+ + "Mr1fP+mZAxbF2ZdehWxn6mtAngIBAaCCArAwggKsMA0GCSqGSIb3DQEBBQUAA4GB"
+ + "AAzHBm4nL5AcRQB3Jkz7ScNeZF+GbRZ0p4kBDTnqi3IeESuso12yJhpqqyijdnj5"
+ + "gd4/GsSAgdluLHyYZ6wgozV7G9MDXCnFnG4PBUW05HaVX81JYAp+amVyU0NOgNrG"
+ + "90npVBsHb0o+UlkxNgMiEbSkp/TeGb6YURsYKhmwp7BgoIICFTCCAhEwggINMIIB"
+ + "dqADAgECAgEBMA0GCSqGSIb3DQEBBAUAMCUxFjAUBgNVBAoTDUJvdW5jeSBDYXN0"
+ + "bGUxCzAJBgNVBAYTAkFVMB4XDTA0MTAyNDEzNDc0M1oXDTA1MDIwMTEzNDc0M1ow"
+ + "JTEWMBQGA1UEChMNQm91bmN5IENhc3RsZTELMAkGA1UEBhMCQVUwgZ8wDQYJKoZI"
+ + "hvcNAQEBBQADgY0AMIGJAoGBAJBmLeIzthMHUeTkOeJ76iBxcMHY31o/i3a9VT12"
+ + "y2FcS/ejJmeUCMTdtwl5alOwXY66vF4DyT1VU/nJG3mHpSoqq7qrMXOIFGcXg1Wf"
+ + "oJRrQgTOLdQ6bod7i9ME/EjEJy70orh0nVS7NGcu0R5TjcbLde2J5zxjb/W9wqfy"
+ + "RovJAgMBAAGjTTBLMB0GA1UdDgQWBBTcMr1fP+mZAxbF2ZdehWxn6mtAnjAfBgNV"
+ + "HSMEGDAWgBTcMr1fP+mZAxbF2ZdehWxn6mtAnjAJBgNVHRMEAjAAMA0GCSqGSIb3"
+ + "DQEBBAUAA4GBAF/4EH1KkNrNxocJPIp7lThmG1KIVYESIadowMowrbok46ESofRF"
+ + "OIPku07W+e1Y1Y1KXLIiPMG3IGwrBrn04iLsbbBUiN37BcC/VyT4xKJ2MYscGjKL"
+ + "ua/9bU0lOyeTRAwqb8towWRd5lLYAI3RQ7dhStUTFp3Vqd803PJ/cpR6");
+
+ private byte[] response = Base64.decode(
+ "MIIFnAoBAKCCBZUwggWRBgkrBgEFBQcwAQEEggWCMIIFfjCCARehgZ8wgZwx"
+ + "CzAJBgNVBAYTAklOMRcwFQYDVQQIEw5BbmRocmEgcHJhZGVzaDESMBAGA1UE"
+ + "BxMJSHlkZXJhYmFkMQwwCgYDVQQKEwNUQ1MxDDAKBgNVBAsTA0FUQzEeMBwG"
+ + "A1UEAxMVVENTLUNBIE9DU1AgUmVzcG9uZGVyMSQwIgYJKoZIhvcNAQkBFhVv"
+ + "Y3NwQHRjcy1jYS50Y3MuY28uaW4YDzIwMDMwNDAyMTIzNDU4WjBiMGAwOjAJ"
+ + "BgUrDgMCGgUABBRs07IuoCWNmcEl1oHwIak1BPnX8QQUtGyl/iL9WJ1VxjxF"
+ + "j0hAwJ/s1AcCAQKhERgPMjAwMjA4MjkwNzA5MjZaGA8yMDAzMDQwMjEyMzQ1"
+ + "OFowDQYJKoZIhvcNAQEFBQADgYEAfbN0TCRFKdhsmvOdUoiJ+qvygGBzDxD/"
+ + "VWhXYA+16AphHLIWNABR3CgHB3zWtdy2j7DJmQ/R7qKj7dUhWLSqclAiPgFt"
+ + "QQ1YvSJAYfEIdyHkxv4NP0LSogxrumANcDyC9yt/W9yHjD2ICPBIqCsZLuLk"
+ + "OHYi5DlwWe9Zm9VFwCGgggPMMIIDyDCCA8QwggKsoAMCAQICAQYwDQYJKoZI"
+ + "hvcNAQEFBQAwgZQxFDASBgNVBAMTC1RDUy1DQSBPQ1NQMSYwJAYJKoZIhvcN"
+ + "AQkBFhd0Y3MtY2FAdGNzLWNhLnRjcy5jby5pbjEMMAoGA1UEChMDVENTMQww"
+ + "CgYDVQQLEwNBVEMxEjAQBgNVBAcTCUh5ZGVyYWJhZDEXMBUGA1UECBMOQW5k"
+ + "aHJhIHByYWRlc2gxCzAJBgNVBAYTAklOMB4XDTAyMDgyOTA3MTE0M1oXDTAz"
+ + "MDgyOTA3MTE0M1owgZwxCzAJBgNVBAYTAklOMRcwFQYDVQQIEw5BbmRocmEg"
+ + "cHJhZGVzaDESMBAGA1UEBxMJSHlkZXJhYmFkMQwwCgYDVQQKEwNUQ1MxDDAK"
+ + "BgNVBAsTA0FUQzEeMBwGA1UEAxMVVENTLUNBIE9DU1AgUmVzcG9uZGVyMSQw"
+ + "IgYJKoZIhvcNAQkBFhVvY3NwQHRjcy1jYS50Y3MuY28uaW4wgZ8wDQYJKoZI"
+ + "hvcNAQEBBQADgY0AMIGJAoGBAM+XWW4caMRv46D7L6Bv8iwtKgmQu0SAybmF"
+ + "RJiz12qXzdvTLt8C75OdgmUomxp0+gW/4XlTPUqOMQWv463aZRv9Ust4f8MH"
+ + "EJh4ekP/NS9+d8vEO3P40ntQkmSMcFmtA9E1koUtQ3MSJlcs441JjbgUaVnm"
+ + "jDmmniQnZY4bU3tVAgMBAAGjgZowgZcwDAYDVR0TAQH/BAIwADALBgNVHQ8E"
+ + "BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwkwNgYIKwYBBQUHAQEEKjAoMCYG"
+ + "CCsGAQUFBzABhhpodHRwOi8vMTcyLjE5LjQwLjExMDo3NzAwLzAtBgNVHR8E"
+ + "JjAkMCKgIKAehhxodHRwOi8vMTcyLjE5LjQwLjExMC9jcmwuY3JsMA0GCSqG"
+ + "SIb3DQEBBQUAA4IBAQB6FovM3B4VDDZ15o12gnADZsIk9fTAczLlcrmXLNN4"
+ + "PgmqgnwF0Ymj3bD5SavDOXxbA65AZJ7rBNAguLUo+xVkgxmoBH7R2sBxjTCc"
+ + "r07NEadxM3HQkt0aX5XYEl8eRoifwqYAI9h0ziZfTNes8elNfb3DoPPjqq6V"
+ + "mMg0f0iMS4W8LjNPorjRB+kIosa1deAGPhq0eJ8yr0/s2QR2/WFD5P4aXc8I"
+ + "KWleklnIImS3zqiPrq6tl2Bm8DZj7vXlTOwmraSQxUwzCKwYob1yGvNOUQTq"
+ + "pG6jxn7jgDawHU1+WjWQe4Q34/pWeGLysxTraMa+Ug9kPe+jy/qRX2xwvKBZ");
+
+ private boolean isSameAs(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private TestResult unsignedRequest()
+ {
+ try
+ {
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(unsignedReq));
+ OCSPRequest req = OCSPRequest.getInstance(aIn.readObject());
+
+ if (!isSameAs(req.getEncoded(), unsignedReq))
+ {
+ return new SimpleTestResult(false, getName() + ": OCSP unsigned request failed to re-encode");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed unsigned exception - " + e.toString(), e);
+ }
+ }
+
+ private TestResult signedRequest()
+ {
+ try
+ {
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(signedReq));
+ OCSPRequest req = OCSPRequest.getInstance(aIn.readObject());
+
+ if (!isSameAs(req.getEncoded(), signedReq))
+ {
+ return new SimpleTestResult(false, getName() + ": OCSP signed request failed to re-encode");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed signed exception - " + e.toString(), e);
+ }
+ }
+
+ private TestResult response()
+ {
+ try
+ {
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(response));
+ OCSPResponse resp = OCSPResponse.getInstance(aIn.readObject());
+ ResponseBytes rBytes = ResponseBytes.getInstance(resp.getResponseBytes());
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(rBytes.getResponse().getOctets()));
+
+ BasicOCSPResponse bResp = BasicOCSPResponse.getInstance(aIn.readObject());
+
+ resp = new OCSPResponse(resp.getResponseStatus(), new ResponseBytes(rBytes.getResponseType(), new DEROctetString(bResp.getEncoded())));
+
+ if (!isSameAs(resp.getEncoded(), response))
+ {
+ return new SimpleTestResult(false, getName() + ": OCSP response failed to re-encode");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed response exception - " + e.toString(), e);
+ }
+ }
+
+ public TestResult perform()
+ {
+ TestResult res = unsignedRequest();
+
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+
+ res = signedRequest();
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+
+ return response();
+ }
+
+ public String getName()
+ {
+ return "OCSP";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ OCSPTest test = new OCSPTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OIDTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OIDTest.java
new file mode 100644
index 000000000..209f048d6
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OIDTest.java
@@ -0,0 +1,166 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+
+/**
+ * X.690 test example
+ */
+public class OIDTest
+ extends SimpleTest
+{
+ byte[] req1 = Hex.decode("0603813403");
+ byte[] req2 = Hex.decode("06082A36FFFFFFDD6311");
+
+ public String getName()
+ {
+ return "OID";
+ }
+
+ private void recodeCheck(
+ String oid,
+ byte[] enc)
+ throws IOException
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(enc);
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+
+ DERObjectIdentifier o = new DERObjectIdentifier(oid);
+ DERObjectIdentifier encO = (DERObjectIdentifier)aIn.readObject();
+
+ if (!o.equals(encO))
+ {
+ fail("oid ID didn't match", o, encO);
+ }
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(o);
+
+ byte[] bytes = bOut.toByteArray();
+
+ if (bytes.length != enc.length)
+ {
+ fail("failed length test");
+ }
+
+ for (int i = 0; i != enc.length; i++)
+ {
+ if (bytes[i] != enc[i])
+ {
+ fail("failed comparison test", new String(Hex.encode(enc)), new String(Hex.encode(bytes)));
+ }
+ }
+ }
+
+ private void validOidCheck(
+ String oid)
+ throws IOException
+ {
+ DERObjectIdentifier o = new DERObjectIdentifier(oid);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(o);
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+
+ o = (DERObjectIdentifier)aIn.readObject();
+
+ if (!o.getId().equals(oid))
+ {
+ fail("failed oid check for " + oid);
+ }
+ }
+
+ private void invalidOidCheck(
+ String oid)
+ {
+ try
+ {
+ new DERObjectIdentifier(oid);
+ fail("failed to catch bad oid: " + oid);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void branchCheck(String stem, String branch)
+ {
+ String expected = stem + "." + branch;
+ String actual = new ASN1ObjectIdentifier(stem).branch(branch).getId();
+
+ if (!expected.equals(actual))
+ {
+ fail("failed 'branch' check for " + stem + "/" + branch);
+ }
+ }
+
+ private void onCheck(String stem, String test, boolean expected)
+ {
+ if (expected != new ASN1ObjectIdentifier(test).on(new ASN1ObjectIdentifier(stem)))
+ {
+ fail("failed 'on' check for " + stem + "/" + test);
+ }
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ recodeCheck("2.100.3", req1);
+ recodeCheck("1.2.54.34359733987.17", req2);
+
+ validOidCheck(PKCSObjectIdentifiers.pkcs_9_at_contentType.getId());
+ validOidCheck("0.1");
+ validOidCheck("1.1.127.32512.8323072.2130706432.545460846592.139637976727552.35747322042253312.9151314442816847872");
+ validOidCheck("1.2.123.12345678901.1.1.1");
+ validOidCheck("2.25.196556539987194312349856245628873852187.1");
+
+ invalidOidCheck("0");
+ invalidOidCheck("1");
+ invalidOidCheck("2");
+ invalidOidCheck("3.1");
+ invalidOidCheck("..1");
+ invalidOidCheck("192.168.1.1");
+ invalidOidCheck(".123452");
+ invalidOidCheck("1.");
+ invalidOidCheck("1.345.23.34..234");
+ invalidOidCheck("1.345.23.34.234.");
+ invalidOidCheck(".12.345.77.234");
+ invalidOidCheck(".12.345.77.234.");
+ invalidOidCheck("1.2.3.4.A.5");
+ invalidOidCheck("1,2");
+
+ branchCheck("1.1", "2.2");
+
+ onCheck("1.1", "1.1", false);
+ onCheck("1.1", "1.2", false);
+ onCheck("1.1", "1.2.1", false);
+ onCheck("1.1", "2.1", false);
+ onCheck("1.1", "1.11", false);
+ onCheck("1.12", "1.1.2", false);
+ onCheck("1.1", "1.1.1", true);
+ onCheck("1.1", "1.1.2", true);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new OIDTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ObjectIdentifierTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ObjectIdentifierTest.java
new file mode 100644
index 000000000..ed0a60238
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ObjectIdentifierTest.java
@@ -0,0 +1,38 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.util.test.SimpleTest;
+import org.spongycastle.util.test.TestResult;
+
+public class ObjectIdentifierTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ObjectIdentifier";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ // exercise the object cache
+ for (int i = 0; i < 1024; i++)
+ {
+ for (int j = 0; j != 17000; j++)
+ {
+ byte[] encoded = new ASN1ObjectIdentifier("1.1." + i + "." + j).getEncoded();
+
+ ASN1ObjectIdentifier.getInstance(encoded);
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ ObjectIdentifierTest test = new ObjectIdentifierTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OctetStringTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OctetStringTest.java
new file mode 100644
index 000000000..19dab4a1f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OctetStringTest.java
@@ -0,0 +1,203 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.spongycastle.asn1.ASN1OctetStringParser;
+import org.spongycastle.asn1.ASN1SequenceParser;
+import org.spongycastle.asn1.ASN1StreamParser;
+import org.spongycastle.asn1.BEROctetStringGenerator;
+import org.spongycastle.asn1.BERSequenceGenerator;
+import org.spongycastle.asn1.BERTags;
+import org.spongycastle.asn1.DERInteger;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.DERSequenceGenerator;
+import org.spongycastle.asn1.cms.CMSObjectIdentifiers;
+import org.spongycastle.asn1.cms.CompressedDataParser;
+import org.spongycastle.asn1.cms.ContentInfoParser;
+
+public class OctetStringTest
+ extends TestCase
+{
+ public void testReadingWriting()
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BEROctetStringGenerator octGen = new BEROctetStringGenerator(bOut);
+
+ OutputStream out = octGen.getOctetOutputStream();
+
+ out.write(new byte[] { 1, 2, 3, 4 });
+ out.write(new byte[4]);
+
+ out.close();
+
+ ASN1StreamParser aIn = new ASN1StreamParser(bOut.toByteArray());
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)aIn.readObject();
+
+ InputStream in = s.getOctetStream();
+ int count = 0;
+
+ while (in.read() >= 0)
+ {
+ count++;
+ }
+
+ assertEquals(8, count);
+ }
+
+ public void testReadingWritingZeroInLength()
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BEROctetStringGenerator octGen = new BEROctetStringGenerator(bOut);
+
+ OutputStream out = octGen.getOctetOutputStream();
+
+ out.write(new byte[] { 1, 2, 3, 4 });
+ out.write(new byte[512]); // forces a zero to appear in length
+
+ out.close();
+
+ ASN1StreamParser aIn = new ASN1StreamParser(bOut.toByteArray());
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)aIn.readObject();
+
+ InputStream in = s.getOctetStream();
+ int count = 0;
+
+ while (in.read() >= 0)
+ {
+ count++;
+ }
+
+ assertEquals(516, count);
+ }
+
+ public void testReadingWritingNested()
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BERSequenceGenerator sGen = new BERSequenceGenerator(bOut);
+ BEROctetStringGenerator octGen = new BEROctetStringGenerator(sGen.getRawOutputStream());
+
+ OutputStream out = octGen.getOctetOutputStream();
+
+ BERSequenceGenerator inSGen = new BERSequenceGenerator(out);
+
+ BEROctetStringGenerator inOctGen = new BEROctetStringGenerator(inSGen.getRawOutputStream());
+
+ OutputStream inOut = inOctGen.getOctetOutputStream();
+
+ inOut.write(new byte[] { 1, 2, 3, 4 });
+ inOut.write(new byte[10]);
+
+ inOut.close();
+
+ inSGen.close();
+
+ out.close();
+
+ sGen.close();
+
+ ASN1StreamParser aIn = new ASN1StreamParser(bOut.toByteArray());
+
+ ASN1SequenceParser sq = (ASN1SequenceParser)aIn.readObject();
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)sq.readObject();
+
+ ASN1StreamParser aIn2 = new ASN1StreamParser(s.getOctetStream());
+
+ ASN1SequenceParser sq2 = (ASN1SequenceParser)aIn2.readObject();
+
+ ASN1OctetStringParser inS = (ASN1OctetStringParser)sq2.readObject();
+
+ InputStream in = inS.getOctetStream();
+ int count = 0;
+
+ while (in.read() >= 0)
+ {
+ count++;
+ }
+
+ assertEquals(14, count);
+ }
+
+ public void testNestedStructure()
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ BERSequenceGenerator sGen = new BERSequenceGenerator(bOut);
+
+ sGen.addObject(new DERObjectIdentifier(CMSObjectIdentifiers.compressedData.getId()));
+
+ BERSequenceGenerator cGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);
+
+ cGen.addObject(new DERInteger(0));
+
+ //
+ // AlgorithmIdentifier
+ //
+ DERSequenceGenerator algGen = new DERSequenceGenerator(cGen.getRawOutputStream());
+
+ algGen.addObject(new DERObjectIdentifier("1.2"));
+
+ algGen.close();
+
+ //
+ // Encapsulated ContentInfo
+ //
+ BERSequenceGenerator eiGen = new BERSequenceGenerator(cGen.getRawOutputStream());
+
+ eiGen.addObject(new DERObjectIdentifier("1.1"));
+
+ BEROctetStringGenerator octGen = new BEROctetStringGenerator(eiGen.getRawOutputStream(), 0, true);
+
+ //
+ // output containing zeroes
+ //
+ OutputStream out = octGen.getOctetOutputStream();
+
+ out.write(new byte[] { 1, 2, 3, 4 });
+ out.write(new byte[4]);
+ out.write(new byte[20]);
+
+ out.close();
+ eiGen.close();
+ cGen.close();
+ sGen.close();
+
+ //
+ // reading back
+ //
+ ASN1StreamParser aIn = new ASN1StreamParser(bOut.toByteArray());
+
+ ContentInfoParser cp = new ContentInfoParser((ASN1SequenceParser)aIn.readObject());
+
+ CompressedDataParser comData = new CompressedDataParser((ASN1SequenceParser)cp.getContent(BERTags.SEQUENCE));
+ ContentInfoParser content = comData.getEncapContentInfo();
+
+ ASN1OctetStringParser bytes = (ASN1OctetStringParser)content.getContent(BERTags.OCTET_STRING);
+
+ InputStream in = bytes.getOctetStream();
+ int count = 0;
+
+ while (in.read() >= 0)
+ {
+ count++;
+ }
+
+ assertEquals(28, count);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(OctetStringTest.class);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OtherCertIDUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OtherCertIDUnitTest.java
new file mode 100644
index 000000000..6d3e36e42
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OtherCertIDUnitTest.java
@@ -0,0 +1,97 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.ess.OtherCertID;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.IssuerSerial;
+import org.spongycastle.asn1.x509.X509Name;
+
+public class OtherCertIDUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "OtherCertID";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(new DERObjectIdentifier("1.2.2.3"));
+ byte[] digest = new byte[20];
+ IssuerSerial issuerSerial = new IssuerSerial(new GeneralNames(new GeneralName(new X509Name("CN=test"))), new ASN1Integer(1));
+
+ OtherCertID certID = new OtherCertID(algId, digest);
+
+ checkConstruction(certID, algId, digest, null);
+
+ certID = new OtherCertID(algId, digest, issuerSerial);
+
+ checkConstruction(certID, algId, digest, issuerSerial);
+
+ certID = OtherCertID.getInstance(null);
+
+ if (certID != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ OtherCertID.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ OtherCertID certID,
+ AlgorithmIdentifier algId,
+ byte[] digest,
+ IssuerSerial issuerSerial)
+ throws IOException
+ {
+ checkValues(certID, algId, digest, issuerSerial);
+
+ certID = OtherCertID.getInstance(certID);
+
+ checkValues(certID, algId, digest, issuerSerial);
+
+ ASN1InputStream aIn = new ASN1InputStream(certID.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ certID = OtherCertID.getInstance(seq);
+
+ checkValues(certID, algId, digest, issuerSerial);
+ }
+
+ private void checkValues(
+ OtherCertID certID,
+ AlgorithmIdentifier algId,
+ byte[] digest,
+ IssuerSerial issuerSerial)
+ {
+ checkMandatoryField("algorithmHash", algId, certID.getAlgorithmHash());
+ checkMandatoryField("certHash", digest, certID.getCertHash());
+
+ checkOptionalField("issuerSerial", issuerSerial, certID.getIssuerSerial());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new OtherCertIDUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OtherSigningCertificateUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OtherSigningCertificateUnitTest.java
new file mode 100644
index 000000000..b92451a4a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/OtherSigningCertificateUnitTest.java
@@ -0,0 +1,86 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.ess.OtherCertID;
+import org.spongycastle.asn1.ess.OtherSigningCertificate;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+
+import java.io.IOException;
+
+public class OtherSigningCertificateUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "OtherSigningCertificate";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(new DERObjectIdentifier("1.2.2.3"));
+ byte[] digest = new byte[20];
+ OtherCertID otherCertID = new OtherCertID(algId, digest);
+
+ OtherSigningCertificate otherCert = new OtherSigningCertificate(otherCertID);
+
+ checkConstruction(otherCert, otherCertID);
+
+ otherCert = OtherSigningCertificate.getInstance(null);
+
+ if (otherCert != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ OtherCertID.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ OtherSigningCertificate otherCert,
+ OtherCertID otherCertID)
+ throws IOException
+ {
+ checkValues(otherCert, otherCertID);
+
+ otherCert = OtherSigningCertificate.getInstance(otherCert);
+
+ checkValues(otherCert, otherCertID);
+
+ ASN1InputStream aIn = new ASN1InputStream(otherCert.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ otherCert = OtherSigningCertificate.getInstance(seq);
+
+ checkValues(otherCert, otherCertID);
+ }
+
+ private void checkValues(
+ OtherSigningCertificate otherCert,
+ OtherCertID otherCertID)
+ {
+ if (otherCert.getCerts().length != 1)
+ {
+ fail("getCerts() length wrong");
+ }
+ checkMandatoryField("getCerts()[0]", otherCertID, otherCert.getCerts()[0]);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new OtherSigningCertificateUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKCS10Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKCS10Test.java
new file mode 100644
index 000000000..5aaff0bba
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKCS10Test.java
@@ -0,0 +1,101 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.pkcs.CertificationRequest;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class PKCS10Test
+ implements Test
+{
+ byte[] req1 = Base64.decode(
+ "MIHoMIGTAgEAMC4xDjAMBgNVBAMTBVRlc3QyMQ8wDQYDVQQKEwZBbmFUb20xCzAJBgNVBAYTAlNF"
+ + "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALlEt31Tzt2MlcOljvacJgzQVhmlMoqAOgqJ9Pgd3Gux"
+ + "Z7/WcIlgW4QCB7WZT21O1YoghwBhPDMcNGrHei9kHQkCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA0EA"
+ + "NDEI4ecNtJ3uHwGGlitNFq9WxcoZ0djbQJ5hABMotav6gtqlrwKXY2evaIrsNwkJtNdwwH18aQDU"
+ + "KCjOuBL38Q==");
+
+ byte[] req2 = Base64.decode(
+ "MIIB6TCCAVICAQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRQwEgYDVQQH"
+ + "EwtTYW50YSBDbGFyYTEMMAoGA1UEChMDQUJCMVEwTwYDVQQLHEhQAAAAAAAAAG8AAAAAAAAAdwAA"
+ + "AAAAAABlAAAAAAAAAHIAAAAAAAAAIAAAAAAAAABUAAAAAAAAABxIAAAAAAAARAAAAAAAAAAxDTAL"
+ + "BgNVBAMTBGJsdWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANETRZ+6occCOrFxNhfKIp4C"
+ + "mMkxwhBNb7TnnahpbM9O0r4hrBPcfYuL7u9YX/jN0YNUP+/CiT39HhSe/bikaBPDEyNsl988I8vX"
+ + "piEdgxYq/+LTgGHbjRsRYCkPtmzwBbuBldNF8bV7pu0v4UScSsExmGqqDlX1TbPU8KkPU1iTAgMB"
+ + "AAGgADANBgkqhkiG9w0BAQQFAAOBgQAFbrs9qUwh93CtETk7DeUD5HcdCnxauo1bck44snSV6MZV"
+ + "OCIGaYu1501kmhEvAtVVRr6SEHwimfQDDIjnrWwYsEr/DT6tkTZAbfRd3qUu3iKjT0H0vlUZp0hJ"
+ + "66mINtBM84uZFBfoXiWY8M3FuAnGmvy6ah/dYtJorTxLKiGkew==");
+
+ public String getName()
+ {
+ return "PKCS10";
+ }
+
+ public TestResult pkcs10Test(
+ String testName,
+ byte[] req)
+ {
+ try
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(req);
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+
+ CertificationRequest r = new CertificationRequest((ASN1Sequence)aIn.readObject());
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(r.toASN1Primitive());
+
+ byte[] bytes = bOut.toByteArray();
+
+ if (bytes.length != req.length)
+ {
+ return new SimpleTestResult(false, getName() + ": " + testName + " failed length test");
+ }
+
+ for (int i = 0; i != req.length; i++)
+ {
+ if (bytes[i] != req[i])
+ {
+ return new SimpleTestResult(false, getName() + ": " + testName + " failed comparison test");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": Exception - " + testName + " " + e.toString());
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public TestResult perform()
+ {
+ TestResult res = pkcs10Test("basic CR", req1);
+
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+
+ return pkcs10Test("Universal CR", req2);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ Test test = new PKCS10Test();
+
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKCS12Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKCS12Test.java
new file mode 100644
index 000000000..0ad0a26ea
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKCS12Test.java
@@ -0,0 +1,227 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.BEROctetString;
+import org.spongycastle.asn1.DLSequence;
+import org.spongycastle.asn1.pkcs.AuthenticatedSafe;
+import org.spongycastle.asn1.pkcs.ContentInfo;
+import org.spongycastle.asn1.pkcs.EncryptedData;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.MacData;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.Pfx;
+import org.spongycastle.asn1.pkcs.SafeBag;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.DigestInfo;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTest;
+
+public class PKCS12Test
+ extends SimpleTest
+{
+ byte[] pkcs12 = Base64.decode(
+ "MIACAQMwgAYJKoZIhvcNAQcBoIAkgASCA+gwgDCABgkqhkiG9w0BBwGggCSA"
+ + "BIIDRDCCA0AwggM8BgsqhkiG9w0BDAoBAqCCArEwggKtMCcGCiqGSIb3DQEM"
+ + "AQMwGQQUFlnNVpQoEHc+J3UEGxARipkHu5kCAWQEggKAAH9tmy40lly6QDoc"
+ + "1TfmY9y2qysD+lrgk+dnxP04RfoJfycTRDeaz2sPLImZtio9nsqCFqtzU/sl"
+ + "eWigbH34BpKU1sC0Gq1cyik0GO65sW95S6YjKtGcGOBfQCPk1oQjfiqnfU3G"
+ + "oeOaG3COQJukMFj8unv55u0xbX1hwO8SsZmr9RjPzLrVaeY6BP5+CCzOKBaj"
+ + "GxneIDqnQW7/kBIVWK7M+JXGdgQyiKhD6NvXL/zD8oKEne0nIX7IokQuWEn6"
+ + "8Sglv5OSclsSdvHTk57bCuV5lVzoIzczA4J/LZWdrtITeVefBLQSalBzpRde"
+ + "rSTMj485z2x5ChizhjE627/KQ5vkKQkQVqXYYXVyeTvKZRpL7vz13C4DUCwN"
+ + "im1XvNSCNebXS1yHJRtcONDhGJN3UsrVjHr+2kCfE5SCEeSU/dqgNLuLa1tk"
+ + "5+jwZFNj/HjO88wlOwPCol1uuJjDpaEW7dxu5qsVSfZhEXWHs8rZAMttFMzi"
+ + "yxsEkZe8kqngRbNJOY6KpppYedsMWDusUJGfIHo+8zymiw3gv/z+lmFOlDGt"
+ + "CKMk9Es/MgfjpbfhbTVYHOBKS6Qyrz7LdTuBMI8XdsZMuN+Uf73690ggLmKW"
+ + "IELUg8h1RX0ra2n6jOc/1rnebAifMhiMkL1ABQvqOobfOrG/9h9XcXoi64Qr"
+ + "htc3T7yMAHafBX5KUcNkbcn6kssYhpvd8bPADoLBnbx3GxGh/uziB0zKQEI0"
+ + "GnaY4SL7aR4C5xNNi41lYtsR6ohKyfPEGslhrhd4axx0cKxC2sHgVl0k+r8B"
+ + "8Vu44XHbW8LqdspjOHN9qg2erES1Dvgj05SfHDup+V6a3ogJo2YKXOiu3DF4"
+ + "MFEGCSqGSIb3DQEJFDFEHkIARABhAHYAaQBkACAARwAuACAASABvAG8AawAn"
+ + "AHMAIABWAGUAcgBpAFMAaQBnAG4ALAAgAEkAbgBjAC4AIABJAEQwIwYJKoZI"
+ + "hvcNAQkVMRYEFKEcMJ798oZLFkH0OnpbUBnrTLgWAAAAAAAAMIAGCSqGSIb3"
+ + "DQEHBqCAMIACAQAwgAYJKoZIhvcNAQcBMCcGCiqGSIb3DQEMAQYwGQQUTErH"
+ + "kWZ8nBXZYWO53FH4yqRZZsECAWSggASCDGCreuCr6/azcOv5w04bN3jkg4G2"
+ + "dsvTPAjL8bichaEOQCykhuNPt1dv3FsjUsdFC550K0+Y48RyBIID6JTiN9Gj"
+ + "K+a5aLPaXgTRdY74Toof1hYtZ4DIcVyq25LezVQHoe/++pAgEpWjqHTxVDIv"
+ + "YFAgT2oDB+2vkeXM61XnNWOjwCY3pXpk/VGjyN4USkD7Q/Y6tPjQOywvQE7c"
+ + "Ab1z62k9iMia7Yk/qmh+zJu4SSneo0/RLLdMZOlGZv89MResVG038TC8MTA9"
+ + "Uf+wDRcS20d7XDbTaBAgju8TpFIw5/lbDi0feUVlk6L+jkT1ktaTc1Pwtxn7"
+ + "psXMFW6HAWB4exOi09297R9BCOQX6vcetK/iA/3jIC6NuTdizYof0DWetdGy"
+ + "haIkMiEnERYE3unJocH4fq585Rw6mE+BYssPVPkVWZZInF3l69bKduuxsQt+"
+ + "pcApgBVsTjsU+1FOiUxuW2wWKi70RcQprPv5Ef1A5FRNxPFp+7IzLNlE4qCo"
+ + "wvC6NTpeuRw3aGsXSfqHmSddrHugNPmghNgG5lv1Ef7A8MUuyp8fyjAgxCDk"
+ + "4Hpb8PCHGj5t//Fr6Cd0MygJMIFQmv4kUd2LVHxQ9A9WFNCqTz/nBe+ZRLJL"
+ + "NghTv6gGpjGJiBnXYv6Sod2fs+5J2GIvex4qbdh6gzZIU2YTAwpj6Aca3SjA"
+ + "X8+m8AXt2SC3Z6T5+m8SxyiNp2P511paV/TZKtLWXQGKeEX1JXhQkaM6Q5W/"
+ + "IhSgC8/gppk1gbIraBqrW8bEnGBnC03wi0OnMz3ohM4CVHyaW3dQquT2+u6F"
+ + "8VeGXAYHU022NkrpPl/VlfNNEAyisU2+oJqpPZkqL6FsDWF3k6Fq2jXBLL+/"
+ + "a0WA82jIpgjNeXze/cgoHtU023V9E9Qcu+5nPBYdCTR4sRxvHLANii0W8lPv"
+ + "tvU5XO1UsEjHDfKL4E1bhGzGpb/OU5yg/98EN95r/xdFL5G+XVyHeR0UtkcB"
+ + "IuvyBdhkwoprCjkcgLZe8FPIBNw84HRe7Ye6f2gDW/F5uej6rBehJS1VFvCh"
+ + "DXzkajGmK40Gc2APS1/1vZqPu68polgw9dT84rem36PLEOq4KuU7n4QE0g7T"
+ + "YR2G8+4FNgQTjjg/qw3lX+sj6yLn1lYt1dOVvkiM8i8tdZg/3pCKKAW1uV7a"
+ + "astlBxVSkFfn1BrFTc2oFGkTrlUg90a+parOfGHTfDiaHX8ouEg63fk0+Xdi"
+ + "FCarXsqHNPDbpmWLKw8TAmdeneGipyScntJJk4ajy+jROQBgGew3ofOmfkqm"
+ + "oJFNwUvKOXN2ucViLZgsdK/7YgV1OR7oiTh8knQNPk3d5fRYSMFf9GJTjQRV"
+ + "y2CLdICAVzvrUXf9k7miWYkjIp2/HGD7pOH018sX9MrpfJKqvdPFOssZiFd0"
+ + "I2FUbgcEggPotvnT0XoabEiurTm8EPPpw66NKmK/H1kQL0hEtdIazPxfLmm/"
+ + "ZUDokwa7d4bE3BwFh0weQfEvMzJu6Y5E7ir2MqD33XaGMOGys1nst1SPPyDB"
+ + "WpOWD9w7Ng3yU1JVzqFWuVXaXDYbfnlG7AGevKF5PYNZj/RIQBBf5Xle9hTd"
+ + "c9CtxPkrsJwA8DeAwKl2WIfbXGzAYLSnXoYUcoTkWn/O81BlUFgAXv80gLe8"
+ + "NUrH7bhsnyGaPY953NyDk8IWUYrsn/sXvxTy5B0/7/WGMh3CSZrLX3p7TcFY"
+ + "yBrL6SRas4q9rrcwuhBq0tUUbbgWi92nhZl4bOGmx7ehHnwuUId2HWXyVGoB"
+ + "qToee/2E4PZFxSZwKCY6dahswFq5QGDrQKN2/qpOLZcJib6SvSGyEZl2pqr0"
+ + "lqk7tVPzBkN/4uP0qrcbZCDbGW6IXwu3RGMRehqj/HEJcs92lZKfVrk/U07X"
+ + "MBAiQHqV+kLw7kStECR/MGJG1c0xhqqBrf0W74+LpJiv/Q9iFNdWbXvE/cAk"
+ + "G7+OTUABd2kI88uA43T0UoRuPOi5KnLuD3AG+7IuyGyP69Xncd4u0srMg2fn"
+ + "DiLLZUy6vWmxwRFsSMCEfQNLtZaggukoPIihQvbX3mQS9izwLs6D89WtEcZ5"
+ + "6DVbIlUqUinnNKsT8vW1DZo5FMJkUxB666YIPVmkQbbJOEUU89dZg5Gw0og6"
+ + "rn4irEr4xHFdx+S7iqJXhzs9THg/9e4/k8KQ136z7LALOqDookcSdBzW6H8c"
+ + "STjs4qKQyNimsLB90mEuIEApzhseAaLFl+kgORGJv/2a+uoukZchMsJ98MVo"
+ + "sEPS1oBXJl2m9AshkWfON2GDeJatgcw6CyC1mSx++Gg602ZKUZZUaWxkz1Sw"
+ + "zTj3nhiJe+SZsdfxhsojNq7zfxqgY/Rq7BwvphU3StjnxvkB4rTkbmbiGOBO"
+ + "cvTFg4yOtQGRcifk2/XH/bgYiPqQrYSXpO3WRASV005RaSGufcpTtj3YlHGe"
+ + "8FUgZfDtfiGezhNET9KO3/Q0i34bGEpoIb/9uOWH4ZHULIlfdSm1ynV50nE4"
+ + "mJTXccrF6BE80KZI5GWGhqXdfPFaHTK1S20+XCw7bRJCGeiwVxvGfB+C0SZ4"
+ + "ndtqx165dKG5JwFukcygiIZN6foh0/PhwzmFxmPtZuPQt9dtuIQ35Y7PSDsy"
+ + "IH2Ot0Hh0YIN99lHJ6n9HomSjpwcgDXGssEuevbpz27u/MI/Uhq4Gfx0k5RF"
+ + "0pcRYtk1dYSx44a+8WgqZLF8DUNtyjSE/H8P5iGa6tqOl7kNyeeEkfoTtKst"
+ + "asGFwL4Qxxus4GC7repyVi7IJgSCA+iopiqKQJ2IqUHvoIEuD//sZooDx0Je"
+ + "oFRO5VakkTO6WHd8JpOOEU2f6Zjg++HdIl0QK7xcUaRH075LzEfqgn1vyw6J"
+ + "N6ex8D76sf/nAy01NvDPij48Z50XDwXu4kJGJvv0AJwId8BpjziBF0j3K/DI"
+ + "YOOpd6nW4EvdivCgaCnxqlIU/u1OP4BwpO+AUjJh6RKlKviGihQpi103DFhR"
+ + "yXNDhh55pqgCCCuNeEB+ovRt7UxzlGAVRSxJh1Zbjp/+iQun0E32RlSR4Diz"
+ + "p5vDk8NBZpIiKRqI+8GWZc3G1igp7dvViTLw4OdWMKwhccV5+3Ll/W72aNVm"
+ + "azYUoYOVn+OYS1NJkER0tjFOCozRGm5hfkxGlP+02wbH5uu/AQoJMqWIxT6l"
+ + "46IWC24lmAnDCXuM+gWmwUvyXLwuBdejVK8iG1Lnfg1qztoLpYRbBROgRdpt"
+ + "2cbPRm+9seqrth3eJbtmxCvuh3bZ3pR2e0/r5Tob/fDcOc5Kp+j4ndXWkwpa"
+ + "OuH1yxam7zNJR+mcYp1Wiujia5qIeY1QCAEY5QgAWaSHtjlEprwUuootA2Xm"
+ + "V7D8Vsr9BValhm9zMKj6IzsPmM+HZJWlhHcoucuAmPK6Lnys3Kv/mbkSgNOq"
+ + "fJDY901veFfKeqiCbAm6hZjNWoQDNJKFhjXUALrcOv9VCFPA3bMW3Xul/sB4"
+ + "Mq595e+x/1HkNOgZorBv97C6X7ENVDaAFcyZvrRU/ZeDnvFhisfxS4EJhzxl"
+ + "cWWnQhzD+ur1FTTlkmUFzgoB/rW+i3XigiHOuRRnkcoMy1uV17rwH8eELHJu"
+ + "Yni5vu2QUaD4jNEhliE2XCsn8Sm6bcXnfzBa7FXC39QvAcdJHzqcD6iIwjIz"
+ + "hKLu+/XoWFMFFNsgV78AwzPAn6TRya8LLCYPoIZkEP4qBoeZtUZ8PIS/Y7M9"
+ + "QStMwa/NI9SPswb3iScTGvor/obUEQS4QM6mVxFMpQWfwJfyU6jingX4EHRE"
+ + "mqvZ3ehzU8ZLOdKzRKuk022YDT7hwEQ+VL0Fg0Ld9oexqT96nQpUTHZtDRMV"
+ + "iTuJoUYTneDs2c9tsY4mWBqamZQSfTegj4sLMZagkuSUp/SpPM2zSGuD3nY6"
+ + "u3553gIM9jYhvLBEXwjGudVCwMd3bqo/4EhnKb2PcwUzdaMkipQlNteHZjBT"
+ + "1ici63xjJva+di0qTV+W9cyYyHwg1927X2qcMh06BhbHlcXQKbgmbL18KJEt"
+ + "K+GGhGNkP7mtPyHHgBb6vref/z8p7oxT2CG+oBuN/z+xQoYfe9c4IC3e/kNN"
+ + "DIoyYvPyEzAdfMS2aL8qDxzc5GH9UE9kcusJ/2dNEFTzBH2GK1CItL3IACv/"
+ + "LwX1SkI0w7oIQTL127CSnuTrUUkvJ/+rOYScQTMD/ntZPdLdu2ffszg3SzhN"
+ + "ELgojK8ss1OBlruWRHw/fP736Nx8MNsuOvXMnO8lruz+uyuEhF3BLv96oTcg"
+ + "XVHdWhPmOoqNdBQdRgAAAAAAAAAAAAAAAAAAAAAAADA8MCEwCQYFKw4DAhoF"
+ + "AAQUJMZn7MEKv4vW/+voCVyHBa6B0EMEFJOzH/BEjRtNNsZWlo/4L840aE5r"
+ + "AgFkAAA=");
+
+ public void performTest()
+ throws Exception
+ {
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(pkcs12));
+ ASN1Sequence obj = (ASN1Sequence)aIn.readObject();
+ Pfx bag = Pfx.getInstance(obj);
+ ContentInfo info = bag.getAuthSafe();
+ MacData mData = bag.getMacData();
+ DigestInfo dInfo = mData.getMac();
+ AlgorithmIdentifier algId = dInfo.getAlgorithmId();
+ byte[] salt = mData.getSalt();
+ int itCount = mData.getIterationCount().intValue();
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(((ASN1OctetString)info.getContent()).getOctets()));
+
+ AuthenticatedSafe authSafe = AuthenticatedSafe.getInstance(aIn.readObject());
+ ContentInfo[] c = authSafe.getContentInfo();
+
+ //
+ // private key section
+ //
+ if (!c[0].getContentType().equals(PKCSObjectIdentifiers.data))
+ {
+ fail("failed comparison data test");
+ }
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(((ASN1OctetString)c[0].getContent()).getOctets()));
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ SafeBag b = SafeBag.getInstance(seq.getObjectAt(0));
+ if (!b.getBagId().equals(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag))
+ {
+ fail("failed comparison shroudedKeyBag test");
+ }
+
+ EncryptedPrivateKeyInfo encInfo = EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
+
+ encInfo = new EncryptedPrivateKeyInfo(encInfo.getEncryptionAlgorithm(), encInfo.getEncryptedData());
+
+ b = new SafeBag(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag, encInfo.toASN1Primitive(), b.getBagAttributes());
+
+ ByteArrayOutputStream abOut = new ByteArrayOutputStream();
+ ASN1OutputStream berOut = new ASN1OutputStream(abOut);
+
+ berOut.writeObject(new DLSequence(b));
+
+ c[0] = new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(abOut.toByteArray()));
+
+ //
+ // certificates
+ //
+ if (!c[1].getContentType().equals(PKCSObjectIdentifiers.encryptedData))
+ {
+ fail("failed comparison encryptedData test");
+ }
+
+ EncryptedData eData = EncryptedData.getInstance(c[1].getContent());
+
+ c[1] = new ContentInfo(PKCSObjectIdentifiers.encryptedData, eData);
+
+ //
+ // create an octet stream represent the BER encoding of authSafe
+ //
+ authSafe = new AuthenticatedSafe(c);
+
+ abOut = new ByteArrayOutputStream();
+ berOut = new ASN1OutputStream(abOut);
+
+ berOut.writeObject(authSafe);
+
+ info = new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(abOut.toByteArray()));
+
+ mData = new MacData(new DigestInfo(algId, dInfo.getDigest()), salt, itCount);
+
+ bag = new Pfx(info, mData);
+
+ //
+ // comparison test
+ //
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(bag);
+
+ if (!Arrays.areEqual(bOut.toByteArray(), pkcs12))
+ {
+ fail("failed comparison test");
+ }
+ }
+
+ public String getName()
+ {
+ return "PKCS12";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PKCS12Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKIFailureInfoTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKIFailureInfoTest.java
new file mode 100644
index 000000000..b4b527446
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PKIFailureInfoTest.java
@@ -0,0 +1,68 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.cmp.PKIFailureInfo;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTest;
+
+
+/**
+ * PKIFailureInfoTest
+ */
+public class PKIFailureInfoTest
+ extends SimpleTest
+{
+ // A correct hex encoded BAD_DATA_FORMAT PKIFailureInfo
+ private static final byte[] CORRECT_FAILURE_INFO = Base64.decode("AwIANQ==");
+
+ public String getName()
+ {
+ return "PKIFailureInfo";
+ }
+
+ private void testEncoding()
+ throws IOException
+ {
+ DERBitString bitString = (DERBitString)new ASN1InputStream(CORRECT_FAILURE_INFO).readObject();
+ PKIFailureInfo correct = new PKIFailureInfo(bitString);
+
+ PKIFailureInfo bug = new PKIFailureInfo(PKIFailureInfo.badRequest | PKIFailureInfo.badTime |PKIFailureInfo.badDataFormat | PKIFailureInfo.incorrectData);
+
+ if (!areEqual(correct.getEncoded(ASN1Encoding.DER),bug.getEncoded(ASN1Encoding.DER)))
+ {
+ fail("encoding doesn't match");
+ }
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ BitStringConstantTester.testFlagValueCorrect(0, PKIFailureInfo.badAlg);
+ BitStringConstantTester.testFlagValueCorrect(1, PKIFailureInfo.badMessageCheck);
+ BitStringConstantTester.testFlagValueCorrect(2, PKIFailureInfo.badRequest);
+ BitStringConstantTester.testFlagValueCorrect(3, PKIFailureInfo.badTime);
+ BitStringConstantTester.testFlagValueCorrect(4, PKIFailureInfo.badCertId);
+ BitStringConstantTester.testFlagValueCorrect(5, PKIFailureInfo.badDataFormat);
+ BitStringConstantTester.testFlagValueCorrect(6, PKIFailureInfo.wrongAuthority);
+ BitStringConstantTester.testFlagValueCorrect(7, PKIFailureInfo.incorrectData);
+ BitStringConstantTester.testFlagValueCorrect(8, PKIFailureInfo.missingTimeStamp);
+ BitStringConstantTester.testFlagValueCorrect(9, PKIFailureInfo.badPOP);
+ BitStringConstantTester.testFlagValueCorrect(14, PKIFailureInfo.timeNotAvailable);
+ BitStringConstantTester.testFlagValueCorrect(15, PKIFailureInfo.unacceptedPolicy);
+ BitStringConstantTester.testFlagValueCorrect(16, PKIFailureInfo.unacceptedExtension);
+ BitStringConstantTester.testFlagValueCorrect(17, PKIFailureInfo.addInfoNotAvailable);
+ BitStringConstantTester.testFlagValueCorrect(25, PKIFailureInfo.systemFailure);
+
+ testEncoding();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PKIFailureInfoTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ParseTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ParseTest.java
new file mode 100644
index 000000000..f2ba17702
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ParseTest.java
@@ -0,0 +1,308 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+import org.spongycastle.asn1.ASN1OctetStringParser;
+import org.spongycastle.asn1.ASN1SequenceParser;
+import org.spongycastle.asn1.ASN1StreamParser;
+import org.spongycastle.asn1.ASN1TaggedObjectParser;
+import org.spongycastle.asn1.BERTags;
+import org.spongycastle.asn1.cms.ContentInfoParser;
+import org.spongycastle.asn1.cms.EncryptedContentInfoParser;
+import org.spongycastle.asn1.cms.EnvelopedDataParser;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.io.Streams;
+
+public class ParseTest
+ extends TestCase
+{
+ private static byte[] classCastTest = Base64.decode(
+ "MIIXqAYJKoZIhvcNAQcDoIIXmTCCF5UCAQAxggG1MIIBsQIBADCBmDCBkDEL"
+ + "MAkGA1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMQ0wCwYDVQQHEwRUcm95"
+ + "MQwwCgYDVQQKEwNFRFMxGTAXBgNVBAsTEEVMSVQgRW5naW5lZXJpbmcxJDAi"
+ + "BgkqhkiG9w0BCQEWFUVsaXQuU2VydmljZXNAZWRzLmNvbTEQMA4GA1UEAxMH"
+ + "RURTRUxJVAIDD6FBMA0GCSqGSIb3DQEBAQUABIIBAGh04C2SyEnH9J2Va18w"
+ + "3vdp5L7immD5h5CDZFgdgHln5QBzT7hodXMVHmyGnycsWnAjYqpsil96H3xQ"
+ + "A6+9a7yB6TYSLTNv8zhL2qU3IrfdmUJyxxfsFJlWFO1MlRmu9xEAW5CeauXs"
+ + "RurQCT+C5tLc5uytbvw0Jqbz+Qp1+eaRbfvyhWFGkO/BYZ89hVL9Yl1sg/Ls"
+ + "mA5jwTj2AvHkAwis+F33ZhYlto2QDvbPsUa0cldnX8+1Pz4QzKMHmfUbFD2D"
+ + "ngaYN1tDlmezCsYFQmNx1th1SaQtTefvPr+qaqRsm8KEXlWbJQXmIfdyi0zY"
+ + "qiwztEtO81hXZYkKqc5fKMMwghXVBgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcE"
+ + "CEq3cLLWVds9gIIVsAAik3al6Nn5pr7r0mSy9Ki3vEeCBcV9EzEG44BvNHNA"
+ + "WyEsqQsdSxuF7h1/DJAMuZFwCbGflaRGx/1L94zrmtpeuH501lzPMvvZCmpj"
+ + "KrOF8e1B4MVQ5TfQTdUVyRnbcDa6E4V1ZZIdAI7BgDeJttS4+L6btquXfxUg"
+ + "ttPYQkevF7MdShYNnfLkY4vUMDOp3+iVzrOlq0elM95dfSA7OdBavgDJbz/7"
+ + "mro3AFTytnWjGz8TUos+oUujTk9/kHOn4cEAIm0hHrNhPS5qoj3QnNduNrad"
+ + "rLpGtcYyNlHIsYCsvPMxwoHmIw+r9xQQRjjzmVYzidn+cNOt0FmLs6YE8ds4"
+ + "wvHRO9S69TgKPHRgk2bihgHqII9lF9qIzfG40YwJLHzGoEwVO1O0+wn8j2EP"
+ + "O9I/Q3vreCH+5VbpUD2NGTwsMwZ3YlUesurLwse/YICxmgdN5Ro4DeQJSa9M"
+ + "iJnRFYWRq+58cKgr+L11mNc9nApZBShlpPP7pdNqWOafStIEjo+dsY/J+iyS"
+ + "6WLlUvNt/12qF4NAgZMb3FvRQ9PrMe87lqSRnHcpLWHcFjuKbMKCBvcdWGWI"
+ + "R7JR8UNzUvoLGGAUI9Ck+yTq4QtfgtL5MLmdBGxSKzgs44Mmek+LnrFx+e9n"
+ + "pkrdDf2gM/m7E50FnLYqzUjctKYGLNYpXQorq9MJx6TB20CHXcqOOoQqesXa"
+ + "9jL9PIOtBQy1Ow5Bh4SP07nTFWFSMI/Wt4ZvNvWJj3ecA9KjMOA9EXWUDS/H"
+ + "k9iCb2EEMo7fe5mhoyxMxPO+EIa1sEC9A1+rDACKPQCHOLI0uPmsdo0AEECC"
+ + "QLgOQkcwQlkHexOyHiOOtBxehtGZ1eBQQZ+31DF+RRU6WvS6grg58eS4gGOQ"
+ + "bd7CS9yYebvAQkz61J8KprWdtZuG1gBGma12wKMuQuC6RuWlKsj+rPMvaQCt"
+ + "8mucGbkElPGZVhdyD8/BvpSCNbgRwb6iSiw4EECovu4P4GFJaMGUYEuCA711"
+ + "itEieYc1QqS6ULjb3LFL/RcwSw0fGdjnt6B2nHckC2VsYKU1NwU7j0R1Omb4"
+ + "y5AvSgpuWjTXWnHnE9Ey0B+KP5ERZA+jJGiwYz48ynYlvQFSbBm4I6nh/DuI"
+ + "dWB2dLNxWuhdfzafBGtEHhLHzjW3WQwwRZsKesgHLrrj9hBUObodl1uvqvZN"
+ + "AjMOj8DrqbGOhAClj1t4S1Zk1ZekuMjsuoxEL+/lgtbT+056ES0k3A/LnpRb"
+ + "uxA1ZBr26Im+GVFzEcsV0hB4vNujSwStTTZH5jX5rMyi085yJfnikcLYUn9N"
+ + "apl+srhpIZlDJPw7IHaw8tsqXKDxF7MozIXo8B45CKv5Am+BMrIemCMX/ehu"
+ + "PODICl98Ur8tNAn1L+m0nj7H3c8HW2vNuBLEI3SEHHgm2Ij3IY5pyyeVUaWC"
+ + "pumhy8Ru5dj3fZcfKgYuJBQxWMf+UqPsf4iUK3923pouJ1cQ8XU8gOXIRrtX"
+ + "e41d/yR+UAZXSig6SITLw+wLtvitSvtxvjcUSUOI9CYTovKyuz1PQKiaLsV5"
+ + "4CoJhMQ5uRlVFS3H829I2d2gLRpSp6pNWeIZO2NMBxPYf2qcSHyHqQjR7xP2"
+ + "ZTg7U3OO6dZHORfXxzAnW2ExavBIYQmZh1gLn5jSS4wXFPXyvnJAsF4s5wed"
+ + "YHsyAqM/ek0n2Oo/zAh7UcP2vcb9FOoeRK8qC9HjTciS6WbjskRN0ft4T69G"
+ + "+1RsH8/edBxo2LZeA48BSCXDXOlBZJBsOptzYJD8HSZONPnef0jn23lk0fkU"
+ + "C3BjJu2ubFChctRvJniTko4klpidkHwuJgrTnL4er8rG3RfiiEHn/d5era15"
+ + "E1cekdVYWqwQOObOd4v+0gZSJgI48TBc5Qdy8F6wIU38DR2pn/5uNthNDgXk"
+ + "NcV9a2gOE3DoLe8CEIPMihqYMPY8NuSp97eHB2YhKpjP7qX9TUMoOdE2Iat2"
+ + "klNxadJt6JTFeiBPL6R9RHAD5sVBrkrl0S+oYtgF92f9WHVwAXU7zP6IgM4x"
+ + "hhzeJT07yyIp44mKd//F+7ntbgQjZ/iLbHh0mtOlUmzkFsDR0UNSXEQoourZ"
+ + "EY4A62HXj0DMqEQbik6QwEF7FKuwZX2opdOyVKH9MzJxNfDLd5dc8wAc8bCX"
+ + "jcCx5/GzHx2S5DndWQEVhp2hOQYuoJS3r6QCYFaHtDPKnFHS2PBFyFWL+2UK"
+ + "c0WsvVaHYqYKnksmxse9I9oU75kx5O05DZCThPX6h8J8MHRuxU9tcuuleIUQ"
+ + "XY8On+JeEtLSUZgp+Z7ITLuagf6yuKQpaR396MlDii/449/dvBiXAXeduyO1"
+ + "QzSkQCh37fdasqGL3mP0ssMcxM/qpOwQsx3gMtwiHQRi1oQE1QHb8qZHDE4m"
+ + "I5afQJ9O/H/m/EVlGUSn2yYOsPlZrWuI3BBZKoRzRq1lZOQDtOh18BE3tWmX"
+ + "viGIAxajam0i2Ce3h2U7vNwtiePRNEgPmQ7RwTTv0U6X8qqkjeYskiF4Cv9G"
+ + "nrB0WreC19ih5psEWLIkCYKTr+OhQuRrtv7RcyUi9QSneh7BjcvRjlGB6joA"
+ + "F6J4Y6ENAA/nzOZJ699VkljTi59bbNJYlONpQhOeRTu8M/wExkIJz7yR9DTY"
+ + "bY4/JdbdHNFf5DSDmYAHaFLmdnnfuRy+tC9CGGJvlcLVv5LMFJQGt2Wi15p8"
+ + "lctx7sL6yNCi7OakWbEOCvGPOxY7ejnvOjVK/Krx1T+dAXNUqrsDZmvmakOP"
+ + "We+P4Di1GqcyLVOTP8wNCkuAUoN0JFoBHy336/Xnae91KlY4DciPMpEOIpPN"
+ + "oB+3h6CozV7IWX5Wh3rhfC25nyGJshIBUS6cMXAsswQI8rOylMlGaekNcSU4"
+ + "gNKNDZAK5jNkS0Z/ziIrElSvMNTfYbnx3gCkY0pV18uadmchXihVT11Bt77O"
+ + "8KCKHycR39WYFIRO09wvGv6P42CRBFTdQbWFtkSwRiH8l6x39Z7pIkDFxokT"
+ + "Dp6Htkj3ywfQXNbFgRXZUXqgD1gZVFDFx920hcJnuu65CKz6pEL6X0XUwNPg"
+ + "vtraA2nj4wjVB/y+Cxc+1FgzeELB4CAmWO1OfRVLjYe7WEe/X5DPT6p8HBkB"
+ + "5mWuv+iQ3e37e1Lrsjt2frRYQWoOSP5Lv7c8tZiNfuIp07IYnJKBWZLTqNf9"
+ + "60uiY93ssE0gr3mfYOj+fSbbjy6NgAenT7NRZmFCjFwAfmapIV0hJoqnquaN"
+ + "jj5KKOP72hp+Zr9l8cEcvIhG/BbkY3kYbx3JJ9lnujBVr69PphHQTdw67CNB"
+ + "mDkH7y3bvZ+YaDY0vdKOJif9YwW2qoALXKgVBu1T2BONbCTIUTOzrKhWEvW8"
+ + "D6x03JsWrMMqOKeoyomf1iMt4dIOjp7yGl/lQ3iserzzLsAzR699W2+PWrAT"
+ + "5vLgklJPX/Fb3Tojbsc074lBq669WZe3xzlj85hFcBmoLPPyBE91BLhEwlGC"
+ + "+lWmwFOENLFGZE0mGoRN+KYxwqfA2N6H8TWoz6m0oPUW4uQvy9sGtYTSyQO9"
+ + "6ZwVNT3ndlFrP5p2atdEFVc5aO5FsK8/Fenwez06B2wv9cE9QTVpFrnJkKtF"
+ + "SaPCZkignj64XN7cHbk7Ys6nC3WIrTCcj1UOyp5ihuMS9eL9vosYADsmrR6M"
+ + "uqqeqHsf2+6U1sO1JBkDYtLzoaILTJoqg9/eH7cTA0T0mEfxVos9kAzk5nVN"
+ + "nVOKFrCGVIbOStpYlWP6wyykIKVkssfO6D42D5Im0zmgUwgNEkB+Vxvs8bEs"
+ + "l1wPuB2YPRDCEvwM3A5d5vTKhPtKMECIcDxpdwkD5RmLt+iaYN6oSFzyeeU0"
+ + "YvXBQzq8gfpqJu/lP8cFsjEJ0qCKdDHVTAAeWE6s5XpIzXt5cEWa5JK7Us+I"
+ + "VbSmri4z0sVwSpuopXmhLqLlNWLGXRDyTjZSGGJbguczXCq5XJ2E3E4WGYd6"
+ + "mUWhnP5H7gfW7ILOUN8HLbwOWon8A6xZlMQssL/1PaP3nL8ukvOqzbIBCZQY"
+ + "nrIYGowGKDU83zhO6IOgO8RIVQBJsdjXbN0FyV/sFCs5Sf5WyPlXw/dUAXIA"
+ + "cQiVKM3GiVeAg/q8f5nfrr8+OD4TGMVtUVYujfJocDEtdjxBuyFz3aUaKj0F"
+ + "r9DM3ozAxgWcEvl2CUqJLPHH+AWn5kM7bDyQ2sTIUf5M6hdeick09hwrmXRF"
+ + "NdIoUpn7rZORh0h2VX3XytLj2ERmvv/jPVC97VKU916n1QeMJLprjIsp7GsH"
+ + "KieC1RCKEfg4i9uHoIyHo/VgnKrnTOGX/ksj2ArMhviUJ0yjDDx5jo/k5wLn"
+ + "Rew2+bhiQdghRSriUMkubFh7TN901yl1kF2BBP5PHbpgfTP6R7qfl8ZEwzzO"
+ + "elHe7t7SvI7ff5LkwDvUXSEIrHPGajYvBNZsgro+4Sx5rmaE0QSXACG228OQ"
+ + "Qaju8qWqA2UaPhcHSPHO/u7ad/r8kHceu0dYnSFNe1p5v9Tjux0Yn6y1c+xf"
+ + "V1cu3plCwzW3Byw14PH9ATmi8KJpZQaJOqTxn+zD9TvOa93blK/9b5KDY1QM"
+ + "1s70+VOq0lEMI6Ch3QhFbXaslpgMUJLgvEa5fz3GhmD6+BRHkqjjwlLdwmyR"
+ + "qbr4v6o+vnJKucoUmzvDT8ZH9nH2WCtiiEtQaLNU2vsJ4kZvEy0CEajOrqUF"
+ + "d8qgEAHgh9it5oiyGBB2X/52notXWOi6OMKgWlxxKHPTJDvEVcQ4zZUverII"
+ + "4vYrveRXdiDodggfrafziDrA/0eEKWpcZj7fDBYjUBazwjrsn5VIWfwP2AUE"
+ + "wNn+xR81/so8Nl7EDBeoRXttyH7stbZYdRnkPK025CQug9RLzfhEAgjdgQYw"
+ + "uG+z0IuyctJW1Q1E8YSOpWEFcOK5okQkLFUfB63sO1M2LS0dDHzmdZriCfIE"
+ + "F+9aPMzojaHg3OQmZD7MiIjioV6w43bzVmtMRG22weZIYH/Sh3lDRZn13AS9"
+ + "YV6L7hbFtKKYrie79SldtYazYT8FTSNml/+Qv2TvYTjVwYwHpm7t479u+MLh"
+ + "LxMRVsVeJeSxjgufHmiLk7yYJajNyS2j9Kx/fmXmJbWZNcerrfLP+q+b594Y"
+ + "1TGWr8E6ZTh9I1gU2JR7WYl/hB2/eT6sgSYHTPyGSxTEvEHP242lmjkiHY94"
+ + "CfiTMDu281gIsnAskl05aeCBkj2M5S0BWCxy7bpVAVFf5nhf74EFIBOtHaJl"
+ + "/8psz1kGVF3TzgYHkZXpUjVX/mJX8FG0R8HN7g/xK73HSvqeamr4qVz3Kmm/"
+ + "kMtYRbZre7E1D10qh/ksNYnOkYBcG4P2JyjZ5q+8CQNungz2/b0Glg5LztNz"
+ + "hUgG27xDOUraJXjkkZl/GOh0eTqhfLHXC/TfyoEAQOPcA59MKqvroFC5Js0Q"
+ + "sTgqm2lWzaLNz+PEXpJHuSifHFXaYIkLUJs+8X5711+0M03y8iP4jZeEOrjI"
+ + "l9t3ZYbazwsI3hBIke2hGprw4m3ZmSvQ22g+N6+hnitnDALMsZThesjb6aJd"
+ + "XOwhjLkWRD4nQN594o6ZRrfv4bFEPTp4ev8l6diouKlXSFFnVqz7AZw3Pe53"
+ + "BvIsoh66zHBpZhauPV/s/uLb5x6Z8sU2OK6AoJ7b8R9V/AT7zvonBi/XQNw3"
+ + "nwkwGnTS9Mh7PFnGHLJWTKKlYXrSpNviR1vPxqHMO6b+Lki10d/YMY0vHQrY"
+ + "P6oSVkA6RIKsepHWo11+rV838+2NRrdedCe91foUmOs+eoWQnwmTy2CTZmQ5"
+ + "b7/TTcau9ewimZAqI+MtDWcmWoZfgibZmnIITGcduNOJDRn+aLt9dz+zr1qA"
+ + "HxlLXCOyBPdtfx6eo4Jon+fVte37i3HmxHk+8ZGMMSS9hJbLQEkA59b4E+7L"
+ + "GI3JZjvEkhizB4n/aFeG7KT7K3x072DMbHLZ7VgsXQ1VDDmcZmizFwgyNqKy"
+ + "hKCKxU+I2O10IMtiZUpEzV1Pw7hD5Kv/eFCsJFPXOJ2j3KP6qPtX5IYki1qH"
+ + "Juo5C5uGKtqNc6OzkXsvNUfBz5sJkEYl0WfitSSo4ARyshFUNh2hGxNxUVKM"
+ + "2opOcuHSxBgwUSmVprym50C305zdHulBXv3mLzGjvRstE9qfkQ8qVJYLQEkL"
+ + "1Yn7E92ex71YsC8JhNNMy0/YZwMkiFrqyaFd/LrblWpBbGumhe4reCJ4K3mk"
+ + "lFGEsICcMoe+zU1+QuLlz/bQ+UtvClHUe8hTyIjfY04Fwo2vbdSc1U/SHho5"
+ + "thQy+lOZ/HijzCmfWK3aTqYMdwCUTCsoxri2N8vyD/K2kbMLQWUfUlBQfDOK"
+ + "VrksBoSfcluNVaO56uEUw3enPhhJghfNlJnpr5gUcrAMES53DfkjNr0dCsfM"
+ + "JOY2ZfQEwwYey1c4W1MNNMoegSTg4aXzjVc0xDgKa7RGbtRmVNbOxIhUNAVi"
+ + "thQV3Qujoz1ehDt2GyLpjGjHSpQo3WlIU4OUqJaQfF6EH+3khFqUmp1LT7Iq"
+ + "zH3ydYsoCDjvdXSSEY3hLcZVijUJqoaNWBLb/LF8OG5qTjsM2gLgy2vgO/lM"
+ + "NsqkHnWTtDimoaRRjZBlYLhdzf6QlfLi7RPmmRriiAOM0nXmylF5xBPHQLoz"
+ + "LO9lXYIfNbVJVqQsV43z52MvEQCqPNpGqjB+Au/PZalYHbosiVOQLgTB9hTI"
+ + "sGutSXXeLnf5rftCFvWyL3n5DgURzDFLibrbyVGGKAk166bK1RyVP9XZJonr"
+ + "hPYELk4KawCysJJSmC0E8sSsuXpfd6PPDru6nCV1EdXKR7DybS7NVHCktiPR"
+ + "4B4y8O/AgfJX8sb6LuxmjaINtUKEJ1+O88Gb69uy6b/Kpu2ri/SUBaNNw4Sn"
+ + "/tuaD+jxroL7RlZmt9ME/saNKn9OmLuggd6IUKAL4Ifsx9i7+JKcYuP8Cjdf"
+ + "Rx6U6H4qkEwwYGXnZYqF3jxplyOfqA2Vpvp4rnf8mST6dRLKk49IhKGTzwZr"
+ + "4za/RZhyl6lyoRAFDrVs1b+tj6RYZk0QnK3dLiN1MFYojLyz5Uvi5KlSyFw9"
+ + "trsvXyfyWdyRmJqo1fT7OUe0ImJW2RN3v/qs1k+EXizgb7DW4Rc2goDsCGrZ"
+ + "ZdMwuAdpRnyg9WNtmWwp4XXeb66u3hJHr4RwMd5oyKFB1GsmzZF7aOhSIb2B"
+ + "t3coNXo/Y+WpEj9fD7/snq7I1lS2+3Jrnna1048O7N4b5S4b5TtEcCBILP1C"
+ + "SRvaHyZhBtJpoH6UyimKfabXi08ksrcHmbs1+HRvn+3pl0bHcdeBIQS/wjk1"
+ + "TVEDtaP+K9zkJxaExtoa45QvqowxtcKtMftNoznF45LvwriXEDV9jCXvKMcO"
+ + "nxG5aQ//fbnn4j4q1wsKXxn61wuLUW5Nrg9fIhX7nTNAAooETO7bMUeOWjig"
+ + "2S1nscmtwaV+Sumyz/XUhvWynwE0AXveLrA8Gxfx");
+
+ private static byte[] derExpTest = Base64.decode(
+ "MIIS6AYJKoZIhvcNAQcDoIIS2TCCEtUCAQAxggG1MIIBsQIBADCBmDCBkDEL"
+ + "MAkGA1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMQ0wCwYDVQQHEwRUcm95"
+ + "MQwwCgYDVQQKEwNFRFMxGTAXBgNVBAsTEEVMSVQgRW5naW5lZXJpbmcxJDAi"
+ + "BgkqhkiG9w0BCQEWFUVsaXQuU2VydmljZXNAZWRzLmNvbTEQMA4GA1UEAxMH"
+ + "RURTRUxJVAIDD6FBMA0GCSqGSIb3DQEBAQUABIIBAGsRYK/jP1YujirddAMl"
+ + "ATysfLCwd0eZhENohVqLiMleH25Dnwf+tBaH4a9hyW+7VrWw/LC6ILPVbKpo"
+ + "oLBAOical40cw6C3zulajc4gM3AlE2KEeAWtI+bgPMXhumqiWDb4byX/APYk"
+ + "53Gk7WXF6Xs4hj3tmrHSJxCUOsTdHKUJYvOqjwKGARPQDjP0EUbVJezeAwBA"
+ + "RMlJ/qBVLBj2UW28n5oJZm3oaSaU93Uc6GPVIk43IWrmEUcWVPiMfUtUCwcX"
+ + "tRNtHuQ9os++rmdNBiuB5p+vtUeA45KWnTUtkwJXvrzE6Sf9AUH/p8uOvvZJ"
+ + "3yt9LhPxcZukGIVvcQnBxLswghEVBgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcE"
+ + "CGObmTycubs2gIIQ8AKUC8ciGPxa3sFJ1EPeX/nRwYGNAarlpVnG+07NITL2"
+ + "pUzqZSgsYh5JiKd8TptQBZNdebzNmCvjrVv5s9PaescGcypL7FNVPEubh0w/"
+ + "8h9rTACqUpF5yRgfcgpAGeK29F1hyZ1WaIH43avUCaDnrZcOKB7wc1ats1aQ"
+ + "TSDLImyFn4KjSo5k0Ec/xSoWnfg391vebp8eOsyHZhFMffFtKQMaayZNHJ7Q"
+ + "BzG3r/ysUbkgI5x+0bX0QfZjEIs7yuV5Wt8DxMTueCm3RQ+HkR4lNdTBkM4V"
+ + "qozCqC1SjcAF5YHB0WFkGouEPGgTlmyvLqR2xerEXVZn9YwSnT48kOde3oGt"
+ + "EAYyg0yHbNbL0sp6LDM7upRmrgWwxf0BR6lP4wyWdv/XSLatEB7twSNiPBJ4"
+ + "PJ+QagK08yQJ84UB7YpMTudKsaUs7zW76eA7KkW3TndfDYGdhbmZ5wxNl+5x"
+ + "yPZc/jcQHW7vplMfWglUVxnzibNW12th0QXSB57Mzk8v1Rvc/HLGvAOJZG/S"
+ + "N12FZOxbUrMIHGi3kXsmfWznVyq92X4P9tuDDD7sxkSGsyUAm/UJIZ3KsXhV"
+ + "QeaRHVTVDxtJtnbYxBupy1FDBO6AhVrp16Blvnip9cPn/aLfxDoFHzmsZmEg"
+ + "IcOFqpT1fW+KN6i/JxLD3mn3gKzzdL1/8F36A2GxhCbefQFp0MfIovlnMLFv"
+ + "mrINwMP8a9VnP8gIV5oW5CxmmMUPHuGkXrfg+69iVACaC2sTq6KGebhtg9OC"
+ + "8vZhmu7+Eescst694pYa3b8Sbr5bTFXV68mMMjuRnhvF2NZgF+O0jzU+sFps"
+ + "o7s1rUloCBk1clJUJ/r+j9vbhVahCeJQw62JAqjZu4R1JYAzON3S7jWU5zJ7"
+ + "pWYPSAQkLYUz3FmRRS2Yv65mXDNHqR9vqkHTIphwA9CLMKC2rIONxSVB57q1"
+ + "Npa/TFkVdXnw+cmYjyFWiWeDP7Mw0Kwy7tO008UrBY0rKQU466RI5ezDqYPc"
+ + "Lm73dUH2EjUYmKUi8zCtXpzgfTYVa/DmkbVUL9ThHMVRq1OpT2nctE7kpXZk"
+ + "OsZjEZHZX4MCrSOlc10ZW7MJIRreWMs70n7JX7MISU+8fK6JKOuaQNG8XcQp"
+ + "5IrCTIH8vmN2rVt4UT8zgm640FtO3jWUxScvxCtUJJ49hGCwK+HwDDpO6fLw"
+ + "LFuybey+6hnAbtaDyqgsgDh2KN8GSkQT9wixqwQPWsMQ4h0xQixf4IMdFOjP"
+ + "ciwp3ul8KAp/q70i0xldWGqcDjUasx6WHKc++rFjVJjoVvijKgEhlod5wJIw"
+ + "BqQVMKRsXle07NS1MOB+CRTVW6mwBEhDDERL+ym2GT2Q4uSDzoolmLq2y5vL"
+ + "+RfDHuh3W0UeC3Q5D2bJclgMsVjgfQUN19iD+lPFp2xvLTaNWi5fYDn4uuJL"
+ + "lgVDXIMmM8I+Z2hlTXTM1Pldz2/UFe3QXTbYnjP6kfd7Bo2Webhhgs/YmSR2"
+ + "XPuA42tWNAAjlK77lETWodxi3UC7XELjZ9xoGPRbxjOklXXvev9v5Vo+vcmN"
+ + "0KrLXhLdkyHRSm81SRsWoadCTSyT8ibv66P00GOt+OlIUOt0YKSUkULQfPvC"
+ + "EgMpeTm1/9l8n9bJ6td5fpJFDqLDm+FpJX6T2sWevV/Tyt6aoDPuET5iHBHW"
+ + "PoHxKl8YPRHBf+nRWoh45QMGQWNSrJRDlO8oYOhdznh4wxLn3DXEfDr0Z7Kd"
+ + "gEg6xr1XCobBn6Gi7wWXp5FDTaRF41t7fH8VxPwwDa8Yfu3vsgB6q426kjAj"
+ + "Q77wx1QFIg8gOYopTOgqze1i4h1U8ehP9btznDD6OR8+hPsVKoXYGp8Ukkc7"
+ + "JBA0o8l9O2DSGh0StsD94UhdYzn+ri7ozkXFy2SHFT2/saC34NHLoIF0v/aw"
+ + "L9G506Dtz6xXOACZ4brCG+NNnPLIcGblXIrYTy4+sm0KSdsl6BGzYh9uc8tu"
+ + "tfCh+iDuhT0n+nfnvdCmPwonONFb53Is1+dz5sisILfjB7OPRW4ngyfjgfHm"
+ + "oxxHDC/N01uoJIdmQRIisLi2nLhG+si8+Puz0SyPaB820VuV2mp77Y2osTAB"
+ + "0hTDv/sU0DQjqcuepYPUMvMs3SlkEmaEzNSiu7xOOBQYB8FoK4PeOXDIW6n2"
+ + "0hv6iS17hcZ+8GdhwC4x2Swkxt99ikRM0AxWrh1lCk5BagVN5xG79c/ZQ1M7"
+ + "a0k3WTzYF1Y4d6QPNOYeOBP9+G7/a2o3hGXDRRXnFpO7gQtlXy9A15RfvsWH"
+ + "O+UuFsOTtuiiZk1qRgWW5nkSCPCl2rP1Z7bwr3VD7o6VYhNCSdjuFfxwgNbW"
+ + "x8t35dBn6xLkc6QcBs2SZaRxvPTSAfjON++Ke0iK5w3mec0Br4QSNB1B0Aza"
+ + "w3t3AleqPyJC6IP1OQl5bi+PA+h3YZthwQmcwgXgW9bWxNDqUjUPZfsnNNDX"
+ + "MU9ANDLjITxvwr3F3ZSfJyeeDdbhr3EJUTtnzzWC6157EL9dt0jdPO35V0w4"
+ + "iUyZIW1FcYlCJp6t6Sy9n3TmxeLbq2xML4hncJBClaDMOp2QfabJ0XEYrD8F"
+ + "jq+aDM0NEUHng+Gt9WNqnjc8GzNlhxTNm3eQ6gyM/9Ip154GhH6c9hsmkMy5"
+ + "DlMjGFpFnsSTNFka2+DOzumWUiXLGbe4M3RePl1N4MLwXrkR2llguQynyoqF"
+ + "Ptat2Ky5yW2q9+IQHY49NJTlsCpunE5HFkAK9rY/4lM4/Q7hVunP6U4a0Kbu"
+ + "beFuOQMKQlBZvcplnYBefXD79uarY/q7ui6nFHlqND5mlXMknMrsQk3papfp"
+ + "OpMS4T07rCTLek0ODtb5KsHdIF76NZXevko4+d/xbv7HLCUYd8xuOuqf+y4I"
+ + "VJiT1FmYtZd9w+ubfHrOfHxY+SBtN6fs02WAccZqBXUYzZEijRbN2YUv1OnG"
+ + "rfYe4EcfOu/Sa+wLbB7msYpLfvUfEO3iseKf4LXZkgtF5P610PBZR8edeSgr"
+ + "YZW+J0K78PRAl5nEi1mvzBxi9DyNf6iQ9mWLyyCmr9p9HGE+aCMKVCn9jfZH"
+ + "WeBDAJNYDcUh5NEckqJtbEc2S1FJM7yZBWLQUt3NCQvj+nvQT45osZ3BJvFg"
+ + "IcGJ0CysoblVz4fCLybrYxby9HP89WMLHqdqsIeVX8IJ3x84SqLPuzrqf9FT"
+ + "ZVYLo0F2oBjAzjT7obt9+NJc/psOMCg+OGQkAfwj3VNvaqkkQsVxSiozgxrC"
+ + "7KaTXuAL6eKKspman96kz4QVk9P0usUPii+LFnW4XYc0RNfgJVO6BgJT7pLX"
+ + "NWwv/izMIMNAqSiWfzHHRVkhq4f1TMSF91auXOSICpJb3QQ4XFh52Mgl8+zs"
+ + "fobsb0geyb49WqFrZhUu+X+8LfQztppGmiUpFL+8EW0aPHbfaf4y9J1/Wthy"
+ + "c28Yqu62j/ljXq4Qa21uaEkoxzH1wPKCoKM9TXJtZJ39Yl9cf119Qy4M6QsB"
+ + "6oMXExlMjqIMCCWaLXLRiqbc2Y7rZHgEr08msibdoYHbSkEl8U+Kii2p6Vdx"
+ + "zyiEIz4CadrFbrAzxmrR/+3u8JuBdq0K3KNR0WWx73BU+G0rgBX56GnP7Ixy"
+ + "fuvkRb4YfJUF4PkDa50BGVhybPrIhoFteT6bSh6LQtBm9c4Kop8Svx3ZbqOT"
+ + "kgQDa0n+O0iR7x3fvNZ0Wz4YJrKGnVOPCqJSlSsnX6v2JScmaNdrSwkMTnUf"
+ + "F9450Hasd88+skC4jVAv3WAB03Gz1MtiGDhdUKFnHnU9HeHUnh38peCFEfnK"
+ + "WihakVQNfc72YoFVZHeJI5fJAW8P7xGTZ95ysyirtirxt2zkRVJa5p7semOw"
+ + "bL/lBC1bp4J6xHF/NHY8NQjvuhqkDyNlh3dRpIBVBu6Z04hRhLFW6IBxcCCv"
+ + "pjfoxJoox9yxKQKpr3J6MiZKBlndZRbSogO/wYwFeh7HhUzMNM1xIy3jWVVC"
+ + "CrzWp+Q1uxnL74SwrMP/EcZh+jZO4CYWk6guUMhTo1kbW03BZfyAqbPM+X+e"
+ + "ZqMZljydH8AWgl0MZd2IAfajDxI03/6XZSgzq24n+J7wKMYWS3WzB98OIwr+"
+ + "oKoQ7aKwaaT/KtR8ggUVYsCLs4ScFY24MnjUvMm+gQcVyeX74UlqR30Aipnf"
+ + "qzDRVcAUMMNcs0fuqePcrZ/yxPo+P135YClPDo9J8bwNpioUY8g+BQxjEQTj"
+ + "py3i2rAoX+Z5fcGjnZQVPMog0niIvLPRJ1Xl7yzPW0SevhlnMo6uDYDjWgQ2"
+ + "TLeTehRCiSd3z7ZunYR3kvJIw1Kzo4YjdO3l3WNf3RQvxPmJcSKzeqKVxWxU"
+ + "QBMIC/dIzmRDcY787qjAlKDZOdDp7qBKIqnfodWolxBA0KhvE61eYabZqUCT"
+ + "G2HJaQE1SvOdL9KM4ORFlxE3/dqv8ttBJ6N1qKk423CJjajZHYTwf1dCfj8T"
+ + "VAE/A3INTc6vg02tfkig+7ebmbeXJRH93KveEo2Wi1xQDsWNA+3DVzsMyTqV"
+ + "+AgfSjjwKouXAznhpgNc5QjmD2I6RyTf+hngftve18ZmVhtlW5+K6qi62M7o"
+ + "aM83KweH1QgCS12/p2tMEAfz//pPbod2NrFDxnmozhp2ZnD04wC+6HGz6bX/"
+ + "h8x2PDaXrpuqnZREFEYzUDKQqxdglXj5oE/chBR8+eBfYSS4JW3TBkW6RfwM"
+ + "KOBBOOv8pe3Sfq/bg7OLq5bn0jKwulqP50bysZJNlQUG/KqJagKRx60fnTqB"
+ + "7gZRebvtqgn3JQU3fRCm8ikmGz9XHruoPlrUQJitWIt4AWFxjyl3oj+suLJn"
+ + "7sK62KwsqAztLV7ztoC9dxldJF34ykok1XQ2cMT+uSrD6ghYZrmrG5QDkiKW"
+ + "tOQCUvVh/CorZNlON2rt67UvueMoW+ua25K4pLKDW316c2hGZRf/jmCpRSdb"
+ + "Xr3RDaRFIK6JpmEiFMMOEnk9yf4rChnS6MHrun7vPkf82w6Q0VxoR8NRdFyW"
+ + "3mETtm2mmG5zPFMMD8uM0BYJ/mlJ2zUcD4P3hWZ8NRiU5y1kazvrC6v7NijV"
+ + "o459AKOasZUj1rDMlXDLPloTHT2ViURHh/8GKqFHi2PDhIjPYUlLR5IrPRAl"
+ + "3m6DLZ7/tvZ1hHEu9lUMMcjrt7EJ3ujS/RRkuxhrM9BFlwzpa2VK8eckuCHm"
+ + "j89UH5Nn7TvH964K67hp3TeV5DKV6WTJmtIoZKCxSi6FFzMlky73gHZM4Vur"
+ + "eccwycFHu+8o+tQqbIAVXaJvdDstHpluUCMtb2SzVmI0bxABXp5XrkOOCg8g"
+ + "EDZz1I7rKLFcyERSifhsnXaC5E99BY0DJ/7v668ZR3bE5cU7Pmo/YmJctK3n"
+ + "m8cThrYDXJNbUi0c5vrAs36ZQECn7BY/bdDDk2NPgi36UfePI8XsbezcyrUR"
+ + "ZZwT+uQ5LOB931NjD5GOMEb96cjmECONcRjB0uD7DoTiVeS3QoWmf7Yz4g0p"
+ + "v9894YWQgOl+CvmTERO4dxd7X5wJsM3Y0acGPwneDF+HtQrIpJlslm2DivEv"
+ + "sikc6DtAQrnVRSNDr67HPPeIpgzThbxH3bm5UjvnP/zcGV1W8Nzk/OBQWi0l"
+ + "fQM9DccS6P/DW3XPSD1+fDtUK5dfH8DFf8wwgnxeVwi/1hCBq9+33XPwiVpz"
+ + "489DnjGhHqq7BdHjTIqAZvNm8UPQfXRpeexbkFZx1mJvS7so54Cs58/hHgQN"
+ + "GHJh4AUCLEt0v7Hc3CMy38ovLr3Q8eZsyNGKO5GvGNa7EffGjzOKxgqtMwT2"
+ + "yv8TOTFCWnZEUTtVA9+2CpwfmuEjD2UQ4vxoM+o=");
+
+ byte[] longTagged = Hex.decode("9f1f023330");
+
+ public void testClassCast()
+ throws IOException
+ {
+ parseEnveloped(classCastTest);
+ }
+
+ public void testDerExp()
+ throws IOException
+ {
+ parseEnveloped(derExpTest);
+ }
+
+ public void testLongTag()
+ throws IOException
+ {
+ ASN1StreamParser aIn = new ASN1StreamParser(longTagged);
+
+ ASN1TaggedObjectParser tagged = (ASN1TaggedObjectParser)aIn.readObject();
+
+ assertEquals(31, tagged.getTagNo());
+ }
+
+ private void parseEnveloped(byte[] data) throws IOException
+ {
+ ASN1StreamParser aIn = new ASN1StreamParser(data);
+
+ ContentInfoParser cP = new ContentInfoParser((ASN1SequenceParser)aIn.readObject());
+
+ EnvelopedDataParser eP = new EnvelopedDataParser((ASN1SequenceParser)cP.getContent(BERTags.SEQUENCE));
+
+ eP.getRecipientInfos().toASN1Primitive(); // Must drain the parser!
+
+ EncryptedContentInfoParser ecP = eP.getEncryptedContentInfo();
+
+ ASN1OctetStringParser content = (ASN1OctetStringParser)ecP.getEncryptedContent(BERTags.OCTET_STRING);
+
+ Streams.drain(content.getOctetStream());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ParsingTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ParsingTest.java
new file mode 100644
index 000000000..65424e227
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ParsingTest.java
@@ -0,0 +1,99 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1StreamParser;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ParsingTest
+ extends SimpleTest
+{
+ String[] streams = {
+ "oRNphCO0F+jcMQKC1uMO8qFBPikDDYmtfVGeB45xvbfj1qu696YGjdW2igRnePYM/KkQtADG7gMHIhqBRcl7dBtkejNeolOklPNA3NgsACTiVN9JFUsYq0a5842+TU+U2/6Kt/D0kvz0WmwWFRPHWEWVM9PYOWabGsh28Iucc6s7eEqmr8NEzWUx/jM3dmjpFYVpSpxt2KbbT+yUO0EqFQyy8hQ7JvKRgv1AoWQfPjMsfjkKgxnA8DjenmwXaZnDaKEvQIKQl46L1Yyu3boN082SQliSJMJVgNuNNLFIt5QSUdG1ant5O6f9Yr0niAkAoqGzmqz+LZE1S7RrGHWiQ3DowE9NzviBuaAoI4WdCn1ClMwb9fdEmBMU4C7DJSgs3qaJzPUuaAT9vU3GhZqZ0wcTV5DHxSRzGLqg9JEJRi4qyeuG3Qkg3YBtathl+FiLJ7mVoO3dFIccRuuqp2MpMhfuP1DxHLNLNiUZEhLMQ0CLTGabUISBuyQudVFlKBZIpcLD0k7fKpMPuywrYiDrTinMc2ZP3fOGevoR5fnZ6kZAE5oMTtMNokzBuctGqVapblXNrVMLYbriT538oYz5",
+ "KEKVhHxtyUR9D3v5K4IJbVQLAMiVKoK9z7wFWUjzvLFNLg9C/r8zKfBa3YgZrt0Nq64+MxBePMbiNLCnfditc2qUcQZUHnvNnhwT6uGK37JmXg7MvQiKwvi31EIYt6ghqBZVs1iaqc0ep7wuQ16uwSQMlaDdXc9Qf1L0dGO/6eLyREz+p4UR4NOXK+GooQLfMxYL40zJlYcwNyR0rigvIr84WP2IMS2hZjqXtyS6HMM4yUv70hkIorjr7+JC4GtU1MyWuPuNSAGen0AZTaEEXd5sMbqXMqWg3jeM4mzRH1Kb3WdAChO5vMJZPBj9jZZKgXzmxkUh5GlIhUdYgztoNceBzQ3PIc7slCDUw9I2PjB87xsfy7jA5tFtFADs2EUyxUTMCuhilP664jSHgwbrr80k9Xc4sU+MCwCq2nQmcZYcPgKb4M31VJMlKwnZF3JUU2Jtqgg4gbErw58YoBwSkEcMJ2Juhiyx9U36MzxHs9OcTURfpsilMy+mDL8arCDx1knM1KkAHCLjWuJI+p1PvuIypgCwVc+MtGfd7wW8iR1JPJLBiuoZyNJ+xx9htd/HVB+rLtB57H8Gz8W+R00f",
+ "Ol9I/rXMwbLpxTY97v70B+HCl2+cojz2574x/cC56A7KGVF13La8RdzOOvSkl338ct9T/blEFa6QwNz3GmF+MoPdH9lncwz+tqixIqGU02Bp5swH0qjbp/Yjaeq91eR6B+9fl+KKrpglBr8S1BrI4Ey5v3AxxJdCWP8Gd+6Sp15/HMYanwlHBpCsW4+Kq8sGJoJXUXpQ/GBUJKs+WjX1zE6PsvF7/B8cByuqE3NJt7x4Oa+qZtF8qNc0CFDNj31Yhdt7JkAoD30IAd+ue9OhImQMCWwFwySRIRJXU3865K2dBR+VhLuI2aKzLh7MlgVKJk6b2P/ZIkc86ksR1sOUiHrs9EdoYuIssAgMc8QGzn4VN8lxopdzQYVG6pbXGS/VQlHkGdyLd+OHt4srz/NTUWiOquVTRxa6GgtlBFfIXikPTb+iT2pZKyKUlBvpgo0BY9vVUadsteHAI5qrFZBrL5ecK/Qtl9hf/M8qEjyjt2aCXe9B96Hg2QR5A53qW2PJW5VzS0AeB3g+zJSPCTpygrBs20q5Xrna0ux2l17r6HT9Q/AXIOkwPZUXXn0d02igS4D6Hxrg3Fhdp+OTXL8G",
+ "o3eXWpwAGmUkxHEKm/pGkDb1ZQQctCQ06lltZjeMXDp9AkowmA0KXjPQCQwyWE/nqEvk2g/58AxNU0TWSujo5uU0h4/hdMZ7Mrj33NSskWvDpKe7lE5tUjPi74Rmc5RRS+1T/EQobpNxoic3+tTO7NBbZfJtcUYeZ3jqxL+3YQL3PrGe/Zpno9TnQW8mWbbhKhDRtKY4p3Pgk9hPSpJCM9xYo3EMAOAIiH2P6RKH6uX/gSaUY2b6DE/TT0V6v/jdSmYM4+cnYiTyJCi5txI35jfCqIlVCXJd7klirvUMg9SXBhGR25AgQ5Z8yjd7lbB8FvD8JQAXZrp6xiHxbLIW7G11fWEo7RGLFtALI6H38Ud0vKjsEN7N5AibJcxS2A/CWk9R00sTHRBHFUP8o5mz8nE7FeCiwJPs/+tCt04nGb9wxBFMsmWcPEDfIzphCaO6U/D/tQHlA846gbKoikv/6LI0ussSR/i85XBclNcvzTctxylSbCR02lZ+go6fe5rmMouiel/0Tndz8t1YpQGilVeOQ3mqAFyAJk3dgfTNKZuOhNzVIZ5GWScKQ5ZtNcWrg6siR+6YwKvLiRb/TJZk",
+ "PwRUnW4yU8PI7ggbI1BIO9fcTup8optkqCirodyHCiqsPOMZ4g28bJ2+kpfQRujWGlKFYQzA1ZT32s9hdci+fvXPX0KAjcUgcxsGzMABFbEm04BwDF2WLgg9s4/x71r5JrgME1S08I3mCo4N0eFHWDeLJL1b5YNNo6tfO5V2WpIE867N9zdAgvp1gijVjUNWqEB3A/NLb3reLMu2hYgqRFTCVBfcFclD46k0XEfUJqwWdQhOz92WNl/3g53bjKX1hDZgjLIzK6m+SU6+J/h4NidrS7E0gOBevZW8gRYdKMVqNWxzUfxv6kgG+kIeF9JqMcO6jdh/Zu/0tpZoHFeCweZ1jT1eEtltFu1FcTTPc1UT0pT+ZNVgefrBONoGnvn8+dBjPese6F2TmRCExJq9taKlIh/kHdkbpaa7vwrBpYRgVGfARPyM9SSCaE7pVBDuwkFeYiGU4tamm5Gq10ojRQgetJ3UOg/PGTJcxo97GBiG5zAST9NdHdgK3eI4FAbWpGwmWxNpPWOst0a7zuGKAzYU+1IQh8XA3IgJ2vy3+w0JihU6G+12LUzsL2aQtpG7d1PqLhwOqHq3Qqv3SDsB",
+ "ZIAKizvGzbvqvqOlxOeVgHGHV9TfKNjjqyzbCj8tggv2yp7kkq1D3yRlC349tuul3zN9g4u83Ctio9Gg3HiLzMULxoOImF/hKRDhJpPLbLm0jSq1fyF+N7/YvyLeFhGoPhYEBUihDcxo1NIcWy66aBt3EuOlTyDQWrDe0Za3mrTrrl10uLHVKcQMgeD+UMgjQqmHzQJR8wdNjHPKHWVvZEdiwI031nV2giHJHXv08Jvf4vmw4dAlH2drCl6cBgg33jy7ohK8IiXz6eCw6iY9Ri8YaMzxOhgE2BOHzEz5ZC2hilL4xO/ambTER4bhb4+9VTUIehHP18FcXm8TKPQRMqyKP2fMlzWW3/uelYfPP5SHlyLAULa1KjDCkLIDunEKZDpv2ljGB6JPrTlNwFsvlZcihfOAwjbr2jW3MwP704OA8xzd/dynBU47unIZEu0LAvQ3TUz3PLga0GGO1LZGtg0Foo9zFG2wuVCdgYHmozOQ+8I3gRguW1CjGy7ZCTBuN1GZ510ERhae+bRQtldHsLeiHTghnkU1xEX1+W0iEf3csDYrgpuq3NaBYRGirovDiPBYFHmru0AMclhFnpcX",
+ "uG0wQ55kMlfZtJFAqTl0bnYW/oy9NFOi0e4FqAYwsvMxGO4JtGzXgkVwEUAC0AUDItRUjxBl+TkoPTYaprgn0M/NQvKPpXJ+yzI7Ssi+F2alLR0T6eF/4rQ32AVjnANJaghXZm0ZKduckbhSxk5lilJVJRuzXKchZRtlPluvlj448bq+iThktsEQoNP8NMpi7n/EVxovp+dow4Q6t7msSRP4cGXtyYoWKbf/7e5XzBKOZZ1/f3s86uJke4dcKIaljpJfBrtuFxZC6NYXzX6PkoDoBgqQ8RBrxsX54S9cBDAPxxmkq8zviAOW3oqPMULGGmQzHBiRwE8oeDFoMnzF5aR/lkbNuTLOxhbIkosgLWlDNVEFYx9bVhdLzO7VwaAK829dimlPOo5loKB7Pd2G7ekRKXwu7wNvJBq8KRkhtLKbKoS8D6TaRWUMb9VBJ1CMy4mrw+YwTmAKURQ6Dko9J/RgzRg5Y/sUlwdMYS9HOnvKiTVu5I/ha35wwkhIPVm+FCn05tstntZaXXXu4xExHeugAKNBhkcc/SQt+GFdxXDd+R4C2LfKxGDSyZKVTFYojHTdZUo8Gx6SZLY6b2SZ",
+ "sH0kIwIq1THAfTLfqUKJfG1YauCQKPc9/mk3l39yK6zgxSpCH2IjZIwhhJtGm3F+8PTneT725OuyR617nxqrgqMGkkZkyY4DA5CjsikpBo5mo8TspX1g+vtXXtxymJMTMo8JwX3nSH4gSb3vPia+gwOW2TcJmxVdp3ITPA4gJpMfqoMBqRM+eDWO6QXW5ijVL4+wnp40u5bU4fQLVzpg25+QGLqBHD6PZTQaN6F9Vy5XpsAGDlncCklVuX3Lkp3Xb9cTiNa/4ii04uwZqx0juszjwFAMPPb6u56crvN1x4FXfXzabWECHbdQLlKazowvU9bEnqG2i4H44Ae+v8Iw8HK5mbZ6ercLTD9oPgs7Ogal037l2WwLApUz/fmD5fV8SxHh+vKDpfOzv6xcQxynS82jAJw9AdUwE/4ndGzzHPIu2M81gbAgZQ02EurMMU62hYgiXeridrtyh+H5R+CiwQdEyX7/op6WVihsYj2O3O/1hgjhGQRFD6sGwnko50jgWRxaMMfJGNlyGoT8WT5k931jU7547u7Ovr7XP/t8r3G7ceCiCcYjQgdwXdvIStzPvvV7Yy02isZjiJF8TLJQ",
+ "tycxf1mOz1yLE6cT/ZlCxMeTxlEEHFeIdw0+nF/40Tsw4vLco+4kR2A6cVml611CSpN6l/RMKk2LnAkprrbJ/Uam902WBnQ+I6Vsl6GkFFq7362bdixojqMFVKYytXLCT8I78f6s8M6a3jSALQloD6Ftvn+cc+cctO3weaaaPgAlrz+f2MFs8bqpnLQYbbY/JS9IAYJFH+yVtLz7eKcedEp9JMlJ3/43szU2fDN9ZMxBoQnxEmF3WZv6GF0WRc8VhTblXRgk4mlz6Fu3IXvwW/rbn+VCYYIk/XaVLrxFAnnw6mBozAF7vmV0OrIYBlSDU8rMb+F7AvE7pwErO9TJtCE8IUvQf8TsJYRoYv21/X57pzcBedtqVeU3DnTlmESHxG6H1uJbadSFLWSxKS4svfp8T9FWqX5815yD/UplAKEIeorLTAlPIC2ASKDU6SQW260biNAfY8FYQCWa8btaTwFuY8NMwSHzyqgU0aoPKnagi/4hOIWNO5rZ8Xcnzx+ELtEl33hQnzc4OUlT5eeVYQWkz2IWVQ6Re4JWF3L4OXzNZWgefKGMzZU6IHoBeCgfi+popLRJpaOx0dcvwGjk",
+ "oDsoFvUA+sGOoMyZY6w1UhY3NBkeoozzjEkDSRN1golyXJ1dC5CtYNEjvAJYKj+sqNwg9mBlYyybYpnI3GSP125zMeBHPCoy5CoNOkJW4OH/oLyjVeQbFNic/b2Jcz6lTguYhep8hq9EM2XuFV8T1rm5+4ucI7fH1UiOqRZyuHBAJ0Cna5kv6D3efsa9rd+swybiMIUjmPWpyxzNOOihCYuf4JqRh/D5eZKm6x0Zj2uRhTAYYxI7Q3czd0R9490ufG8VbF8ASBMireMONNNAA/OZCpxJh6xnIANBqV6YDeysws3NBWY2QuNumvg5Kr3/g+VMzJHi4wGuJjraKWi9+ylMfelHF5h/h+pAQVxCotq8JU3OTnMUW4rQp2a8BR5S+mZqPSPlb87tDG9r0+yqb1uO4UIo71C7Xxwoq4M0tXjk6mSmtP/sm+Lh14qfUzKRhTHVdz91TK104mbTJNXbK+jGPD/2BJO9fiaXY8IYanpfDLBfJo06VYbm6HehRZTwnDHnN50j7ki4aMS3COZvffjRInXD8dS5h9zmtKNpoqg//lPg4gpS+4Th2sJ3SGtBV0Ne89r7AfZMAVa26PMK",
+ "MIDLuZTrtZnEBOB6l14iSEyokAg5Wf5JviumhfPeL7WSFTHfOodU2hrvhyvM6oAlRHY1blTj7mw+Tcf9Tmc+/FHT6PGu0NT5UAqaqChX0gS9jizgAE2Yfhd4X/DoeQySMAixKuhu8TbvDxb54jeW9+7LVkmlntJ/0SkMgsT+WQ31OfpwDmEGDczYc+Ol14aJS+EW+rnGv9d38bo/cy+EnpNh8iV2rGGoC8fDzFHKU4gqGFSZF/tnD2OfCne0Vjr/GD6kyp2MVcHig19DBg2toGRkHnuY5kLkwOanztXA80IaAmv8e6s62U8CE8ozUZeDBcvBigEkSGx79Vsyiks8+9Kq9xLHLeS5kRT6zSl8whe8U1fIfrgic34KPlozcQVahwCru1XWyQ+9uevih8x4zMftkJ3JBZhPrnlgtx9McntH/Ss9fdUEkNwWpDnq8Xby8/5gMMMwQ13XDB73vqqteDiltMq8i7LRez4iIHfSBBfIkZIzMZAblQXaSm029iBcAAUes7wcGHUl7KOpRy18jNtI3+h7e1Ri6sT2vJYQaove0nzZ5xAjpBKnbJX+lpGVlI00fC2YSTfyNqFA0jkL",
+ "MG4QbKLbQR3enPn6Z/kEUtHrzWBIqYKR7Gvs5QHLPF6417p1O58suZq38Bb8dO5udtgOqNEVAPGmOuidYygWWfWOP5ReggTUk5XlrkvRxCU0MHWbkSKkJ+T4nLjozreqTJ0io41sFVrpxuOugAvXJ6QtMmixSABUaNgU9SkkWf9pOEiJI8dfND51HxJCbXHwsMCMBp5FbaMZmlWPwirRdAox4wbLk9ZLmoWUcorUjdaWhKvT/LnjMgPmwnwwKpN/4MOnRDdAPdzXX3aWHdkzpfsQnqt3UJsTsSaJlzeUja5C5L4CXGyt99qmfcwB8OB9TL4EYTIl3maD/gUWBfckOsji8x2E2c2iuKKrcmMmcChYr4wCjtTjEeVKOAZ2m9mU2MKc2z2hDw3AuZxsY6aOtdAjnrwl5QXGRg9I5LVl5SI5RwnLwk90pJzDGuSSCtSmzh9DUZ4WpfN+1393aTGRqCMOsB4KxbXjspUbVMFJbnXXlsSNWaoFTpHjK6b6Ghi2/re7KJpoKElM3nGs3qvxdvGTKu7LKr/sgKDL6uQLRKoyk8AHSIGX9c8ZUTk7Sq9jV9p4QfV1WFVpaBxSsEmw",
+ "MR0BACgWKis9/AKwG9/ARgGWJn1aM3nU8YXzWG+b7aeRUkVCjl4WxeL38E3FAMLW4UcyLzxeb+CskOqhPPTglmxhK7jQcrNILsWcZvdZfApYIvk5uKqA5FKuUuL48uvD0aKGRENe/VEUFlkQru5YX4Xnp+ZThrJJlgn7ANat/qAdP6ULEcLaOQlLYcGRh5ttsJTRT4+cZQggTJjWt+9idUQ66HfC6zQ1qHcMuochy7GHiUmNXAs0AgwOF9Jwet/Qh74KGMtmppJ9gkEqiYECFQA2gVgKc1AufHJS6S6Re72FfH/UkL41L2hvlwktkD5/hZrUZ1R+RG12Eip2zKgus4g/aGl0V8B/JvkcnFUsZJ6uxs24arOBDJOuzzxky5F5B/hwVGPEdcfHunqndUcx26/KCK72hOljlqTXl8yEbXlcMqVFNByZLr7TnGzGGUlO7kuHPW/ItZUJvrHokpsLLrb3ZhEZ8pTQd75gFcf0Ve8CYzEtk2ISHtNJQV6Iz4AZHWssU6F6YWM/OlJz5JGTtPHfGMJXgl4oxbBjeenS3JQ0X7vWXYMwPe3U1dat6m5hrRC1KzI6e6w+gPDtF8GQ",
+ "DH2WX6XoIseX6lHIey3seUr3DAz82fyk0jL7xc5IDTrDfqS64QBhHDpqHETF/81MrPXsM3IANBfjDOl9g/gua8wWPpPNxuWZMNh0GLcAr6PJ939TCbjE3soZHF2wiA82nZEO8jIZosDVRWFUfJS6Y3nrJz63SExqB6OUdBfvSfz1Y1M/90ofBxkfeuS85deMdn+1rZdsnZJYwz2Z6lCDvYjUTfrSwfVFJBP8Y2BXr8WClUYkfGG4eNG7IPNBRuMmhrhHj5y9z+5Jor+EbbTi5F5Jvdu2/bDM7s32XsaMNLYuVtNYONrbQ+3QZ746/yKZM4hDREvxyGLgDx3Apz7pyvwKm0//iTCY3yJLxZifGLh2uc28cFBln7IH1x8oui4Xq9vF+Z2EH4Ow48Ln5pzggBKMGy4dsfW6266TNYd/Z3SZUi28sxondqhGCSGUo7ZVPAOoYDcYKvjdI/cJ688PHliyZSYBYVmR5HBxZ57sqWwgZQ7zVvwv4CHHysvb92sPrXijHxBIkwpNuK56UMyQCcywlTRLDCMAMNAEGi4fWbDQIoPfn+NixMhEieg3Zh7GXPwHxW8morlgBW5aF76P",
+ "AwClK6Tq9R2DYGf8RAHu9dEttLeOfUVmS4WPwX0NehsnyM7y7n2sgGnXsiva3yFqK1hKZICkVukvHF7/bpgEEm/jRwcAhQUoG+c1qVde38eHJXj58YOBGTveruc+021or9/tHBtenmYPO6+arLQtONi43NKm7+I6ugkgQlp6iBr4haa0XMDTzMX9c8Qm/O+MrVo3qESYKkVtoSSK7SGZTBaRWNF/dOM0NQxeMP+XTVOuroqE23ZNsubBTEZnd4vUilFb/iKnhyT9XnIo7gM/Yz7HLVU5yc3yIj2sFUE+DcwpvcNO5EnPhj3bHsJvf3N4r72+5my2KjoR3KAJE1Imabd54o4xZ/9UaR93qLkVuXMkLRCCU/zlZDtfbJDsRR0C5rSYd2k6IPlNcl7PgmUpsNPUyoDYqvhuRUZxgoUAfKbogzJX8FU/QpllsKVtt68ucBi0bqC5pVKu23j79nDvYQsSlYY3JwJQaM5M558J5qpP1yEF2p4kSRphnB9UR29wWgch5eWZ4a02LlHVM5Msl6W5PdmHt+eFARBRv6edIyYDFzxm4WZroH5F/GxBhM0KObgawkxa5VWsYm0VhhXb",
+ "KACwq8rZuOLHuNnZJA07WzI7kppCwptbcYU2B7t86QcZrnadCtxoM5QNcl9rsbMA26iWCPV3VlDAmLSWcxtMoSKWuo4edJpk8K915xkFU5U6I/901vx5hqAECQDy/Q+QDWmWTXDoVHqFV9wvIj3wCJPpJL/Ewpl0NZd+68jjOjUhjIdNebLrWNK2nhTPiIjFjkcVqEgashpOmnbHT+2MV/CHoixmUEiuRI1B0dvSf7FHGRgbXGBubisuu60g8XTens5zyRo4Qn/LTxIu2aj4LTtyLonV3sXr+y35A1zq5mCrE1f1nOINVzwYYY76iJGIaBkZuMU3366FPIbYkmXwla6RQU1FA0Y7n05qczw7Ie5TveRTByKFtUqW8OAb9vH+H2ezJ4CXE3AGbu/nTj64KClO/zL499GA+97g+X6tTN6xOJdNknlqw6ZnFNtCL8+A3hL4OyOgWD0IGC+xFvcKjDUaaJenCtQvprCJaFrvoOS+yYmixnFqglnPYL/64/Lca8NmDVpPzlHI8HNwUDzKiXTw3q7GnQZWmUYzu1vLIEi6/hyqrULRN1vLdd/8HCMNQFj4ot61UftHtOG8MCKa",
+ "rUABPQ3SEWE5rY16pM+o+7EObLNM1jEa5YCMQM/aen0PWajWNax3Pyo6TZL8aGDXZF0yWqDM3b2m6UHOr6yqsUSrD+0jXPT48QN1VdBmh+AFRK+UcaYO383a0nvtv0c9uHt4yfceXLPGWrNjW+uTnS/lKpCdpE4GfLF1SFHIUcMxT+3At7hwDHNkLXllEXqbgDP8LyQSlYwT5jQUDCOzwc8CSxAryUOj6fN+iLKAiw4haPV/WZDG+JOmDMG2azo8SoBMi3y6Z2Le2fz2dMuvn5DUvCUvazrUmWYx4NEdSzc9GfBc6cXkduMqCs+lT2Ik2GHO0WjhrEB6j5NULOaCtbrislM85P6QutN4Pj9l18pcD6vZCcDTOwMj/BznclH342jeMn7rBgpW1YSzbNGP6KC4NeNW1H2xqNtuyhcJvasx4dwhzO18A36H6HtkiQyJNnfnVHh1oviO6mi3atmnh9B/55ugXM1Wf/6Kv8kJyaKtK8cWo+jCAR0/P/EsPtzToJM9Yk2+qxaPFd3k7T2KXvCQ9D1jLeECxL59L+WDvdBtxOEBD7W0a/Mn/9LuQPOiwARKJSTU+blJ6ezTeo83",
+ "poA1hF4zRh7HF0xVglYoLFqkUR7Pru/qYFnfMKBPuEOOGdgO3MMcAvIZ+w+Ug4THr/6+Vux0TN3wdOB+beObOboLgNE2zaD65lyMFbaulzrEnWjUgIg63CdpQJ2ESaimHGg/GmsipUCndRJ37TbUtn8W112SehsAgrsjiBcuJhw61i4bVfAZEcycq4Y/FlEDxtzoH8WzDoESNbl+r5agLcHGr37BFi81IXS8TLihC1T8b7d6tLb6lpXT+9IR4xAyZTw1IFMDZZEzVmHgYE/Et20/WhkX/oGghkWSpCxR0kynDplk+BEK2oyGKnl+rf4vymhsse2iQ/C99PhaodZjDfuGVSwPLoU0AYyAKaEwmgHPOFbDlrAmNk4iBp+IZYm9guZM2hcQ4GeA5WQyZzw4C1yMywWbdjtL9ZhpClmmPZ28nmwNORAat7tXPJoBBdXFB0gNT/wU7UYIKU5GnAiDIFJ0o8ijnuAMat3AsBki2vxwdypuBq5M6OF9DVA0HRUjOA0l4JHjK8Y282mz3U34PDPQvwCT342uD9cO3uXoSr3T2FnDmsVHz4Q9zYpSjioLmZk9ZTnQWgN5V5Oyat6m"
+ };
+
+ public String getName()
+ {
+ return "ParsingTest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ inputStreamTest();
+ parserTest();
+ }
+
+ private void parserTest()
+ {
+ for (int i = 0; i != streams.length; i++)
+ {
+ ASN1StreamParser aIn = new ASN1StreamParser(Base64.decode(streams[i]));
+
+ try
+ {
+ Object obj;
+
+ while ((obj = aIn.readObject()) != null)
+ {
+
+ }
+
+ fail("bad stream parsed successfully!");
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ }
+
+ private void inputStreamTest()
+ {
+ for (int i = 0; i != streams.length; i++)
+ {
+ ASN1InputStream aIn = new ASN1InputStream(Base64.decode(streams[i]));
+
+ try
+ {
+ Object obj;
+
+ while ((obj = aIn.readObject()) != null)
+ {
+
+ }
+
+ fail("bad stream parsed successfully!");
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ParsingTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PersonalDataUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PersonalDataUnitTest.java
new file mode 100644
index 000000000..6f7af40a6
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/PersonalDataUnitTest.java
@@ -0,0 +1,122 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.x500.DirectoryString;
+import org.spongycastle.asn1.x509.sigi.NameOrPseudonym;
+import org.spongycastle.asn1.x509.sigi.PersonalData;
+
+public class PersonalDataUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "PersonalData";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ NameOrPseudonym nameOrPseudonym = new NameOrPseudonym("pseudonym");
+ BigInteger nameDistinguisher = BigInteger.valueOf(10);
+ ASN1GeneralizedTime dateOfBirth= new ASN1GeneralizedTime("20070315173729Z");
+ DirectoryString placeOfBirth = new DirectoryString("placeOfBirth");
+ String gender = "M";
+ DirectoryString postalAddress = new DirectoryString("address");
+
+ PersonalData data = new PersonalData(nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, postalAddress);
+
+ checkConstruction(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, postalAddress);
+
+ data = new PersonalData(nameOrPseudonym, null, dateOfBirth, placeOfBirth, gender, postalAddress);
+
+ checkConstruction(data, nameOrPseudonym, null, dateOfBirth, placeOfBirth, gender, postalAddress);
+
+ data = new PersonalData(nameOrPseudonym, nameDistinguisher, null, placeOfBirth, gender, postalAddress);
+
+ checkConstruction(data, nameOrPseudonym, nameDistinguisher, null, placeOfBirth, gender, postalAddress);
+
+ data = new PersonalData(nameOrPseudonym, nameDistinguisher, dateOfBirth, null, gender, postalAddress);
+
+ checkConstruction(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, null, gender, postalAddress);
+
+ data = new PersonalData(nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, null, postalAddress);
+
+ checkConstruction(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, null, postalAddress);
+
+ data = new PersonalData(nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, null);
+
+ checkConstruction(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, null);
+
+ data = PersonalData.getInstance(null);
+
+ if (data != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ PersonalData.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ PersonalData data,
+ NameOrPseudonym nameOrPseudonym,
+ BigInteger nameDistinguisher,
+ DERGeneralizedTime dateOfBirth,
+ DirectoryString placeOfBirth,
+ String gender,
+ DirectoryString postalAddress)
+ throws IOException
+ {
+ checkValues(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, postalAddress);
+
+ data = PersonalData.getInstance(data);
+
+ checkValues(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, postalAddress);
+
+ ASN1InputStream aIn = new ASN1InputStream(data.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ data = PersonalData.getInstance(seq);
+
+ checkValues(data, nameOrPseudonym, nameDistinguisher, dateOfBirth, placeOfBirth, gender, postalAddress);
+ }
+
+ private void checkValues(
+ PersonalData data,
+ NameOrPseudonym nameOrPseudonym,
+ BigInteger nameDistinguisher,
+ DERGeneralizedTime dateOfBirth,
+ DirectoryString placeOfBirth,
+ String gender,
+ DirectoryString postalAddress)
+ {
+ checkMandatoryField("nameOrPseudonym", nameOrPseudonym, data.getNameOrPseudonym());
+ checkOptionalField("nameDistinguisher", nameDistinguisher, data.getNameDistinguisher());
+ checkOptionalField("dateOfBirth", dateOfBirth, data.getDateOfBirth());
+ checkOptionalField("placeOfBirth", placeOfBirth, data.getPlaceOfBirth());
+ checkOptionalField("gender", gender, data.getGender());
+ checkOptionalField("postalAddress", postalAddress, data.getPostalAddress());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PersonalDataUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ProcurationSyntaxUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ProcurationSyntaxUnitTest.java
new file mode 100644
index 000000000..8064bc654
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ProcurationSyntaxUnitTest.java
@@ -0,0 +1,107 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.isismtt.x509.ProcurationSyntax;
+import org.spongycastle.asn1.x500.DirectoryString;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.IssuerSerial;
+import org.spongycastle.asn1.x509.X509Name;
+
+public class ProcurationSyntaxUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "ProcurationSyntax";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ String country = "AU";
+ DirectoryString typeOfSubstitution = new DirectoryString("substitution");
+ GeneralName thirdPerson = new GeneralName(new X509Name("CN=thirdPerson"));
+ IssuerSerial certRef = new IssuerSerial(new GeneralNames(new GeneralName(new X509Name("CN=test"))), new ASN1Integer(1));
+
+ ProcurationSyntax procuration = new ProcurationSyntax(country, typeOfSubstitution, thirdPerson);
+
+ checkConstruction(procuration, country, typeOfSubstitution, thirdPerson, null);
+
+ procuration = new ProcurationSyntax(country, typeOfSubstitution, certRef);
+
+ checkConstruction(procuration, country, typeOfSubstitution, null, certRef);
+
+ procuration = new ProcurationSyntax(null, typeOfSubstitution, certRef);
+
+ checkConstruction(procuration, null, typeOfSubstitution, null, certRef);
+
+ procuration = new ProcurationSyntax(country, null, certRef);
+
+ checkConstruction(procuration, country, null, null, certRef);
+
+ procuration = ProcurationSyntax.getInstance(null);
+
+ if (procuration != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ ProcurationSyntax.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ ProcurationSyntax procuration,
+ String country,
+ DirectoryString typeOfSubstitution,
+ GeneralName thirdPerson,
+ IssuerSerial certRef)
+ throws IOException
+ {
+ checkValues(procuration, country, typeOfSubstitution, thirdPerson, certRef);
+
+ procuration = ProcurationSyntax.getInstance(procuration);
+
+ checkValues(procuration, country, typeOfSubstitution, thirdPerson, certRef);
+
+ ASN1InputStream aIn = new ASN1InputStream(procuration.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ procuration = ProcurationSyntax.getInstance(seq);
+
+ checkValues(procuration, country, typeOfSubstitution, thirdPerson, certRef);
+ }
+
+ private void checkValues(
+ ProcurationSyntax procuration,
+ String country,
+ DirectoryString typeOfSubstitution,
+ GeneralName thirdPerson,
+ IssuerSerial certRef)
+ {
+ checkOptionalField("country", country, procuration.getCountry());
+ checkOptionalField("typeOfSubstitution", typeOfSubstitution, procuration.getTypeOfSubstitution());
+ checkOptionalField("thirdPerson", thirdPerson, procuration.getThirdPerson());
+ checkOptionalField("certRef", certRef, procuration.getCertRef());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ProcurationSyntaxUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ProfessionInfoUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ProfessionInfoUnitTest.java
new file mode 100644
index 000000000..d8fe671a2
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ProfessionInfoUnitTest.java
@@ -0,0 +1,118 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.isismtt.x509.NamingAuthority;
+import org.spongycastle.asn1.isismtt.x509.ProcurationSyntax;
+import org.spongycastle.asn1.isismtt.x509.ProfessionInfo;
+import org.spongycastle.asn1.x500.DirectoryString;
+
+public class ProfessionInfoUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "ProfessionInfo";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ NamingAuthority auth = new NamingAuthority(new DERObjectIdentifier("1.2.3"), "url", new DirectoryString("fred"));
+ DirectoryString[] professionItems = { new DirectoryString("substitution") };
+ ASN1ObjectIdentifier[] professionOids = { new ASN1ObjectIdentifier("1.2.3") };
+ String registrationNumber = "12345";
+ DEROctetString addProfInfo = new DEROctetString(new byte[20]);
+
+ ProfessionInfo info = new ProfessionInfo(auth, professionItems, professionOids, registrationNumber, addProfInfo);
+
+ checkConstruction(info, auth, professionItems, professionOids, registrationNumber, addProfInfo);
+
+ info = new ProfessionInfo(null, professionItems, professionOids, registrationNumber, addProfInfo);
+
+ checkConstruction(info, null, professionItems, professionOids, registrationNumber, addProfInfo);
+
+ info = new ProfessionInfo(auth, professionItems, null, registrationNumber, addProfInfo);
+
+ checkConstruction(info, auth, professionItems, null, registrationNumber, addProfInfo);
+
+ info = new ProfessionInfo(auth, professionItems, professionOids, null, addProfInfo);
+
+ checkConstruction(info, auth, professionItems, professionOids, null, addProfInfo);
+
+ info = new ProfessionInfo(auth, professionItems, professionOids, registrationNumber, null);
+
+ checkConstruction(info, auth, professionItems, professionOids, registrationNumber, null);
+
+ info = ProfessionInfo.getInstance(null);
+
+ if (info != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ ProcurationSyntax.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ ProfessionInfo profInfo,
+ NamingAuthority auth,
+ DirectoryString[] professionItems,
+ DERObjectIdentifier[] professionOids,
+ String registrationNumber,
+ DEROctetString addProfInfo)
+ throws IOException
+ {
+ checkValues(profInfo, auth, professionItems, professionOids, registrationNumber, addProfInfo);
+
+ profInfo = ProfessionInfo.getInstance(profInfo);
+
+ checkValues(profInfo, auth, professionItems, professionOids, registrationNumber, addProfInfo);
+
+ ASN1InputStream aIn = new ASN1InputStream(profInfo.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ profInfo = ProfessionInfo.getInstance(seq);
+
+ checkValues(profInfo, auth, professionItems, professionOids, registrationNumber, addProfInfo);
+ }
+
+ private void checkValues(
+ ProfessionInfo profInfo,
+ NamingAuthority auth,
+ DirectoryString[] professionItems,
+ DERObjectIdentifier[] professionOids,
+ String registrationNumber,
+ DEROctetString addProfInfo)
+ {
+ checkOptionalField("auth", auth, profInfo.getNamingAuthority());
+ checkMandatoryField("professionItems", professionItems[0], profInfo.getProfessionItems()[0]);
+ if (professionOids != null)
+ {
+ checkOptionalField("professionOids", professionOids[0], profInfo.getProfessionOIDs()[0]);
+ }
+ checkOptionalField("registrationNumber", registrationNumber, profInfo.getRegistrationNumber());
+ checkOptionalField("addProfessionInfo", addProfInfo, profInfo.getAddProfessionInfo());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ProfessionInfoUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/QCStatementUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/QCStatementUnitTest.java
new file mode 100644
index 000000000..02094ed12
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/QCStatementUnitTest.java
@@ -0,0 +1,105 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.x509.qualified.QCStatement;
+import org.spongycastle.asn1.x509.qualified.RFC3739QCObjectIdentifiers;
+import org.spongycastle.asn1.x509.qualified.SemanticsInformation;
+import org.spongycastle.util.test.SimpleTest;
+
+public class QCStatementUnitTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "QCStatement";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ QCStatement mv = new QCStatement(RFC3739QCObjectIdentifiers.id_qcs_pkixQCSyntax_v1);
+
+ checkConstruction(mv, RFC3739QCObjectIdentifiers.id_qcs_pkixQCSyntax_v1, null);
+
+ ASN1Encodable info = new SemanticsInformation(new ASN1ObjectIdentifier("1.2"));
+
+ mv = new QCStatement(RFC3739QCObjectIdentifiers.id_qcs_pkixQCSyntax_v1, info);
+
+ checkConstruction(mv, RFC3739QCObjectIdentifiers.id_qcs_pkixQCSyntax_v1, info);
+
+ mv = QCStatement.getInstance(null);
+
+ if (mv != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ QCStatement.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ QCStatement mv,
+ DERObjectIdentifier statementId,
+ ASN1Encodable statementInfo)
+ throws IOException
+ {
+ checkStatement(mv, statementId, statementInfo);
+
+ mv = QCStatement.getInstance(mv);
+
+ checkStatement(mv, statementId, statementInfo);
+
+ ASN1InputStream aIn = new ASN1InputStream(mv.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ mv = QCStatement.getInstance(seq);
+
+ checkStatement(mv, statementId, statementInfo);
+ }
+
+ private void checkStatement(
+ QCStatement qcs,
+ DERObjectIdentifier statementId,
+ ASN1Encodable statementInfo)
+ throws IOException
+ {
+ if (!qcs.getStatementId().equals(statementId))
+ {
+ fail("statementIds don't match.");
+ }
+
+ if (statementInfo != null)
+ {
+ if (!qcs.getStatementInfo().equals(statementInfo))
+ {
+ fail("statementInfos don't match.");
+ }
+ }
+ else if (qcs.getStatementInfo() != null)
+ {
+ fail("statementInfo found when none expected.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new QCStatementUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RFC4519Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RFC4519Test.java
new file mode 100644
index 000000000..52e5b5734
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RFC4519Test.java
@@ -0,0 +1,149 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+import org.spongycastle.asn1.x500.style.RFC4519Style;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class RFC4519Test
+ extends SimpleTest
+{
+ static String[] attributeTypes =
+ {
+ "businessCategory",
+ "c",
+ "cn",
+ "dc",
+ "description",
+ "destinationIndicator",
+ "distinguishedName",
+ "dnQualifier",
+ "enhancedSearchGuide",
+ "facsimileTelephoneNumber",
+ "generationQualifier",
+ "givenName",
+ "houseIdentifier",
+ "initials",
+ "internationalISDNNumber",
+ "l",
+ "member",
+ "name",
+ "o",
+ "ou",
+ "owner",
+ "physicalDeliveryOfficeName",
+ "postalAddress",
+ "postalCode",
+ "postOfficeBox",
+ "preferredDeliveryMethod",
+ "registeredAddress",
+ "roleOccupant",
+ "searchGuide",
+ "seeAlso",
+ "serialNumber",
+ "sn",
+ "st",
+ "street",
+ "telephoneNumber",
+ "teletexTerminalIdentifier",
+ "telexNumber",
+ "title",
+ "uid",
+ "uniqueMember",
+ "userPassword",
+ "x121Address",
+ "x500UniqueIdentifier"
+ };
+
+ static ASN1ObjectIdentifier[] attributeTypeOIDs =
+ {
+ new ASN1ObjectIdentifier("2.5.4.15"),
+ new ASN1ObjectIdentifier("2.5.4.6"),
+ new ASN1ObjectIdentifier("2.5.4.3"),
+ new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25"),
+ new ASN1ObjectIdentifier("2.5.4.13"),
+ new ASN1ObjectIdentifier("2.5.4.27"),
+ new ASN1ObjectIdentifier("2.5.4.49"),
+ new ASN1ObjectIdentifier("2.5.4.46"),
+ new ASN1ObjectIdentifier("2.5.4.47"),
+ new ASN1ObjectIdentifier("2.5.4.23"),
+ new ASN1ObjectIdentifier("2.5.4.44"),
+ new ASN1ObjectIdentifier("2.5.4.42"),
+ new ASN1ObjectIdentifier("2.5.4.51"),
+ new ASN1ObjectIdentifier("2.5.4.43"),
+ new ASN1ObjectIdentifier("2.5.4.25"),
+ new ASN1ObjectIdentifier("2.5.4.7"),
+ new ASN1ObjectIdentifier("2.5.4.31"),
+ new ASN1ObjectIdentifier("2.5.4.41"),
+ new ASN1ObjectIdentifier("2.5.4.10"),
+ new ASN1ObjectIdentifier("2.5.4.11"),
+ new ASN1ObjectIdentifier("2.5.4.32"),
+ new ASN1ObjectIdentifier("2.5.4.19"),
+ new ASN1ObjectIdentifier("2.5.4.16"),
+ new ASN1ObjectIdentifier("2.5.4.17"),
+ new ASN1ObjectIdentifier("2.5.4.18"),
+ new ASN1ObjectIdentifier("2.5.4.28"),
+ new ASN1ObjectIdentifier("2.5.4.26"),
+ new ASN1ObjectIdentifier("2.5.4.33"),
+ new ASN1ObjectIdentifier("2.5.4.14"),
+ new ASN1ObjectIdentifier("2.5.4.34"),
+ new ASN1ObjectIdentifier("2.5.4.5"),
+ new ASN1ObjectIdentifier("2.5.4.4"),
+ new ASN1ObjectIdentifier("2.5.4.8"),
+ new ASN1ObjectIdentifier("2.5.4.9"),
+ new ASN1ObjectIdentifier("2.5.4.20"),
+ new ASN1ObjectIdentifier("2.5.4.22"),
+ new ASN1ObjectIdentifier("2.5.4.21"),
+ new ASN1ObjectIdentifier("2.5.4.12"),
+ new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1"),
+ new ASN1ObjectIdentifier("2.5.4.50"),
+ new ASN1ObjectIdentifier("2.5.4.35"),
+ new ASN1ObjectIdentifier("2.5.4.24"),
+ new ASN1ObjectIdentifier("2.5.4.45")
+ };
+
+ public String getName()
+ {
+ return "RFC4519Test";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ X500NameStyle style = RFC4519Style.INSTANCE;
+
+ for (int i = 0; i != attributeTypes.length; i++)
+ {
+ if (!attributeTypeOIDs[i].equals(style.attrNameToOID(attributeTypes[i])))
+ {
+ fail("mismatch for " + attributeTypes[i]);
+ }
+ }
+
+ byte[] enc = Hex.decode("305e310b300906035504061302415531283026060355040a0c1f546865204c6567696f6e206f662074686520426f756e637920436173746c653125301006035504070c094d656c626f75726e653011060355040b0c0a4173636f742056616c65");
+
+ X500Name n = new X500Name(style, X500Name.getInstance(enc));
+
+ if (!n.toString().equals("l=Melbourne+ou=Ascot Vale,o=The Legion of the Bouncy Castle,c=AU"))
+ {
+ fail("Failed composite to string test got: " + n.toString());
+ }
+
+ n = new X500Name(style, "l=Melbourne+ou=Ascot Vale,o=The Legion of the Bouncy Castle,c=AU");
+
+ if (!Arrays.areEqual(n.getEncoded(), enc))
+ {
+ fail("re-encoding test after parse failed");
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RFC4519Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ReasonFlagsTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ReasonFlagsTest.java
new file mode 100644
index 000000000..343479146
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/ReasonFlagsTest.java
@@ -0,0 +1,35 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.x509.ReasonFlags;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ReasonFlagsTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ReasonFlags";
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ BitStringConstantTester.testFlagValueCorrect(0, ReasonFlags.unused);
+ BitStringConstantTester.testFlagValueCorrect(1, ReasonFlags.keyCompromise);
+ BitStringConstantTester.testFlagValueCorrect(2, ReasonFlags.cACompromise);
+ BitStringConstantTester.testFlagValueCorrect(3, ReasonFlags.affiliationChanged);
+ BitStringConstantTester.testFlagValueCorrect(4, ReasonFlags.superseded);
+ BitStringConstantTester.testFlagValueCorrect(5, ReasonFlags.cessationOfOperation);
+ BitStringConstantTester.testFlagValueCorrect(6, ReasonFlags.certificateHold);
+ BitStringConstantTester.testFlagValueCorrect(7, ReasonFlags.privilegeWithdrawn);
+ BitStringConstantTester.testFlagValueCorrect(8, ReasonFlags.aACompromise);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ReasonFlagsTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RegressionTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RegressionTest.java
new file mode 100644
index 000000000..5fa57fa94
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RegressionTest.java
@@ -0,0 +1,92 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class RegressionTest
+{
+ public static Test[] tests = {
+ new InputStreamTest(),
+ new EqualsAndHashCodeTest(),
+ new TagTest(),
+ new SetTest(),
+ new DERUTF8StringTest(),
+ new CertificateTest(),
+ new GenerationTest(),
+ new CMSTest(),
+ new OCSPTest(),
+ new OIDTest(),
+ new PKCS10Test(),
+ new PKCS12Test(),
+ new X509NameTest(),
+ new X500NameTest(),
+ new X509ExtensionsTest(),
+ new GeneralizedTimeTest(),
+ new BitStringTest(),
+ new MiscTest(),
+ new SMIMETest(),
+ new X9Test(),
+ new MonetaryValueUnitTest(),
+ new BiometricDataUnitTest(),
+ new Iso4217CurrencyCodeUnitTest(),
+ new SemanticsInformationUnitTest(),
+ new QCStatementUnitTest(),
+ new TypeOfBiometricDataUnitTest(),
+ new SignerLocationUnitTest(),
+ new CommitmentTypeQualifierUnitTest(),
+ new CommitmentTypeIndicationUnitTest(),
+ new EncryptedPrivateKeyInfoTest(),
+ new DataGroupHashUnitTest(),
+ new LDSSecurityObjectUnitTest(),
+ new CscaMasterListTest(),
+ new AttributeTableUnitTest(),
+ new ReasonFlagsTest(),
+ new NetscapeCertTypeTest(),
+ new PKIFailureInfoTest(),
+ new KeyUsageTest(),
+ new StringTest(),
+ new UTCTimeTest(),
+ new RequestedCertificateUnitTest(),
+ new OtherCertIDUnitTest(),
+ new OtherSigningCertificateUnitTest(),
+ new ContentHintsUnitTest(),
+ new CertHashUnitTest(),
+ new AdditionalInformationSyntaxUnitTest(),
+ new AdmissionSyntaxUnitTest(),
+ new AdmissionsUnitTest(),
+ new DeclarationOfMajorityUnitTest(),
+ new ProcurationSyntaxUnitTest(),
+ new ProfessionInfoUnitTest(),
+ new RestrictionUnitTest(),
+ new NamingAuthorityUnitTest(),
+ new MonetaryLimitUnitTest(),
+ new NameOrPseudonymUnitTest(),
+ new PersonalDataUnitTest(),
+ new DERApplicationSpecificTest(),
+ new IssuingDistributionPointUnitTest(),
+ new TargetInformationTest(),
+ new SubjectKeyIdentifierTest(),
+ new ESSCertIDv2UnitTest(),
+ new ParsingTest(),
+ new GeneralNameTest(),
+ new ObjectIdentifierTest(),
+ new RFC4519Test()
+ };
+
+ public static void main(
+ String[] args)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace();
+ }
+
+ System.out.println(result);
+ }
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RequestedCertificateUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RequestedCertificateUnitTest.java
new file mode 100644
index 000000000..389f07d3e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RequestedCertificateUnitTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.isismtt.ocsp.RequestedCertificate;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.util.encoders.Base64;
+
+public class RequestedCertificateUnitTest
+ extends ASN1UnitTest
+{
+ byte[] certBytes = Base64.decode(
+ "MIIBWzCCAQYCARgwDQYJKoZIhvcNAQEEBQAwODELMAkGA1UEBhMCQVUxDDAKBgNV"
+ + "BAgTA1FMRDEbMBkGA1UEAxMSU1NMZWF5L3JzYSB0ZXN0IENBMB4XDTk1MDYxOTIz"
+ + "MzMxMloXDTk1MDcxNzIzMzMxMlowOjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA1FM"
+ + "RDEdMBsGA1UEAxMUU1NMZWF5L3JzYSB0ZXN0IGNlcnQwXDANBgkqhkiG9w0BAQEF"
+ + "AANLADBIAkEAqtt6qS5GTxVxGZYWa0/4u+IwHf7p2LNZbcPBp9/OfIcYAXBQn8hO"
+ + "/Re1uwLKXdCjIoaGs4DLdG88rkzfyK5dPQIDAQABMAwGCCqGSIb3DQIFBQADQQAE"
+ + "Wc7EcF8po2/ZO6kNCwK/ICH6DobgLekA5lSLr5EvuioZniZp5lFzAw4+YzPQ7XKJ"
+ + "zl9HYIMxATFyqSiD9jsx");
+
+ public String getName()
+ {
+ return "RequestedCertificate";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ int type = 1;
+ byte[] certOctets = new byte[20];
+ Certificate cert = Certificate.getInstance(certBytes);
+
+ RequestedCertificate requested = new RequestedCertificate(type, certOctets);
+
+ checkConstruction(requested, type, certOctets, null);
+
+ requested = new RequestedCertificate(cert);
+
+ checkConstruction(requested, RequestedCertificate.certificate, null, cert);
+
+ requested = RequestedCertificate.getInstance(null);
+
+ if (requested != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ RequestedCertificate.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ RequestedCertificate requested,
+ int type,
+ byte[] certOctets,
+ Certificate cert)
+ throws IOException
+ {
+ checkValues(requested, type, certOctets, cert);
+
+ requested = RequestedCertificate.getInstance(requested);
+
+ checkValues(requested, type, certOctets, cert);
+
+ ASN1InputStream aIn = new ASN1InputStream(requested.toASN1Object().getEncoded());
+
+ Object obj = aIn.readObject();
+
+ requested = RequestedCertificate.getInstance(obj);
+
+ checkValues(requested, type, certOctets, cert);
+ }
+
+ private void checkValues(
+ RequestedCertificate requested,
+ int type,
+ byte[] certOctets,
+ Certificate cert)
+ throws IOException
+ {
+ checkMandatoryField("certType", type, requested.getType());
+
+ if (requested.getType() == RequestedCertificate.certificate)
+ {
+ checkMandatoryField("certificate", cert.getEncoded(), requested.getCertificateBytes());
+ }
+ else
+ {
+ checkMandatoryField("certificateOctets", certOctets, requested.getCertificateBytes());
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RequestedCertificateUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RestrictionUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RestrictionUnitTest.java
new file mode 100644
index 000000000..582d6851f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/RestrictionUnitTest.java
@@ -0,0 +1,70 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.isismtt.x509.Restriction;
+import org.spongycastle.asn1.x500.DirectoryString;
+
+public class RestrictionUnitTest
+ extends ASN1UnitTest
+{
+ public String getName()
+ {
+ return "Restriction";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DirectoryString res = new DirectoryString("test");
+ Restriction restriction = new Restriction(res.getString());
+
+ checkConstruction(restriction, res);
+
+ try
+ {
+ Restriction.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ Restriction restriction,
+ DirectoryString res)
+ throws IOException
+ {
+ checkValues(restriction, res);
+
+ restriction = Restriction.getInstance(restriction);
+
+ checkValues(restriction, res);
+
+ ASN1InputStream aIn = new ASN1InputStream(restriction.toASN1Object().getEncoded());
+
+ ASN1String str = (ASN1String)aIn.readObject();
+
+ restriction = Restriction.getInstance(str);
+
+ checkValues(restriction, res);
+ }
+
+ private void checkValues(
+ Restriction restriction,
+ DirectoryString res)
+ {
+ checkMandatoryField("restriction", res, restriction.getRestriction());
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RestrictionUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SMIMETest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SMIMETest.java
new file mode 100644
index 000000000..e6330386f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SMIMETest.java
@@ -0,0 +1,109 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.cms.RecipientKeyIdentifier;
+import org.spongycastle.asn1.smime.SMIMECapabilitiesAttribute;
+import org.spongycastle.asn1.smime.SMIMECapability;
+import org.spongycastle.asn1.smime.SMIMECapabilityVector;
+import org.spongycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class SMIMETest
+ implements Test
+{
+ byte[] attrBytes = Base64.decode("MDQGCSqGSIb3DQEJDzEnMCUwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMAcGBSsOAwIH");
+ byte[] prefBytes = Base64.decode("MCwGCyqGSIb3DQEJEAILMR2hGwQIAAAAAAAAAAAYDzIwMDcwMzE1MTczNzI5Wg==");
+
+ private boolean isSameAs(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public TestResult perform()
+ {
+ SMIMECapabilityVector caps = new SMIMECapabilityVector();
+
+ caps.addCapability(SMIMECapability.dES_EDE3_CBC);
+ caps.addCapability(SMIMECapability.rC2_CBC, 128);
+ caps.addCapability(SMIMECapability.dES_CBC);
+
+ SMIMECapabilitiesAttribute attr = new SMIMECapabilitiesAttribute(caps);
+
+ SMIMEEncryptionKeyPreferenceAttribute pref = new SMIMEEncryptionKeyPreferenceAttribute(
+ new RecipientKeyIdentifier(new DEROctetString(new byte[8]), new DERGeneralizedTime("20070315173729Z"), null));
+
+ try
+ {
+ if (!isSameAs(attr.getEncoded(), attrBytes))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed attr data check");
+ }
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(attrBytes);
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+
+ ASN1Primitive o = aIn.readObject();
+ if (!attr.equals(o))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed equality test for attr");
+ }
+
+ if (!isSameAs(pref.getEncoded(), prefBytes))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed attr data check");
+ }
+
+ bIn = new ByteArrayInputStream(prefBytes);
+ aIn = new ASN1InputStream(bIn);
+
+ o = aIn.readObject();
+ if (!pref.equals(o))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed equality test for pref");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": Failed - exception " + e.toString(), e);
+ }
+ }
+
+ public String getName()
+ {
+ return "SMIME";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SMIMETest test = new SMIMETest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SemanticsInformationUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SemanticsInformationUnitTest.java
new file mode 100644
index 000000000..6c437485f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SemanticsInformationUnitTest.java
@@ -0,0 +1,136 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.asn1.x509.qualified.SemanticsInformation;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SemanticsInformationUnitTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "SemanticsInformation";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ ASN1ObjectIdentifier statementId = new ASN1ObjectIdentifier("1.1");
+ SemanticsInformation mv = new SemanticsInformation(statementId);
+
+ checkConstruction(mv, statementId, null);
+
+ GeneralName[] names = new GeneralName[2];
+
+ names[0] = new GeneralName(GeneralName.rfc822Name, "test@test.org");
+ names[1] = new GeneralName(new X509Name("cn=test"));
+
+ mv = new SemanticsInformation(statementId, names);
+
+ checkConstruction(mv, statementId, names);
+
+ mv = new SemanticsInformation(names);
+
+ checkConstruction(mv, null, names);
+
+ mv = SemanticsInformation.getInstance(null);
+
+ if (mv != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ SemanticsInformation.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ SemanticsInformation.getInstance(new DERSequence(v));
+
+ fail("constructor failed to detect empty sequence.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ SemanticsInformation mv,
+ DERObjectIdentifier semanticsIdentifier,
+ GeneralName[] names)
+ throws Exception
+ {
+ checkStatement(mv, semanticsIdentifier, names);
+
+ mv = SemanticsInformation.getInstance(mv);
+
+ checkStatement(mv, semanticsIdentifier, names);
+
+ ASN1InputStream aIn = new ASN1InputStream(mv.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ mv = SemanticsInformation.getInstance(seq);
+
+ checkStatement(mv, semanticsIdentifier, names);
+ }
+
+ private void checkStatement(
+ SemanticsInformation si,
+ DERObjectIdentifier id,
+ GeneralName[] names)
+ {
+ if (id != null)
+ {
+ if (!si.getSemanticsIdentifier().equals(id))
+ {
+ fail("ids don't match.");
+ }
+ }
+ else if (si.getSemanticsIdentifier() != null)
+ {
+ fail("statementId found when none expected.");
+ }
+
+ if (names != null)
+ {
+ GeneralName[] siNames = si.getNameRegistrationAuthorities();
+
+ for (int i = 0; i != siNames.length; i++)
+ {
+ if (!names[i].equals(siNames[i]))
+ {
+ fail("name registration authorities don't match.");
+ }
+ }
+ }
+ else if (si.getNameRegistrationAuthorities() != null)
+ {
+ fail("name registration authorities found when none expected.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SemanticsInformationUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SetTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SetTest.java
new file mode 100644
index 000000000..973c1cb9a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SetTest.java
@@ -0,0 +1,115 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.BERSet;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERBoolean;
+import org.spongycastle.asn1.DERInteger;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Set sorting test example
+ */
+public class SetTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "Set";
+ }
+
+ private void checkedSortedSet(int attempt, ASN1Set s)
+ {
+ if (s.getObjectAt(0) instanceof DERBoolean
+ && s.getObjectAt(1) instanceof DERInteger
+ && s.getObjectAt(2) instanceof DERBitString
+ && s.getObjectAt(3) instanceof DEROctetString)
+ {
+ return;
+ }
+
+ fail("sorting failed on attempt: " + attempt);
+ }
+
+ public void performTest()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ byte[] data = new byte[10];
+
+ v.add(new DEROctetString(data));
+ v.add(new DERBitString(data));
+ v.add(new DERInteger(100));
+ v.add(new DERBoolean(true));
+
+ checkedSortedSet(0, new DERSet(v));
+
+ v = new ASN1EncodableVector();
+ v.add(new DERInteger(100));
+ v.add(new DERBoolean(true));
+ v.add(new DEROctetString(data));
+ v.add(new DERBitString(data));
+
+ checkedSortedSet(1, new DERSet(v));
+
+ v = new ASN1EncodableVector();
+ v.add(new DERBoolean(true));
+ v.add(new DEROctetString(data));
+ v.add(new DERBitString(data));
+ v.add(new DERInteger(100));
+
+
+ checkedSortedSet(2, new DERSet(v));
+
+ v = new ASN1EncodableVector();
+ v.add(new DERBitString(data));
+ v.add(new DEROctetString(data));
+ v.add(new DERInteger(100));
+ v.add(new DERBoolean(true));
+
+ checkedSortedSet(3, new DERSet(v));
+
+ v = new ASN1EncodableVector();
+ v.add(new DEROctetString(data));
+ v.add(new DERBitString(data));
+ v.add(new DERInteger(100));
+ v.add(new DERBoolean(true));
+
+ ASN1Set s = new BERSet(v);
+
+ if (!(s.getObjectAt(0) instanceof DEROctetString))
+ {
+ fail("BER set sort order changed.");
+ }
+
+ // create an implicitly tagged "set" without sorting
+ ASN1TaggedObject tag = new DERTaggedObject(false, 1, new DERSequence(v));
+ s = ASN1Set.getInstance(tag, false);
+
+ if (s.getObjectAt(0) instanceof DERBoolean)
+ {
+ fail("sorted when shouldn't be.");
+ }
+
+ // equality test
+ v = new ASN1EncodableVector();
+
+ v.add(new DERBoolean(true));
+ v.add(new DERBoolean(true));
+ v.add(new DERBoolean(true));
+
+ s = new DERSet(v);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SetTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SignerLocationUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SignerLocationUnitTest.java
new file mode 100644
index 000000000..44b1f66e0
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SignerLocationUnitTest.java
@@ -0,0 +1,197 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.esf.SignerLocation;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SignerLocationUnitTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "SignerLocation";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DERUTF8String countryName = new DERUTF8String("Australia");
+
+ SignerLocation sl = new SignerLocation(countryName, null, null);
+
+ checkConstruction(sl, countryName, null, null);
+
+ DERUTF8String localityName = new DERUTF8String("Melbourne");
+
+ sl = new SignerLocation(null, localityName, null);
+
+ checkConstruction(sl, null, localityName, null);
+
+ sl = new SignerLocation(countryName, localityName, null);
+
+ checkConstruction(sl, countryName, localityName, null);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new DERUTF8String("line 1"));
+ v.add(new DERUTF8String("line 2"));
+
+ ASN1Sequence postalAddress = new DERSequence(v);
+
+ sl = new SignerLocation(null, null, postalAddress);
+
+ checkConstruction(sl, null, null, postalAddress);
+
+ sl = new SignerLocation(countryName, null, postalAddress);
+
+ checkConstruction(sl, countryName, null, postalAddress);
+
+ sl = new SignerLocation(countryName, localityName, postalAddress);
+
+ checkConstruction(sl, countryName, localityName, postalAddress);
+
+ sl = SignerLocation.getInstance(null);
+
+ if (sl != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ SignerLocation.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ //
+ // out of range postal address
+ //
+ v = new ASN1EncodableVector();
+
+ v.add(new DERUTF8String("line 1"));
+ v.add(new DERUTF8String("line 2"));
+ v.add(new DERUTF8String("line 3"));
+ v.add(new DERUTF8String("line 4"));
+ v.add(new DERUTF8String("line 5"));
+ v.add(new DERUTF8String("line 6"));
+ v.add(new DERUTF8String("line 7"));
+
+ postalAddress = new DERSequence(v);
+
+ try
+ {
+ new SignerLocation(null, null, postalAddress);
+
+ fail("constructor failed to detect bad postalAddress.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ SignerLocation.getInstance(new DERSequence(new DERTaggedObject(2, postalAddress)));
+
+ fail("sequence constructor failed to detect bad postalAddress.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ SignerLocation.getInstance(new DERSequence(new DERTaggedObject(5, postalAddress)));
+
+ fail("sequence constructor failed to detect bad tag.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkConstruction(
+ SignerLocation sl,
+ DERUTF8String countryName,
+ DERUTF8String localityName,
+ ASN1Sequence postalAddress)
+ throws IOException
+ {
+ checkValues(sl, countryName, localityName, postalAddress);
+
+ sl = SignerLocation.getInstance(sl);
+
+ checkValues(sl, countryName, localityName, postalAddress);
+
+ ASN1InputStream aIn = new ASN1InputStream(sl.toASN1Object().getEncoded());
+
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ sl = SignerLocation.getInstance(seq);
+
+ checkValues(sl, countryName, localityName, postalAddress);
+ }
+
+ private void checkValues(
+ SignerLocation sl,
+ DERUTF8String countryName,
+ DERUTF8String localityName,
+ ASN1Sequence postalAddress)
+ {
+ if (countryName != null)
+ {
+ if (!countryName.equals(sl.getCountryName()))
+ {
+ fail("countryNames don't match.");
+ }
+ }
+ else if (sl.getCountryName() != null)
+ {
+ fail("countryName found when none expected.");
+ }
+
+ if (localityName != null)
+ {
+ if (!localityName.equals(sl.getLocalityName()))
+ {
+ fail("localityNames don't match.");
+ }
+ }
+ else if (sl.getLocalityName() != null)
+ {
+ fail("localityName found when none expected.");
+ }
+
+ if (postalAddress != null)
+ {
+ if (!postalAddress.equals(sl.getPostalAddress()))
+ {
+ fail("postalAddresses don't match.");
+ }
+ }
+ else if (sl.getPostalAddress() != null)
+ {
+ fail("postalAddress found when none expected.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SignerLocationUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/StringTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/StringTest.java
new file mode 100644
index 000000000..acf29cf5c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/StringTest.java
@@ -0,0 +1,161 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.DERBMPString;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERGeneralString;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERNumericString;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERT61String;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.DERUniversalString;
+import org.spongycastle.asn1.DERVisibleString;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * X.690 test example
+ */
+public class StringTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "String";
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ DERBitString bs = new DERBitString(
+ new byte[] { (byte)0x01,(byte)0x23,(byte)0x45,(byte)0x67,(byte)0x89,(byte)0xab,(byte)0xcd,(byte)0xef });
+
+ if (!bs.getString().equals("#0309000123456789ABCDEF"))
+ {
+ fail("DERBitString.getString() result incorrect");
+ }
+
+ if (!bs.toString().equals("#0309000123456789ABCDEF"))
+ {
+ fail("DERBitString.toString() result incorrect");
+ }
+
+ bs = new DERBitString(
+ new byte[] { (byte)0xfe,(byte)0xdc,(byte)0xba,(byte)0x98,(byte)0x76,(byte)0x54,(byte)0x32,(byte)0x10 });
+
+ if (!bs.getString().equals("#030900FEDCBA9876543210"))
+ {
+ fail("DERBitString.getString() result incorrect");
+ }
+
+ if (!bs.toString().equals("#030900FEDCBA9876543210"))
+ {
+ fail("DERBitString.toString() result incorrect");
+ }
+
+ DERUniversalString us = new DERUniversalString(
+ new byte[] { (byte)0x01,(byte)0x23,(byte)0x45,(byte)0x67,(byte)0x89,(byte)0xab,(byte)0xcd,(byte)0xef });
+
+ if (!us.getString().equals("#1C080123456789ABCDEF"))
+ {
+ fail("DERUniversalString.getString() result incorrect");
+ }
+
+ if (!us.toString().equals("#1C080123456789ABCDEF"))
+ {
+ fail("DERUniversalString.toString() result incorrect");
+ }
+
+ us = new DERUniversalString(
+ new byte[] { (byte)0xfe,(byte)0xdc,(byte)0xba,(byte)0x98,(byte)0x76,(byte)0x54,(byte)0x32,(byte)0x10 });
+
+ if (!us.getString().equals("#1C08FEDCBA9876543210"))
+ {
+ fail("DERUniversalString.getString() result incorrect");
+ }
+
+ if (!us.toString().equals("#1C08FEDCBA9876543210"))
+ {
+ fail("DERUniversalString.toString() result incorrect");
+ }
+
+ byte[] t61Bytes = new byte[] { -1, -2, -3, -4, -5, -6, -7, -8 };
+ String t61String = new String(t61Bytes, "iso-8859-1");
+ DERT61String t61 = new DERT61String(Strings.fromByteArray(t61Bytes));
+
+ if (!t61.getString().equals(t61String))
+ {
+ fail("DERT61String.getString() result incorrect");
+ }
+
+ if (!t61.toString().equals(t61String))
+ {
+ fail("DERT61String.toString() result incorrect");
+ }
+
+ char[] shortChars = new char[] { 'a', 'b', 'c', 'd', 'e'};
+ char[] longChars = new char[1000];
+
+ for (int i = 0; i != longChars.length; i++)
+ {
+ longChars[i] = 'X';
+ }
+
+ checkString(new DERBMPString(new String(shortChars)), new DERBMPString(new String(longChars)));
+ checkString(new DERUTF8String(new String(shortChars)), new DERUTF8String(new String(longChars)));
+ checkString(new DERIA5String(new String(shortChars)), new DERIA5String(new String(longChars)));
+ checkString(new DERPrintableString(new String(shortChars)), new DERPrintableString(new String(longChars)));
+ checkString(new DERVisibleString(new String(shortChars)), new DERVisibleString(new String(longChars)));
+ checkString(new DERGeneralString(new String(shortChars)), new DERGeneralString(new String(longChars)));
+ checkString(new DERT61String(new String(shortChars)), new DERT61String(new String(longChars)));
+
+ shortChars = new char[] { '1', '2', '3', '4', '5'};
+ longChars = new char[1000];
+
+ for (int i = 0; i != longChars.length; i++)
+ {
+ longChars[i] = '1';
+ }
+
+ checkString(new DERNumericString(new String(shortChars)), new DERNumericString(new String(longChars)));
+
+ byte[] shortBytes = new byte[] { (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e'};
+ byte[] longBytes = new byte[1000];
+
+ for (int i = 0; i != longChars.length; i++)
+ {
+ longBytes[i] = (byte)'X';
+ }
+
+ checkString(new DERUniversalString(shortBytes), new DERUniversalString(longBytes));
+
+ }
+
+ private void checkString(ASN1String shortString, ASN1String longString)
+ throws IOException
+ {
+ ASN1String short2 = (ASN1String)ASN1Primitive.fromByteArray(((ASN1Primitive)shortString).getEncoded());
+
+ if (!shortString.toString().equals(short2.toString()))
+ {
+ fail(short2.getClass().getName() + " shortBytes result incorrect");
+ }
+
+ ASN1String long2 = (ASN1String)ASN1Primitive.fromByteArray(((ASN1Primitive)longString).getEncoded());
+
+ if (!longString.toString().equals(long2.toString()))
+ {
+ fail(long2.getClass().getName() + " longBytes result incorrect");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new StringTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SubjectKeyIdentifierTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SubjectKeyIdentifierTest.java
new file mode 100644
index 000000000..469e25ea5
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/SubjectKeyIdentifierTest.java
@@ -0,0 +1,52 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SubjectKeyIdentifierTest
+ extends SimpleTest
+{
+ private static byte[] pubKeyInfo = Base64.decode(
+ "MFgwCwYJKoZIhvcNAQEBA0kAMEYCQQC6wMMmHYMZszT/7bNFMn+gaZoiWJLVP8ODRuu1C2jeAe" +
+ "QpxM+5Oe7PaN2GNy3nBE4EOYkB5pMJWA0y9n04FX8NAgED");
+
+ private static byte[] shaID = Hex.decode("d8128a06d6c2feb0865994a2936e7b75b836a021");
+ private static byte[] shaTruncID = Hex.decode("436e7b75b836a021");
+
+ public String getName()
+ {
+ return "SubjectKeyIdentifier";
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pubKeyInfo));
+ SubjectKeyIdentifier ski = SubjectKeyIdentifier.createSHA1KeyIdentifier(pubInfo);
+
+ if (!Arrays.areEqual(shaID, ski.getKeyIdentifier()))
+ {
+ fail("SHA-1 ID does not match");
+ }
+
+ ski = SubjectKeyIdentifier.createTruncatedSHA1KeyIdentifier(pubInfo);
+
+ if (!Arrays.areEqual(shaTruncID, ski.getKeyIdentifier()))
+ {
+ fail("truncated SHA-1 ID does not match");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SubjectKeyIdentifierTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TagTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TagTest.java
new file mode 100644
index 000000000..cd497de30
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TagTest.java
@@ -0,0 +1,113 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERApplicationSpecific;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+
+/**
+ * X.690 test example
+ */
+public class TagTest
+ extends SimpleTest
+{
+ byte[] longTagged = Base64.decode(
+ "ZSRzIp8gEEZFRENCQTk4NzY1NDMyMTCfIQwyMDA2MDQwMTEyMzSUCCAFERVz"
+ + "A4kCAHEXGBkalAggBRcYGRqUCCAFZS6QAkRFkQlURUNITklLRVKSBQECAwQF"
+ + "kxAREhMUFRYXGBkalAggBREVcwOJAgBxFxgZGpQIIAUXGBkalAggBWUukAJE"
+ + "RZEJVEVDSE5JS0VSkgUBAgMEBZMQERITFBUWFxgZGpQIIAURFXMDiQIAcRcY"
+ + "GRqUCCAFFxgZGpQIIAVlLpACREWRCVRFQ0hOSUtFUpIFAQIDBAWTEBESExQV"
+ + "FhcYGRqUCCAFERVzA4kCAHEXGBkalAggBRcYGRqUCCAFFxgZGpQIIAUXGBka"
+ + "lAg=");
+
+ byte[] longAppSpecificTag = Hex.decode("5F610101");
+
+ public String getName()
+ {
+ return "Tag";
+ }
+
+ public void performTest()
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(longTagged);
+
+ DERApplicationSpecific app = (DERApplicationSpecific)aIn.readObject();
+
+ aIn = new ASN1InputStream(app.getContents());
+
+ app = (DERApplicationSpecific)aIn.readObject();
+
+ aIn = new ASN1InputStream(app.getContents());
+
+ ASN1TaggedObject tagged = (ASN1TaggedObject)aIn.readObject();
+
+ if (tagged.getTagNo() != 32)
+ {
+ fail("unexpected tag value found - not 32");
+ }
+
+ tagged = (ASN1TaggedObject)ASN1Primitive.fromByteArray(tagged.getEncoded());
+
+ if (tagged.getTagNo() != 32)
+ {
+ fail("unexpected tag value found on recode - not 32");
+ }
+
+ tagged = (ASN1TaggedObject)aIn.readObject();
+
+ if (tagged.getTagNo() != 33)
+ {
+ fail("unexpected tag value found - not 33");
+ }
+
+ tagged = (ASN1TaggedObject)ASN1Primitive.fromByteArray(tagged.getEncoded());
+
+ if (tagged.getTagNo() != 33)
+ {
+ fail("unexpected tag value found on recode - not 33");
+ }
+
+ aIn = new ASN1InputStream(longAppSpecificTag);
+
+ app = (DERApplicationSpecific)aIn.readObject();
+
+ if (app.getApplicationTag() != 97)
+ {
+ fail("incorrect tag number read");
+ }
+
+ app = (DERApplicationSpecific)ASN1Primitive.fromByteArray(app.getEncoded());
+
+ if (app.getApplicationTag() != 97)
+ {
+ fail("incorrect tag number read on recode");
+ }
+
+ SecureRandom sr = new SecureRandom();
+ for (int i = 0; i < 100; ++i)
+ {
+ int testTag = sr.nextInt() >>> (1 + (sr.nextInt() >>> 1) % 26);
+ app = new DERApplicationSpecific(testTag, new byte[]{ 1 });
+ app = (DERApplicationSpecific)ASN1Primitive.fromByteArray(app.getEncoded());
+
+ if (app.getApplicationTag() != testTag)
+ {
+ fail("incorrect tag number read on recode (random test value: " + testTag + ")");
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TagTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TargetInformationTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TargetInformationTest.java
new file mode 100644
index 000000000..225cb4b87
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TargetInformationTest.java
@@ -0,0 +1,48 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.Target;
+import org.spongycastle.asn1.x509.TargetInformation;
+import org.spongycastle.asn1.x509.Targets;
+import org.spongycastle.util.test.SimpleTest;
+
+public class TargetInformationTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "TargetInformation";
+ }
+
+ public void performTest() throws Exception
+ {
+ Target[] targets = new Target[2];
+ Target targetName = new Target(Target.targetName, new GeneralName(GeneralName.dNSName, "www.test.com"));
+ Target targetGroup = new Target(Target.targetGroup, new GeneralName(GeneralName.directoryName, "o=Test, ou=Test"));
+ targets[0] = targetName;
+ targets[1] = targetGroup;
+ Targets targetss = new Targets(targets);
+ TargetInformation targetInformation1 = new TargetInformation(targetss);
+ // use an Target array
+ TargetInformation targetInformation2 = new TargetInformation(targets);
+ // targetInformation1 and targetInformation2 must have same
+ // encoding.
+ if (!targetInformation1.equals(targetInformation2))
+ {
+ fail("targetInformation1 and targetInformation2 should have the same encoding.");
+ }
+ TargetInformation targetInformation3 = TargetInformation.getInstance(targetInformation1);
+ TargetInformation targetInformation4 = TargetInformation.getInstance(targetInformation2);
+ if (!targetInformation3.equals(targetInformation4))
+ {
+ fail("targetInformation3 and targetInformation4 should have the same encoding.");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new TargetInformationTest());
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TypeOfBiometricDataUnitTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TypeOfBiometricDataUnitTest.java
new file mode 100644
index 000000000..9df1454d4
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/TypeOfBiometricDataUnitTest.java
@@ -0,0 +1,145 @@
+package org.spongycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.x509.qualified.TypeOfBiometricData;
+import org.spongycastle.util.test.SimpleTest;
+
+public class TypeOfBiometricDataUnitTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "TypeOfBiometricData";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ //
+ // predefined
+ //
+ checkPredefinedType(TypeOfBiometricData.PICTURE);
+
+ checkPredefinedType(TypeOfBiometricData.HANDWRITTEN_SIGNATURE);
+
+ //
+ // non-predefined
+ //
+ ASN1ObjectIdentifier localType = new ASN1ObjectIdentifier("1.1");
+
+ TypeOfBiometricData type = new TypeOfBiometricData(localType);
+
+ checkNonPredefined(type, localType);
+
+ type = TypeOfBiometricData.getInstance(type);
+
+ checkNonPredefined(type, localType);
+
+ ASN1Primitive obj = type.toASN1Primitive();
+
+ type = TypeOfBiometricData.getInstance(obj);
+
+ checkNonPredefined(type, localType);
+
+ type = TypeOfBiometricData.getInstance(null);
+
+ if (type != null)
+ {
+ fail("null getInstance() failed.");
+ }
+
+ try
+ {
+ TypeOfBiometricData.getInstance(new Object());
+
+ fail("getInstance() failed to detect bad object.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new TypeOfBiometricData(100);
+
+ fail("constructor failed to detect bad predefined type.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ if (TypeOfBiometricData.PICTURE != 0)
+ {
+ fail("predefined picture should be 0");
+ }
+
+ if (TypeOfBiometricData.HANDWRITTEN_SIGNATURE != 1)
+ {
+ fail("predefined handwritten signature should be 1");
+ }
+ }
+
+ private void checkPredefinedType(
+ int predefinedType)
+ throws IOException
+ {
+ TypeOfBiometricData type = new TypeOfBiometricData(predefinedType);
+
+ checkPredefined(type, predefinedType);
+
+ type = TypeOfBiometricData.getInstance(type);
+
+ checkPredefined(type, predefinedType);
+
+ ASN1InputStream aIn = new ASN1InputStream(type.toASN1Object().getEncoded());
+
+ ASN1Primitive obj = aIn.readObject();
+
+ type = TypeOfBiometricData.getInstance(obj);
+
+ checkPredefined(type, predefinedType);
+ }
+
+ private void checkPredefined(
+ TypeOfBiometricData type,
+ int value)
+ {
+ if (!type.isPredefined())
+ {
+ fail("predefined type expected but not found.");
+ }
+
+ if (type.getPredefinedBiometricType() != value)
+ {
+ fail("predefined type does not match.");
+ }
+ }
+
+ private void checkNonPredefined(
+ TypeOfBiometricData type,
+ DERObjectIdentifier value)
+ {
+ if (type.isPredefined())
+ {
+ fail("predefined type found when not expected.");
+ }
+
+ if (!type.getBiometricDataOid().equals(value))
+ {
+ fail("data oid does not match.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TypeOfBiometricDataUnitTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/UTCTimeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/UTCTimeTest.java
new file mode 100644
index 000000000..b28fb0e38
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/UTCTimeTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.DERUTCTime;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.text.SimpleDateFormat;
+import java.util.SimpleTimeZone;
+
+/**
+ * X.690 test example
+ */
+public class UTCTimeTest
+ extends SimpleTest
+{
+ String[] input =
+ {
+ "020122122220Z",
+ "020122122220-1000",
+ "020122122220+1000",
+ "020122122220+00",
+ "0201221222Z",
+ "0201221222-1000",
+ "0201221222+1000",
+ "0201221222+00",
+ "550122122220Z",
+ "5501221222Z"
+ };
+
+ String[] output = {
+ "20020122122220GMT+00:00",
+ "20020122122220GMT-10:00",
+ "20020122122220GMT+10:00",
+ "20020122122220GMT+00:00",
+ "20020122122200GMT+00:00",
+ "20020122122200GMT-10:00",
+ "20020122122200GMT+10:00",
+ "20020122122200GMT+00:00",
+ "19550122122220GMT+00:00",
+ "19550122122200GMT+00:00"
+ };
+
+ String[] zOutput1 = {
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122022220Z",
+ "20020122122220Z",
+ "20020122122200Z",
+ "20020122222200Z",
+ "20020122022200Z",
+ "20020122122200Z",
+ "19550122122220Z",
+ "19550122122200Z"
+ };
+
+ String[] zOutput2 = {
+ "20020122122220Z",
+ "20020122222220Z",
+ "20020122022220Z",
+ "20020122122220Z",
+ "20020122122200Z",
+ "20020122222200Z",
+ "20020122022200Z",
+ "20020122122200Z",
+ "19550122122220Z",
+ "19550122122200Z"
+ };
+
+ public String getName()
+ {
+ return "UTCTime";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ SimpleDateFormat yyyyF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+ SimpleDateFormat yyF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ yyyyF.setTimeZone(new SimpleTimeZone(0,"Z"));
+ yyF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ for (int i = 0; i != input.length; i++)
+ {
+ DERUTCTime t = new DERUTCTime(input[i]);
+
+ if (!t.getAdjustedTime().equals(output[i]))
+ {
+ fail("failed conversion test " + i);
+ }
+
+ if (!yyyyF.format(t.getAdjustedDate()).equals(zOutput1[i]))
+ {
+ fail("failed date conversion test " + i);
+ }
+
+ if (!yyF.format(t.getDate()).equals(zOutput2[i]))
+ {
+ fail("failed date shortened conversion test " + i);
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new UTCTimeTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X500NameTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X500NameTest.java
new file mode 100644
index 000000000..94ced56e8
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X500NameTest.java
@@ -0,0 +1,771 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameBuilder;
+import org.spongycastle.asn1.x500.style.BCStrictStyle;
+import org.spongycastle.asn1.x500.style.BCStyle;
+import org.spongycastle.asn1.x500.style.IETFUtils;
+import org.spongycastle.asn1.x509.X509DefaultEntryConverter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class X500NameTest
+ extends SimpleTest
+{
+ String[] subjects =
+ {
+ "C=AU,ST=Victoria,L=South Melbourne,O=Connect 4 Pty Ltd,OU=Webserver Team,CN=www2.connect4.com.au,E=webmaster@connect4.com.au",
+ "C=AU,ST=Victoria,L=South Melbourne,O=Connect 4 Pty Ltd,OU=Certificate Authority,CN=Connect 4 CA,E=webmaster@connect4.com.au",
+ "C=AU,ST=QLD,CN=SSLeay/rsa test cert",
+ "C=US,O=National Aeronautics and Space Administration,SERIALNUMBER=16+CN=Steve Schoch",
+ "E=cooke@issl.atl.hp.com,C=US,OU=Hewlett Packard Company (ISSL),CN=Paul A. Cooke",
+ "O=Sun Microsystems Inc,CN=store.sun.com",
+ "unstructuredAddress=192.168.1.33,unstructuredName=pixfirewall.ciscopix.com,CN=pixfirewall.ciscopix.com",
+ "CN=*.canal-plus.com,OU=Provided by TBS INTERNET http://www.tbs-certificats.com/,OU=\\ CANAL \\+,O=CANAL\\+DISTRIBUTION,L=issy les moulineaux,ST=Hauts de Seine,C=FR",
+ "O=Bouncy Castle,CN=www.bouncycastle.org\\ ",
+ "O=Bouncy Castle,CN=c:\\\\fred\\\\bob",
+ };
+
+ String[] hexSubjects =
+ {
+ "CN=\\20Test\\20X,O=\\20Test,C=GB", // input
+ "CN=\\ Test X,O=\\ Test,C=GB", // expected
+ "CN=\\20Test\\20X\\20,O=\\20Test,C=GB", // input
+ "CN=\\ Test X\\ ,O=\\ Test,C=GB" // expected
+ };
+
+ public String getName()
+ {
+ return "X500Name";
+ }
+
+ private static X500Name fromBytes(
+ byte[] bytes)
+ throws IOException
+ {
+ return X500Name.getInstance(new ASN1InputStream(new ByteArrayInputStream(bytes)).readObject());
+ }
+
+ private ASN1Encodable createEntryValue(ASN1ObjectIdentifier oid, String value)
+ {
+ X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
+
+ builder.addRDN(oid, value);
+
+ X500Name name = builder.build();
+
+ ASN1Sequence seq = (ASN1Sequence)name.toASN1Primitive();
+ ASN1Set set = ASN1Set.getInstance(seq.getObjectAt(0).toASN1Primitive());
+ seq = (ASN1Sequence)set.getObjectAt(0);
+
+ return seq.getObjectAt(1);
+ }
+
+ private ASN1Encodable createEntryValueFromString(ASN1ObjectIdentifier oid, String value)
+ {
+ X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
+
+ builder.addRDN(oid, value);
+
+ X500Name name = new X500Name(builder.build().toString());
+
+ ASN1Sequence seq = (ASN1Sequence)name.toASN1Primitive();
+ ASN1Set set = ASN1Set.getInstance(seq.getObjectAt(0).toASN1Primitive());
+ seq = (ASN1Sequence)set.getObjectAt(0);
+
+ return seq.getObjectAt(1);
+ }
+
+ private void testEncodingPrintableString(ASN1ObjectIdentifier oid, String value)
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERPrintableString))
+ {
+ fail("encoding for " + oid + " not printable string");
+ }
+ }
+
+ private void testEncodingIA5String(ASN1ObjectIdentifier oid, String value)
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERIA5String))
+ {
+ fail("encoding for " + oid + " not IA5String");
+ }
+ }
+
+ private void testEncodingUTF8String(ASN1ObjectIdentifier oid, String value)
+ throws IOException
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERUTF8String))
+ {
+ fail("encoding for " + oid + " not IA5String");
+ }
+ if (!value.equals((DERUTF8String.getInstance(converted.toASN1Primitive().getEncoded()).getString())))
+ {
+ fail("decoding not correct");
+ }
+ }
+
+ private void testEncodingGeneralizedTime(ASN1ObjectIdentifier oid, String value)
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERGeneralizedTime))
+ {
+ fail("encoding for " + oid + " not GeneralizedTime");
+ }
+ converted = createEntryValueFromString(oid, value);
+ if (!(converted instanceof DERGeneralizedTime))
+ {
+ fail("encoding for " + oid + " not GeneralizedTime");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ ietfUtilsTest();
+
+ testEncodingPrintableString(BCStyle.C, "AU");
+ testEncodingPrintableString(BCStyle.SERIALNUMBER, "123456");
+ testEncodingPrintableString(BCStyle.DN_QUALIFIER, "123456");
+ testEncodingIA5String(BCStyle.EmailAddress, "test@test.com");
+ testEncodingIA5String(BCStyle.DC, "test");
+ // correct encoding
+ testEncodingGeneralizedTime(BCStyle.DATE_OF_BIRTH, "#180F32303032303132323132323232305A");
+ // compatibility encoding
+ testEncodingGeneralizedTime(BCStyle.DATE_OF_BIRTH, "20020122122220Z");
+ testEncodingUTF8String(BCStyle.CN, "Mörsky");
+
+ //
+ // composite
+ //
+ X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
+
+ builder.addRDN(BCStyle.C, "AU");
+ builder.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
+ builder.addRDN(BCStyle.L, "Melbourne");
+ builder.addRDN(BCStyle.ST, "Victoria");
+ builder.addRDN(BCStyle.E, "feedback-crypto@bouncycastle.org");
+
+ X500Name name1 = builder.build();
+
+ if (!name1.equals(name1))
+ {
+ fail("Failed same object test");
+ }
+
+// if (!name1.equals(name1, true))
+// {
+// fail("Failed same object test - in Order");
+// }
+
+ builder = new X500NameBuilder(BCStyle.INSTANCE);
+
+ builder.addRDN(BCStyle.C, "AU");
+ builder.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
+ builder.addRDN(BCStyle.L, "Melbourne");
+ builder.addRDN(BCStyle.ST, "Victoria");
+ builder.addRDN(BCStyle.E, "feedback-crypto@bouncycastle.org");
+
+ X500Name name2 = builder.build();
+
+ if (!name1.equals(name2))
+ {
+ fail("Failed same name test");
+ }
+
+// if (!name1.equals(name2, true))
+// {
+// fail("Failed same name test - in Order");
+// }
+
+ if (name1.hashCode() != name2.hashCode())
+ {
+ fail("Failed same name test - in Order");
+ }
+
+ X500NameBuilder builder1 = new X500NameBuilder(BCStyle.INSTANCE);
+
+ builder.addRDN(BCStyle.C, "AU");
+ builder.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
+ builder.addRDN(BCStyle.L, "Melbourne");
+ builder.addRDN(BCStyle.ST, "Victoria");
+ builder.addRDN(BCStyle.E, "feedback-crypto@bouncycastle.org");
+
+ X500NameBuilder builder2 = new X500NameBuilder(BCStyle.INSTANCE);
+
+ builder.addRDN(BCStyle.E, "feedback-crypto@bouncycastle.org");
+ builder.addRDN(BCStyle.C, "AU");
+ builder.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
+ builder.addRDN(BCStyle.L, "Melbourne");
+ builder.addRDN(BCStyle.ST, "Victoria");
+
+ name1 = builder1.build();
+ name2 = builder2.build();
+
+ if (!name1.equals(name2))
+ {
+ fail("Failed reverse name test");
+ }
+
+ if (name1.hashCode() != name2.hashCode())
+ {
+ fail("Failed reverse name test hashCode");
+ }
+
+// if (name1.equals(name2, true))
+// {
+// fail("Failed reverse name test - in Order");
+// }
+//
+// if (!name1.equals(name2, false))
+// {
+// fail("Failed reverse name test - in Order false");
+// }
+
+// Vector oids = name1.getOIDs();
+// if (!compareVectors(oids, ord1))
+// {
+// fail("oid comparison test");
+// }
+ /*
+ Vector val1 = new Vector();
+
+ val1.addElement("AU");
+ val1.addElement("The Legion of the Bouncy Castle");
+ val1.addElement("Melbourne");
+ val1.addElement("Victoria");
+ val1.addElement("feedback-crypto@bouncycastle.org");
+
+ name1 = new X500Name(ord1, val1);
+
+ Vector values = name1.getValues();
+ if (!compareVectors(values, val1))
+ {
+ fail("value comparison test");
+ }
+
+ ord2 = new Vector();
+
+ ord2.addElement(X500Name.ST);
+ ord2.addElement(X500Name.ST);
+ ord2.addElement(X500Name.L);
+ ord2.addElement(X500Name.O);
+ ord2.addElement(X500Name.C);
+
+ name1 = new X500Name(ord1, attrs);
+ name2 = new X500Name(ord2, attrs);
+
+ if (name1.equals(name2))
+ {
+ fail("Failed different name test");
+ }
+
+ ord2 = new Vector();
+
+ ord2.addElement(X500Name.ST);
+ ord2.addElement(X500Name.L);
+ ord2.addElement(X500Name.O);
+ ord2.addElement(X500Name.C);
+
+ name1 = new X500Name(ord1, attrs);
+ name2 = new X500Name(ord2, attrs);
+
+ if (name1.equals(name2))
+ {
+ fail("Failed subset name test");
+ }
+
+ compositeTest();
+ */
+ ByteArrayOutputStream bOut;
+ ASN1OutputStream aOut;
+ ASN1InputStream aIn;
+ /*
+ //
+ // getValues test
+ //
+ Vector v1 = name1.getValues(X500Name.O);
+
+ if (v1.size() != 1 || !v1.elementAt(0).equals("The Legion of the Bouncy Castle"))
+ {
+ fail("O test failed");
+ }
+
+ Vector v2 = name1.getValues(X500Name.L);
+
+ if (v2.size() != 1 || !v2.elementAt(0).equals("Melbourne"))
+ {
+ fail("L test failed");
+ }
+ */
+ //
+ // general subjects test
+ //
+ for (int i = 0; i != subjects.length; i++)
+ {
+ X500Name name = new X500Name(subjects[i]);
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(name);
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+ name = X500Name.getInstance(aIn.readObject());
+ if (!name.toString().equals(subjects[i]))
+ {
+ fail("failed regeneration test " + i + " got: " + name.toString() + " expected " + subjects[i]);
+ }
+ }
+
+ for (int i = 0; i < hexSubjects.length; i += 2)
+ {
+ X500Name name = new X500Name(hexSubjects[i]);
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(name);
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+ name = X500Name.getInstance(aIn.readObject());
+ if (!name.toString().equals(hexSubjects[i + 1]))
+ {
+ fail("failed hex regeneration test " + i + " got: " + name.toString() + " expected " + subjects[i]);
+ }
+ }
+
+ //
+ // sort test
+ //
+ X500Name unsorted = new X500Name("SERIALNUMBER=BBB + CN=AA");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("CN=AA+SERIALNUMBER=BBB"))
+ {
+ fail("failed sort test 1");
+ }
+
+ unsorted = new X500Name("CN=AA + SERIALNUMBER=BBB");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("CN=AA+SERIALNUMBER=BBB"))
+ {
+ fail("failed sort test 2");
+ }
+
+ unsorted = new X500Name("SERIALNUMBER=B + CN=AA");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("SERIALNUMBER=B+CN=AA"))
+ {
+ fail("failed sort test 3");
+ }
+
+ unsorted = new X500Name("CN=AA + SERIALNUMBER=B");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("SERIALNUMBER=B+CN=AA"))
+ {
+ fail("failed sort test 4");
+ }
+
+ //
+ // equality tests
+ //
+ equalityTest(new X500Name("CN=The Legion"), new X500Name("CN=The Legion"));
+ equalityTest(new X500Name("CN= The Legion"), new X500Name("CN=The Legion"));
+ equalityTest(new X500Name("CN=The Legion "), new X500Name("CN=The Legion"));
+ equalityTest(new X500Name("CN= The Legion "), new X500Name("CN=The Legion"));
+ equalityTest(new X500Name("CN= the legion "), new X500Name("CN=The Legion"));
+
+ equalityTest(new X500Name("CN= the legion+C=AU, O=Legion "), new X500Name("CN=The Legion+C=AU, O=Legion"));
+ // # test
+
+ X500Name n1 = new X500Name("SERIALNUMBER=8,O=ABC,CN=ABC Class 3 CA,C=LT");
+ X500Name n2 = new X500Name("2.5.4.5=8,O=ABC,CN=ABC Class 3 CA,C=LT");
+ X500Name n3 = new X500Name("2.5.4.5=#130138,O=ABC,CN=ABC Class 3 CA,C=LT");
+
+ equalityTest(n1, n2);
+ equalityTest(n2, n3);
+ equalityTest(n3, n1);
+
+ n1 = new X500Name("2.5.4.5=#130138,CN=SSC Class 3 CA,O=UAB Skaitmeninio sertifikavimo centras,C=LT");
+ n2 = new X500Name("SERIALNUMBER=#130138,CN=SSC Class 3 CA,O=UAB Skaitmeninio sertifikavimo centras,C=LT");
+ n3 = X500Name.getInstance(ASN1Primitive.fromByteArray(Hex.decode("3063310b3009060355040613024c54312f302d060355040a1326"
+ + "55414220536b6169746d656e696e696f20736572746966696b6176696d6f2063656e74726173311730150603550403130e53534320436c6173732033204341310a30080603550405130138")));
+
+ equalityTest(n1, n2);
+ equalityTest(n2, n3);
+ equalityTest(n3, n1);
+
+ n1 = new X500Name("SERIALNUMBER=8,O=XX,CN=ABC Class 3 CA,C=LT");
+ n2 = new X500Name("2.5.4.5=8,O=,CN=ABC Class 3 CA,C=LT");
+
+// if (n1.equals(n2))
+// {
+// fail("empty inequality check failed");
+// }
+
+ n1 = new X500Name("SERIALNUMBER=8,O=,CN=ABC Class 3 CA,C=LT");
+ n2 = new X500Name("2.5.4.5=8,O=,CN=ABC Class 3 CA,C=LT");
+
+ equalityTest(n1, n2);
+
+ equalityTest(X500Name.getInstance(BCStrictStyle.INSTANCE, n1), X500Name.getInstance(BCStrictStyle.INSTANCE, n2));
+
+ n2 = new X500Name("C=LT,2.5.4.5=8,O=,CN=ABC Class 3 CA");
+
+ equalityTest(n1, n2);
+
+ if (X500Name.getInstance(BCStrictStyle.INSTANCE, n1).equals(X500Name.getInstance(BCStrictStyle.INSTANCE, n2)))
+ {
+ fail("strict comparison failed");
+ }
+
+ //
+ // inequality to sequences
+ //
+ name1 = new X500Name("CN=The Legion");
+
+ if (name1.equals(new DERSequence()))
+ {
+ fail("inequality test with sequence");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet())))
+ {
+ fail("inequality test with sequence and set");
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1ObjectIdentifier("1.1"));
+ v.add(new ASN1ObjectIdentifier("1.1"));
+ if (name1.equals(new DERSequence(new DERSet(new DERSet(v)))))
+ {
+ fail("inequality test with sequence and bad set");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSet(v)))))
+ {
+ fail("inequality test with sequence and bad set");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSequence()))))
+ {
+ fail("inequality test with sequence and short sequence");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSequence()))))
+ {
+ fail("inequality test with sequence and short sequence");
+ }
+
+ v = new ASN1EncodableVector();
+
+ v.add(new ASN1ObjectIdentifier("1.1"));
+ v.add(new DERSequence());
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSequence(v)))))
+ {
+ fail("inequality test with sequence and bad sequence");
+ }
+
+ if (name1.equals(null))
+ {
+ fail("inequality test with null");
+ }
+
+// if (name1.equals(null, true))
+// {
+// fail("inequality test with null");
+// }
+
+ //
+ // this is contrived but it checks sorting of sets with equal elements
+ //
+ unsorted = new X500Name("CN=AA + CN=AA + CN=AA");
+
+ ASN1ObjectIdentifier[] types = unsorted.getAttributeTypes();
+ if (types.length != 3 || !types[0].equals(BCStyle.CN) || !types[1].equals(BCStyle.CN) || !types[2].equals(BCStyle.CN))
+ {
+ fail("types not matched correctly");
+ }
+
+ // general type test
+ X500Name nested = new X500Name("CN=AA + CN=AA, C=AU");
+
+ types = nested.getAttributeTypes();
+ if (types.length != 3 || !types[0].equals(BCStyle.CN) || !types[1].equals(BCStyle.CN) || !types[2].equals(BCStyle.C))
+ {
+ fail("nested types not matched correctly");
+ }
+ //
+ // tagging test - only works if CHOICE implemented
+ //
+ ASN1TaggedObject tag = new DERTaggedObject(false, 1, new X500Name("CN=AA"));
+
+ if (!tag.isExplicit())
+ {
+ fail("failed to explicitly tag CHOICE object");
+ }
+
+ X500Name name = X500Name.getInstance(tag, false);
+
+ if (!name.equals(new X500Name("CN=AA")))
+ {
+ fail("failed to recover tagged name");
+ }
+
+ DERUTF8String testString = new DERUTF8String("The Legion of the Bouncy Castle");
+ byte[] encodedBytes = testString.getEncoded();
+ byte[] hexEncodedBytes = Hex.encode(encodedBytes);
+ String hexEncodedString = "#" + new String(hexEncodedBytes);
+
+ DERUTF8String converted = (DERUTF8String)
+ new X509DefaultEntryConverter().getConvertedValue(
+ BCStyle.L , hexEncodedString);
+
+ if (!converted.equals(testString))
+ {
+ fail("failed X509DefaultEntryConverter test");
+ }
+
+ //
+ // try escaped.
+ //
+ converted = (DERUTF8String)
+ new X509DefaultEntryConverter().getConvertedValue(
+ BCStyle.L , "\\" + hexEncodedString);
+
+ if (!converted.equals(new DERUTF8String(hexEncodedString)))
+ {
+ fail("failed X509DefaultEntryConverter test got " + converted + " expected: " + hexEncodedString);
+ }
+
+ //
+ // try a weird value
+ //
+ X500Name n = new X500Name("CN=\\#nothex#string");
+
+ if (!n.toString().equals("CN=\\#nothex#string"))
+ {
+ fail("# string not properly escaped.");
+ }
+
+ RDN[] vls = n.getRDNs(BCStyle.CN);
+ if (vls.length != 1 || !getValue(vls[0]).equals("#nothex#string"))
+ {
+ fail("escaped # not reduced properly");
+ }
+
+ types = n.getAttributeTypes();
+ if (types.length != 1 || !types[0].equals(BCStyle.CN))
+ {
+ fail("type not matched correctly");
+ }
+
+ n = new X500Name("CN=\"a+b\"");
+
+ vls = n.getRDNs(BCStyle.CN);
+ if (vls.length != 1 || !getValue(vls[0]).equals("a+b"))
+ {
+ fail("escaped + not reduced properly");
+ }
+
+ n = new X500Name("CN=a\\+b");
+
+ vls = n.getRDNs(BCStyle.CN);
+ if (vls.length != 1 || !getValue(vls[0]).equals("a+b"))
+ {
+ fail("escaped + not reduced properly");
+ }
+
+ if (!n.toString().equals("CN=a\\+b"))
+ {
+ fail("+ in string not properly escaped.");
+ }
+
+ n = new X500Name("CN=a\\=b");
+
+ vls = n.getRDNs(BCStyle.CN);
+ if (vls.length != 1 || !getValue(vls[0]).equals("a=b"))
+ {
+ fail("escaped = not reduced properly");
+ }
+
+ if (!n.toString().equals("CN=a\\=b"))
+ {
+ fail("= in string not properly escaped.");
+ }
+
+ n = new X500Name("TELEPHONENUMBER=\"+61999999999\"");
+
+ vls = n.getRDNs(BCStyle.TELEPHONE_NUMBER);
+ if (vls.length != 1 || !getValue(vls[0]).equals("+61999999999"))
+ {
+ fail("telephonenumber escaped + not reduced properly");
+ }
+
+ n = new X500Name("TELEPHONENUMBER=\\+61999999999");
+
+ vls = n.getRDNs(BCStyle.TELEPHONE_NUMBER);
+ if (vls.length != 1 || !getValue(vls[0]).equals("+61999999999"))
+ {
+ fail("telephonenumber escaped + not reduced properly");
+ }
+
+ // test query methods
+ if (!"E".equals(BCStyle.INSTANCE.oidToDisplayName(BCStyle.EmailAddress)))
+ {
+ fail("display name for E incorrect");
+ }
+
+ String[] aliases = BCStyle.INSTANCE.oidToAttrNames(BCStyle.EmailAddress);
+ if (aliases.length != 2)
+ {
+ fail("no aliases found");
+ }
+ if (!("e".equals(aliases[0]) || "e".equals(aliases[1])))
+ {
+ fail("first alias name for E incorrect");
+ }
+ if (!("emailaddress".equals(aliases[0]) || "emailaddress".equals(aliases[1])))
+ {
+ fail("second alias name for E incorrect");
+ }
+
+ if (BCStyle.INSTANCE.oidToDisplayName(new ASN1ObjectIdentifier("1.2.1")) != null)
+ {
+ fail("unknown oid matched!");
+ }
+
+ if (BCStyle.INSTANCE.oidToAttrNames(new ASN1ObjectIdentifier("1.2.1")).length != 0)
+ {
+ fail("unknown oid matched aliases!");
+ }
+ }
+
+ private String getValue(RDN vl)
+ {
+ return ((ASN1String)vl.getFirst().getValue()).getString();
+ }
+
+ private void ietfUtilsTest()
+ throws Exception
+ {
+ IETFUtils.valueToString(new DERUTF8String(" "));
+ }
+
+ /*
+ private boolean compareVectors(Vector a, Vector b) // for compatibility with early JDKs
+ {
+ if (a.size() != b.size())
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.size(); i++)
+ {
+ if (!a.elementAt(i).equals(b.elementAt(i)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void compositeTest()
+ throws IOException
+ {
+ //
+ // composite test
+ //
+ byte[] enc = Hex.decode("305e310b300906035504061302415531283026060355040a0c1f546865204c6567696f6e206f662074686520426f756e637920436173746c653125301006035504070c094d656c626f75726e653011060355040b0c0a4173636f742056616c65");
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(enc));
+
+ X500Name n = X500Name.getInstance(aIn.readObject());
+
+ if (!n.toString().equals("C=AU,O=The Legion of the Bouncy Castle,L=Melbourne+OU=Ascot Vale"))
+ {
+ fail("Failed composite to string test got: " + n.toString());
+ }
+
+ if (!n.toString(true, X500Name.DefaultSymbols).equals("L=Melbourne+OU=Ascot Vale,O=The Legion of the Bouncy Castle,C=AU"))
+ {
+ fail("Failed composite to string test got: " + n.toString(true, X500Name.DefaultSymbols));
+ }
+
+ n = new X500Name(true, "L=Melbourne+OU=Ascot Vale,O=The Legion of the Bouncy Castle,C=AU");
+ if (!n.toString().equals("C=AU,O=The Legion of the Bouncy Castle,L=Melbourne+OU=Ascot Vale"))
+ {
+ fail("Failed composite to string reversal test got: " + n.toString());
+ }
+
+ n = new X500Name("C=AU, O=The Legion of the Bouncy Castle, L=Melbourne + OU=Ascot Vale");
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(n);
+
+ byte[] enc2 = bOut.toByteArray();
+
+ if (!Arrays.areEqual(enc, enc2))
+ {
+ fail("Failed composite string to encoding test");
+ }
+
+ //
+ // dud name test - handle empty DN without barfing.
+ //
+ n = new X500Name("C=CH,O=,OU=dummy,CN=mail@dummy.com");
+
+ n = X500Name.getInstance(ASN1Object.fromByteArray(n.getEncoded()));
+ }
+ */
+ private void equalityTest(X500Name name1, X500Name name2)
+ {
+ if (!name1.equals(name2))
+ {
+ fail("equality test failed for " + name1 + " : " + name2);
+ }
+
+ if (name1.hashCode() != name2.hashCode())
+ {
+ fail("hashCodeTest test failed for " + name1 + " : " + name2);
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new X500NameTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X509ExtensionsTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X509ExtensionsTest.java
new file mode 100644
index 000000000..af99668fc
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X509ExtensionsTest.java
@@ -0,0 +1,105 @@
+package org.spongycastle.asn1.test;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509ExtensionsGenerator;
+import org.spongycastle.util.test.SimpleTest;
+
+public class X509ExtensionsTest
+ extends SimpleTest
+{
+ private static final ASN1ObjectIdentifier OID_2 = new ASN1ObjectIdentifier("1.2.2");
+ private static final ASN1ObjectIdentifier OID_3 = new ASN1ObjectIdentifier("1.2.3");
+ private static final ASN1ObjectIdentifier OID_1 = new ASN1ObjectIdentifier("1.2.1");
+
+ public String getName()
+ {
+ return "X509Extensions";
+ }
+
+ public void performTest() throws Exception
+ {
+ X509ExtensionsGenerator gen = new X509ExtensionsGenerator();
+
+ gen.addExtension(OID_1, true, new byte[20]);
+ gen.addExtension(OID_2, true, new byte[20]);
+
+ X509Extensions ext1 = gen.generate();
+ X509Extensions ext2 = gen.generate();
+
+ if (!ext1.equals(ext2))
+ {
+ fail("equals test failed");
+ }
+
+ gen.reset();
+
+ gen.addExtension(OID_2, true, new byte[20]);
+ gen.addExtension(OID_1, true, new byte[20]);
+
+ ext2 = gen.generate();
+
+ if (ext1.equals(ext2))
+ {
+ fail("inequality test failed");
+ }
+
+ if (!ext1.equivalent(ext2))
+ {
+ fail("equivalence true failed");
+ }
+
+ gen.reset();
+
+ gen.addExtension(OID_1, true, new byte[22]);
+ gen.addExtension(OID_2, true, new byte[20]);
+
+ ext2 = gen.generate();
+
+ if (ext1.equals(ext2))
+ {
+ fail("inequality 1 failed");
+ }
+
+ if (ext1.equivalent(ext2))
+ {
+ fail("non-equivalence 1 failed");
+ }
+
+ gen.reset();
+
+ gen.addExtension(OID_3, true, new byte[20]);
+ gen.addExtension(OID_2, true, new byte[20]);
+
+ ext2 = gen.generate();
+
+ if (ext1.equals(ext2))
+ {
+ fail("inequality 2 failed");
+ }
+
+ if (ext1.equivalent(ext2))
+ {
+ fail("non-equivalence 2 failed");
+ }
+
+ try
+ {
+ gen.addExtension(OID_2, true, new byte[20]);
+ fail("repeated oid");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("extension 1.2.2 already added"))
+ {
+ fail("wrong exception on repeated oid: " + e.getMessage());
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new X509ExtensionsTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X509NameTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X509NameTest.java
new file mode 100644
index 000000000..0f9b64366
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X509NameTest.java
@@ -0,0 +1,694 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERObjectIdentifier;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameBuilder;
+import org.spongycastle.asn1.x500.style.BCStyle;
+import org.spongycastle.asn1.x509.X509DefaultEntryConverter;
+import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class X509NameTest
+ extends SimpleTest
+{
+ String[] subjects =
+ {
+ "C=AU,ST=Victoria,L=South Melbourne,O=Connect 4 Pty Ltd,OU=Webserver Team,CN=www2.connect4.com.au,E=webmaster@connect4.com.au",
+ "C=AU,ST=Victoria,L=South Melbourne,O=Connect 4 Pty Ltd,OU=Certificate Authority,CN=Connect 4 CA,E=webmaster@connect4.com.au",
+ "C=AU,ST=QLD,CN=SSLeay/rsa test cert",
+ "C=US,O=National Aeronautics and Space Administration,SERIALNUMBER=16+CN=Steve Schoch",
+ "E=cooke@issl.atl.hp.com,C=US,OU=Hewlett Packard Company (ISSL),CN=Paul A. Cooke",
+ "O=Sun Microsystems Inc,CN=store.sun.com",
+ "unstructuredAddress=192.168.1.33,unstructuredName=pixfirewall.ciscopix.com,CN=pixfirewall.ciscopix.com",
+ "CN=*.canal-plus.com,OU=Provided by TBS INTERNET http://www.tbs-certificats.com/,OU=\\ CANAL \\+,O=CANAL\\+DISTRIBUTION,L=issy les moulineaux,ST=Hauts de Seine,C=FR",
+ "O=Bouncy Castle,CN=www.bouncycastle.org\\ ",
+ "O=Bouncy Castle,CN=c:\\\\fred\\\\bob"
+ };
+
+ public String getName()
+ {
+ return "X509Name";
+ }
+
+ private static X509Name fromBytes(
+ byte[] bytes)
+ throws IOException
+ {
+ return X509Name.getInstance(new ASN1InputStream(new ByteArrayInputStream(bytes)).readObject());
+ }
+
+ private ASN1Encodable createEntryValue(DERObjectIdentifier oid, String value)
+ {
+ Hashtable attrs = new Hashtable();
+
+ attrs.put(oid, value);
+
+ Vector order = new Vector();
+
+ order.addElement(oid);
+
+ X509Name name = new X509Name(order, attrs);
+
+ ASN1Sequence seq = (ASN1Sequence)name.toASN1Primitive();
+ ASN1Set set = (ASN1Set)seq.getObjectAt(0);
+ seq = (ASN1Sequence)set.getObjectAt(0);
+
+ return seq.getObjectAt(1);
+ }
+
+ private ASN1Encodable createEntryValueFromString(DERObjectIdentifier oid, String value)
+ {
+ Hashtable attrs = new Hashtable();
+
+ attrs.put(oid, value);
+
+ Vector order = new Vector();
+
+ order.addElement(oid);
+
+ X509Name name = new X509Name(new X509Name(order, attrs).toString());
+
+ ASN1Sequence seq = (ASN1Sequence)name.toASN1Primitive();
+ ASN1Set set = (ASN1Set)seq.getObjectAt(0);
+ seq = (ASN1Sequence)set.getObjectAt(0);
+
+ return seq.getObjectAt(1);
+ }
+
+ private void testEncodingPrintableString(DERObjectIdentifier oid, String value)
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERPrintableString))
+ {
+ fail("encoding for " + oid + " not printable string");
+ }
+ }
+
+ private void testEncodingIA5String(DERObjectIdentifier oid, String value)
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERIA5String))
+ {
+ fail("encoding for " + oid + " not IA5String");
+ }
+ }
+
+
+ private void testEncodingUTF8String(DERObjectIdentifier oid, String value)
+ throws IOException
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERUTF8String))
+ {
+ fail("encoding for " + oid + " not IA5String");
+ }
+ if (!value.equals((DERUTF8String.getInstance(converted.toASN1Primitive().getEncoded()).getString())))
+ {
+ fail("decoding not correct");
+ }
+ }
+
+ private void testEncodingGeneralizedTime(DERObjectIdentifier oid, String value)
+ {
+ ASN1Encodable converted = createEntryValue(oid, value);
+ if (!(converted instanceof DERGeneralizedTime))
+ {
+ fail("encoding for " + oid + " not GeneralizedTime");
+ }
+ converted = createEntryValueFromString(oid, value);
+ if (!(converted instanceof DERGeneralizedTime))
+ {
+ fail("encoding for " + oid + " not GeneralizedTime");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testEncodingPrintableString(X509Name.C, "AU");
+ testEncodingPrintableString(X509Name.SERIALNUMBER, "123456");
+ testEncodingPrintableString(X509Name.DN_QUALIFIER, "123456");
+ testEncodingIA5String(X509Name.EmailAddress, "test@test.com");
+ testEncodingIA5String(X509Name.DC, "test");
+ // correct encoding
+ testEncodingGeneralizedTime(X509Name.DATE_OF_BIRTH, "#180F32303032303132323132323232305A");
+ // compatibility encoding
+ testEncodingGeneralizedTime(X509Name.DATE_OF_BIRTH, "20020122122220Z");
+ testEncodingUTF8String(X509Name.CN, "Mörsky");
+ //
+ // composite
+ //
+ Hashtable attrs = new Hashtable();
+
+ attrs.put(X509Name.C, "AU");
+ attrs.put(X509Name.O, "The Legion of the Bouncy Castle");
+ attrs.put(X509Name.L, "Melbourne");
+ attrs.put(X509Name.ST, "Victoria");
+ attrs.put(X509Name.E, "feedback-crypto@bouncycastle.org");
+
+ Vector order = new Vector();
+
+ order.addElement(X509Name.C);
+ order.addElement(X509Name.O);
+ order.addElement(X509Name.L);
+ order.addElement(X509Name.ST);
+ order.addElement(X509Name.E);
+
+ X509Name name1 = new X509Name(order, attrs);
+
+ if (!name1.equals(name1))
+ {
+ fail("Failed same object test");
+ }
+
+ if (!name1.equals(name1, true))
+ {
+ fail("Failed same object test - in Order");
+ }
+
+ X509Name name2 = new X509Name(order, attrs);
+
+ if (!name1.equals(name2))
+ {
+ fail("Failed same name test");
+ }
+
+ if (!name1.equals(name2, true))
+ {
+ fail("Failed same name test - in Order");
+ }
+
+ if (name1.hashCode() != name2.hashCode())
+ {
+ fail("Failed same name test - in Order");
+ }
+
+ Vector ord1 = new Vector();
+
+ ord1.addElement(X509Name.C);
+ ord1.addElement(X509Name.O);
+ ord1.addElement(X509Name.L);
+ ord1.addElement(X509Name.ST);
+ ord1.addElement(X509Name.E);
+
+ Vector ord2 = new Vector();
+
+ ord2.addElement(X509Name.E);
+ ord2.addElement(X509Name.ST);
+ ord2.addElement(X509Name.L);
+ ord2.addElement(X509Name.O);
+ ord2.addElement(X509Name.C);
+
+ name1 = new X509Name(ord1, attrs);
+ name2 = new X509Name(ord2, attrs);
+
+ if (!name1.equals(name2))
+ {
+ fail("Failed reverse name test");
+ }
+
+ if (name1.hashCode() != name2.hashCode())
+ {
+ fail("Failed reverse name test hashCode");
+ }
+
+ if (name1.equals(name2, true))
+ {
+ fail("Failed reverse name test - in Order");
+ }
+
+ if (!name1.equals(name2, false))
+ {
+ fail("Failed reverse name test - in Order false");
+ }
+
+ Vector oids = name1.getOIDs();
+ if (!compareVectors(oids, ord1))
+ {
+ fail("oid comparison test");
+ }
+
+ Vector val1 = new Vector();
+
+ val1.addElement("AU");
+ val1.addElement("The Legion of the Bouncy Castle");
+ val1.addElement("Melbourne");
+ val1.addElement("Victoria");
+ val1.addElement("feedback-crypto@bouncycastle.org");
+
+ name1 = new X509Name(ord1, val1);
+
+ Vector values = name1.getValues();
+ if (!compareVectors(values, val1))
+ {
+ fail("value comparison test");
+ }
+
+ ord2 = new Vector();
+
+ ord2.addElement(X509Name.ST);
+ ord2.addElement(X509Name.ST);
+ ord2.addElement(X509Name.L);
+ ord2.addElement(X509Name.O);
+ ord2.addElement(X509Name.C);
+
+ name1 = new X509Name(ord1, attrs);
+ name2 = new X509Name(ord2, attrs);
+
+ if (name1.equals(name2))
+ {
+ fail("Failed different name test");
+ }
+
+ ord2 = new Vector();
+
+ ord2.addElement(X509Name.ST);
+ ord2.addElement(X509Name.L);
+ ord2.addElement(X509Name.O);
+ ord2.addElement(X509Name.C);
+
+ name1 = new X509Name(ord1, attrs);
+ name2 = new X509Name(ord2, attrs);
+
+ if (name1.equals(name2))
+ {
+ fail("Failed subset name test");
+ }
+
+ compositeTest();
+
+ ByteArrayOutputStream bOut;
+ ASN1OutputStream aOut;
+ ASN1InputStream aIn;
+
+ //
+ // getValues test
+ //
+ Vector v1 = name1.getValues(X509Name.O);
+
+ if (v1.size() != 1 || !v1.elementAt(0).equals("The Legion of the Bouncy Castle"))
+ {
+ fail("O test failed");
+ }
+
+ Vector v2 = name1.getValues(X509Name.L);
+
+ if (v2.size() != 1 || !v2.elementAt(0).equals("Melbourne"))
+ {
+ fail("L test failed");
+ }
+
+ //
+ // general subjects test
+ //
+ for (int i = 0; i != subjects.length; i++)
+ {
+ X509Name name = new X509Name(subjects[i]);
+
+ bOut = new ByteArrayOutputStream();
+ aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(name);
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+ name = X509Name.getInstance(aIn.readObject());
+
+ if (!name.toString().equals(subjects[i]))
+ {
+ fail("failed regeneration test " + i + " got " + name.toString());
+ }
+ }
+
+ //
+ // sort test
+ //
+ X509Name unsorted = new X509Name("SERIALNUMBER=BBB + CN=AA");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("CN=AA+SERIALNUMBER=BBB"))
+ {
+ fail("failed sort test 1");
+ }
+
+ unsorted = new X509Name("CN=AA + SERIALNUMBER=BBB");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("CN=AA+SERIALNUMBER=BBB"))
+ {
+ fail("failed sort test 2");
+ }
+
+ unsorted = new X509Name("SERIALNUMBER=B + CN=AA");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("SERIALNUMBER=B+CN=AA"))
+ {
+ fail("failed sort test 3");
+ }
+
+ unsorted = new X509Name("CN=AA + SERIALNUMBER=B");
+
+ if (!fromBytes(unsorted.getEncoded()).toString().equals("SERIALNUMBER=B+CN=AA"))
+ {
+ fail("failed sort test 4");
+ }
+
+ //
+ // equality tests
+ //
+ equalityTest(new X509Name("CN=The Legion"), new X509Name("CN=The Legion"));
+ equalityTest(new X509Name("CN= The Legion"), new X509Name("CN=The Legion"));
+ equalityTest(new X509Name("CN=The Legion "), new X509Name("CN=The Legion"));
+ equalityTest(new X509Name("CN= The Legion "), new X509Name("CN=The Legion"));
+ equalityTest(new X509Name("CN= the legion "), new X509Name("CN=The Legion"));
+
+ // # test
+
+ X509Name n1 = new X509Name("SERIALNUMBER=8,O=ABC,CN=ABC Class 3 CA,C=LT");
+ X509Name n2 = new X509Name("2.5.4.5=8,O=ABC,CN=ABC Class 3 CA,C=LT");
+ X509Name n3 = new X509Name("2.5.4.5=#130138,O=ABC,CN=ABC Class 3 CA,C=LT");
+
+ equalityTest(n1, n2);
+ equalityTest(n2, n3);
+ equalityTest(n3, n1);
+
+ n1 = new X509Name(true, "2.5.4.5=#130138,CN=SSC Class 3 CA,O=UAB Skaitmeninio sertifikavimo centras,C=LT");
+ n2 = new X509Name(true, "SERIALNUMBER=#130138,CN=SSC Class 3 CA,O=UAB Skaitmeninio sertifikavimo centras,C=LT");
+ n3 = X509Name.getInstance(ASN1Primitive.fromByteArray(Hex.decode("3063310b3009060355040613024c54312f302d060355040a1326"
+ + "55414220536b6169746d656e696e696f20736572746966696b6176696d6f2063656e74726173311730150603550403130e53534320436c6173732033204341310a30080603550405130138")));
+
+ equalityTest(n1, n2);
+ equalityTest(n2, n3);
+ equalityTest(n3, n1);
+
+ n1 = new X509Name("SERIALNUMBER=8,O=XX,CN=ABC Class 3 CA,C=LT");
+ n2 = new X509Name("2.5.4.5=8,O=,CN=ABC Class 3 CA,C=LT");
+
+ if (n1.equals(n2))
+ {
+ fail("empty inequality check failed");
+ }
+
+ n1 = new X509Name("SERIALNUMBER=8,O=,CN=ABC Class 3 CA,C=LT");
+ n2 = new X509Name("2.5.4.5=8,O=,CN=ABC Class 3 CA,C=LT");
+
+ equalityTest(n1, n2);
+
+ //
+ // inequality to sequences
+ //
+ name1 = new X509Name("CN=The Legion");
+
+ if (name1.equals(new DERSequence()))
+ {
+ fail("inequality test with sequence");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet())))
+ {
+ fail("inequality test with sequence and set");
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new DERObjectIdentifier("1.1"));
+ v.add(new DERObjectIdentifier("1.1"));
+ if (name1.equals(new DERSequence(new DERSet(new DERSet(v)))))
+ {
+ fail("inequality test with sequence and bad set");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSet(v))), true))
+ {
+ fail("inequality test with sequence and bad set");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSequence()))))
+ {
+ fail("inequality test with sequence and short sequence");
+ }
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSequence())), true))
+ {
+ fail("inequality test with sequence and short sequence");
+ }
+
+ v = new ASN1EncodableVector();
+
+ v.add(new DERObjectIdentifier("1.1"));
+ v.add(new DERSequence());
+
+ if (name1.equals(new DERSequence(new DERSet(new DERSequence(v)))))
+ {
+ fail("inequality test with sequence and bad sequence");
+ }
+
+ if (name1.equals(null))
+ {
+ fail("inequality test with null");
+ }
+
+ if (name1.equals(null, true))
+ {
+ fail("inequality test with null");
+ }
+
+ //
+ // this is contrived but it checks sorting of sets with equal elements
+ //
+ unsorted = new X509Name("CN=AA + CN=AA + CN=AA");
+
+ //
+ // tagging test - only works if CHOICE implemented
+ //
+ /*
+ ASN1TaggedObject tag = new DERTaggedObject(false, 1, new X509Name("CN=AA"));
+
+ if (!tag.isExplicit())
+ {
+ fail("failed to explicitly tag CHOICE object");
+ }
+
+ X509Name name = X509Name.getInstance(tag, false);
+
+ if (!name.equals(new X509Name("CN=AA")))
+ {
+ fail("failed to recover tagged name");
+ }
+ */
+
+ DERUTF8String testString = new DERUTF8String("The Legion of the Bouncy Castle");
+ byte[] encodedBytes = testString.getEncoded();
+ byte[] hexEncodedBytes = Hex.encode(encodedBytes);
+ String hexEncodedString = "#" + new String(hexEncodedBytes);
+
+ DERUTF8String converted = (DERUTF8String)
+ new X509DefaultEntryConverter().getConvertedValue(
+ X509Name.L , hexEncodedString);
+
+ if (!converted.equals(testString))
+ {
+ fail("failed X509DefaultEntryConverter test");
+ }
+
+ //
+ // try escaped.
+ //
+ converted = (DERUTF8String)
+ new X509DefaultEntryConverter().getConvertedValue(
+ X509Name.L , "\\" + hexEncodedString);
+
+ if (!converted.equals(new DERUTF8String(hexEncodedString)))
+ {
+ fail("failed X509DefaultEntryConverter test got " + converted + " expected: " + hexEncodedString);
+ }
+
+ //
+ // try a weird value
+ //
+ X509Name n = new X509Name("CN=\\#nothex#string");
+
+ if (!n.toString().equals("CN=\\#nothex#string"))
+ {
+ fail("# string not properly escaped.");
+ }
+
+ Vector vls = n.getValues(X509Name.CN);
+ if (vls.size() != 1 || !vls.elementAt(0).equals("#nothex#string"))
+ {
+ fail("escaped # not reduced properly");
+ }
+
+ n = new X509Name("CN=\"a+b\"");
+
+ vls = n.getValues(X509Name.CN);
+ if (vls.size() != 1 || !vls.elementAt(0).equals("a+b"))
+ {
+ fail("escaped + not reduced properly");
+ }
+
+ n = new X509Name("CN=a\\+b");
+
+ vls = n.getValues(X509Name.CN);
+ if (vls.size() != 1 || !vls.elementAt(0).equals("a+b"))
+ {
+ fail("escaped + not reduced properly");
+ }
+
+ if (!n.toString().equals("CN=a\\+b"))
+ {
+ fail("+ in string not properly escaped.");
+ }
+
+ n = new X509Name("CN=a\\=b");
+
+ vls = n.getValues(X509Name.CN);
+ if (vls.size() != 1 || !vls.elementAt(0).equals("a=b"))
+ {
+ fail("escaped = not reduced properly");
+ }
+
+ if (!n.toString().equals("CN=a\\=b"))
+ {
+ fail("= in string not properly escaped.");
+ }
+
+ n = new X509Name("TELEPHONENUMBER=\"+61999999999\"");
+
+ vls = n.getValues(X509Name.TELEPHONE_NUMBER);
+ if (vls.size() != 1 || !vls.elementAt(0).equals("+61999999999"))
+ {
+ fail("telephonenumber escaped + not reduced properly");
+ }
+
+ n = new X509Name("TELEPHONENUMBER=\\+61999999999");
+
+ vls = n.getValues(X509Name.TELEPHONE_NUMBER);
+ if (vls.size() != 1 || !vls.elementAt(0).equals("+61999999999"))
+ {
+ fail("telephonenumber escaped + not reduced properly");
+ }
+
+ // migration
+ X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
+ builder.addMultiValuedRDN(new ASN1ObjectIdentifier[] { BCStyle.CN, BCStyle.SN }, new String[] { "Thomas", "CVR:12341233-UID:1111" });
+ builder.addRDN(BCStyle.O, "Test");
+ builder.addRDN(BCStyle.C, "DK");
+
+ X500Name subject = builder.build();
+ ASN1Primitive derObject = subject.toASN1Primitive();
+ X509Name instance = X509Name.getInstance(derObject);
+ }
+
+ private boolean compareVectors(Vector a, Vector b) // for compatibility with early JDKs
+ {
+ if (a.size() != b.size())
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.size(); i++)
+ {
+ if (!a.elementAt(i).equals(b.elementAt(i)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void compositeTest()
+ throws IOException
+ {
+ //
+ // composite test
+ //
+ byte[] enc = Hex.decode("305e310b300906035504061302415531283026060355040a0c1f546865204c6567696f6e206f662074686520426f756e637920436173746c653125301006035504070c094d656c626f75726e653011060355040b0c0a4173636f742056616c65");
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(enc));
+
+ X509Name n = X509Name.getInstance(aIn.readObject());
+
+ if (!n.toString().equals("C=AU,O=The Legion of the Bouncy Castle,L=Melbourne+OU=Ascot Vale"))
+ {
+ fail("Failed composite to string test got: " + n.toString());
+ }
+
+ if (!n.toString(true, X509Name.DefaultSymbols).equals("L=Melbourne+OU=Ascot Vale,O=The Legion of the Bouncy Castle,C=AU"))
+ {
+ fail("Failed composite to string test got: " + n.toString(true, X509Name.DefaultSymbols));
+ }
+
+ n = new X509Name(true, "L=Melbourne+OU=Ascot Vale,O=The Legion of the Bouncy Castle,C=AU");
+ if (!n.toString().equals("C=AU,O=The Legion of the Bouncy Castle,L=Melbourne+OU=Ascot Vale"))
+ {
+ fail("Failed composite to string reversal test got: " + n.toString());
+ }
+
+ n = new X509Name("C=AU, O=The Legion of the Bouncy Castle, L=Melbourne + OU=Ascot Vale");
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(n);
+
+ byte[] enc2 = bOut.toByteArray();
+
+ if (!Arrays.areEqual(enc, enc2))
+ {
+ //fail("Failed composite string to encoding test");
+ }
+
+ //
+ // dud name test - handle empty DN without barfing.
+ //
+ n = new X509Name("C=CH,O=,OU=dummy,CN=mail@dummy.com");
+
+ n = X509Name.getInstance(ASN1Primitive.fromByteArray(n.getEncoded()));
+ }
+
+ private void equalityTest(X509Name x509Name, X509Name x509Name1)
+ {
+ if (!x509Name.equals(x509Name1))
+ {
+ fail("equality test failed for " + x509Name + " : " + x509Name1);
+ }
+
+ if (x509Name.hashCode() != x509Name1.hashCode())
+ {
+ fail("hashCodeTest test failed for " + x509Name + " : " + x509Name1);
+ }
+
+ if (!x509Name.equals(x509Name1, true))
+ {
+ fail("equality test failed for " + x509Name + " : " + x509Name1);
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new X509NameTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X9Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X9Test.java
new file mode 100644
index 000000000..61250f7f1
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/asn1/test/X9Test.java
@@ -0,0 +1,182 @@
+package org.spongycastle.asn1.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.sec.ECPrivateKey;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.X962NamedCurves;
+import org.spongycastle.asn1.x9.X962Parameters;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.asn1.x9.X9ECPoint;
+import org.spongycastle.asn1.x9.X9IntegerConverter;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTest;
+
+public class X9Test
+ extends SimpleTest
+{
+ private byte[] namedPub = Base64.decode("MDcwEwYHKoZIzj0CAQYIKoZIzj0DAQEDIAADG5xRI+Iki/JrvL20hoDUa7Cggzorv5B9yyqSMjYu");
+ private byte[] expPub = Base64.decode(
+ "MIH8MIHXBgcqhkjOPQIBMIHLAgEBMCkGByqGSM49AQECHn///////////////3///////4AAAA" +
+ "AAAH///////zBXBB5///////////////9///////+AAAAAAAB///////wEHiVXBfoqMGZUsfTL" +
+ "A9anUKMMJQEC1JiHF9m6FattPgMVAH1zdBaP/jRxtgqFdoahlHXTv6L/BB8DZ2iujhi7ks/PAF" +
+ "yUmqLG2UhT0OZgu/hUsclQX+laAh5///////////////9///+XXetBs6YFfDxDIUZSZVECAQED" +
+ "IAADG5xRI+Iki/JrvL20hoDUa7Cggzorv5B9yyqSMjYu");
+
+ private byte[] namedPriv = Base64.decode("MCICAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEECDAGAgEBBAEK");
+ private byte[] expPriv = Base64.decode(
+ "MIHnAgEAMIHXBgcqhkjOPQIBMIHLAgEBMCkGByqGSM49AQECHn///////////////3///////4"
+ + "AAAAAAAH///////zBXBB5///////////////9///////+AAAAAAAB///////wEHiVXBfoqMGZU"
+ + "sfTLA9anUKMMJQEC1JiHF9m6FattPgMVAH1zdBaP/jRxtgqFdoahlHXTv6L/BB8DZ2iujhi7ks"
+ + "/PAFyUmqLG2UhT0OZgu/hUsclQX+laAh5///////////////9///+XXetBs6YFfDxDIUZSZVEC"
+ + "AQEECDAGAgEBBAEU");
+
+ private void encodePublicKey()
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ X9ECParameters ecP = X962NamedCurves.getByOID(X9ObjectIdentifiers.prime239v3);
+
+ X9IntegerConverter conv = new X9IntegerConverter();
+
+ if (conv.getByteLength(ecP.getCurve()) != 30)
+ {
+ fail("wrong byte length reported for curve");
+ }
+
+ if (ecP.getCurve().getFieldSize() != 239)
+ {
+ fail("wrong field size reported for curve");
+ }
+
+ //
+ // named curve
+ //
+ X962Parameters params = new X962Parameters(X9ObjectIdentifiers.prime192v1);
+ ECPoint point = ecP.getG().multiply(BigInteger.valueOf(100));
+
+ ASN1OctetString p = new DEROctetString(point.getEncoded(true));
+
+ SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
+ byte[] infoEncoded = info.getEncoded();
+
+ if (!areEqual(infoEncoded, namedPub))
+ {
+ fail("failed public named generation");
+ }
+
+ X9ECPoint x9P = new X9ECPoint(ecP.getCurve(), p);
+
+ if (!Arrays.areEqual(point.getEncoded(true), x9P.getPoint().getEncoded()))
+ {
+ fail("point encoding not preserved");
+ }
+
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(namedPub));
+ ASN1Primitive o = aIn.readObject();
+
+ if (!info.equals(o))
+ {
+ fail("failed public named equality");
+ }
+
+ //
+ // explicit curve parameters
+ //
+ params = new X962Parameters(ecP);
+
+ info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
+
+ if (!areEqual(info.getEncoded(), expPub))
+ {
+ fail("failed public explicit generation");
+ }
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(expPub));
+ o = aIn.readObject();
+
+ if (!info.equals(o))
+ {
+ fail("failed public explicit equality");
+ }
+ }
+
+ private void encodePrivateKey()
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ X9ECParameters ecP = X962NamedCurves.getByOID(X9ObjectIdentifiers.prime239v3);
+
+ //
+ // named curve
+ //
+ X962Parameters params = new X962Parameters(X9ObjectIdentifiers.prime192v1);
+
+ PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(BigInteger.valueOf(10)));
+
+ if (!areEqual(info.getEncoded(), namedPriv))
+ {
+ fail("failed private named generation");
+ }
+
+ ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(namedPriv));
+ ASN1Primitive o = aIn.readObject();
+
+ if (!info.equals(o))
+ {
+ fail("failed private named equality");
+ }
+
+ //
+ // explicit curve parameters
+ //
+ params = new X962Parameters(ecP);
+
+ info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(BigInteger.valueOf(20)));
+
+ if (!areEqual(info.getEncoded(), expPriv))
+ {
+ fail("failed private explicit generation");
+ }
+
+ aIn = new ASN1InputStream(new ByteArrayInputStream(expPriv));
+ o = aIn.readObject();
+
+ if (!info.equals(o))
+ {
+ fail("failed private explicit equality");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ encodePublicKey();
+ encodePrivateKey();
+ }
+
+ public String getName()
+ {
+ return "X9";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new X9Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/AllTests.java
new file mode 100644
index 000000000..25c428749
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/AllTests.java
@@ -0,0 +1,25 @@
+package org.spongycastle.crypto.agreement.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class AllTests
+ extends TestCase
+{
+ public static void main(String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("JPKAE Engine Tests");
+
+ suite.addTestSuite(JPAKEParticipantTest.class);
+ suite.addTestSuite(JPAKEPrimeOrderGroupTest.class);
+ suite.addTestSuite(JPAKEUtilTest.class);
+
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEParticipantTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEParticipantTest.java
new file mode 100644
index 000000000..223057fc1
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEParticipantTest.java
@@ -0,0 +1,561 @@
+package org.spongycastle.crypto.agreement.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.agreement.jpake.JPAKEParticipant;
+import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroups;
+import org.spongycastle.crypto.agreement.jpake.JPAKERound1Payload;
+import org.spongycastle.crypto.agreement.jpake.JPAKERound2Payload;
+import org.spongycastle.crypto.agreement.jpake.JPAKERound3Payload;
+import org.spongycastle.crypto.agreement.jpake.JPAKEUtil;
+import org.spongycastle.crypto.digests.SHA256Digest;
+
+public class JPAKEParticipantTest
+ extends TestCase
+{
+
+ public void testConstruction()
+ throws CryptoException
+ {
+ JPAKEPrimeOrderGroup group = JPAKEPrimeOrderGroups.SUN_JCE_1024;
+ SecureRandom random = new SecureRandom();
+ Digest digest = new SHA256Digest();
+ String participantId = "participantId";
+ char[] password = "password".toCharArray();
+
+ // should succeed
+ new JPAKEParticipant(participantId, password, group, digest, random);
+
+ // null participantId
+ try
+ {
+ new JPAKEParticipant(null, password, group, digest, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // null password
+ try
+ {
+ new JPAKEParticipant(participantId, null, group, digest, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // empty password
+ try
+ {
+ new JPAKEParticipant(participantId, "".toCharArray(), group, digest, random);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // null group
+ try
+ {
+ new JPAKEParticipant(participantId, password, null, digest, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // null digest
+ try
+ {
+ new JPAKEParticipant(participantId, password, group, null, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // null random
+ try
+ {
+ new JPAKEParticipant(participantId, password, group, digest, null);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+ }
+
+ public void testSuccessfulExchange()
+ throws CryptoException
+ {
+
+ JPAKEParticipant alice = createAlice();
+ JPAKEParticipant bob = createBob();
+
+ ExchangeAfterRound2Creation exchange = runExchangeUntilRound2Creation(alice, bob);
+
+ alice.validateRound2PayloadReceived(exchange.bobRound2Payload);
+ bob.validateRound2PayloadReceived(exchange.aliceRound2Payload);
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+
+ assertEquals(aliceKeyingMaterial, bobKeyingMaterial);
+
+ }
+
+ public void testIncorrectPassword()
+ throws CryptoException
+ {
+
+ JPAKEParticipant alice = createAlice();
+ JPAKEParticipant bob = createBobWithWrongPassword();
+
+ ExchangeAfterRound2Creation exchange = runExchangeUntilRound2Creation(alice, bob);
+
+ alice.validateRound2PayloadReceived(exchange.bobRound2Payload);
+ bob.validateRound2PayloadReceived(exchange.aliceRound2Payload);
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ try
+ {
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ }
+
+ /**
+ * Tests that {@link JPAKEParticipant} throws appropriate {@link IllegalStateException}s
+ * when the methods are called in the wrong order.
+ */
+ public void testStateValidation()
+ throws CryptoException
+ {
+
+ JPAKEParticipant alice = createAlice();
+ JPAKEParticipant bob = createBob();
+
+ // We're testing alice here. Bob is just used for help.
+
+ // START ROUND 1 CHECKS
+
+ assertEquals(JPAKEParticipant.STATE_INITIALIZED, alice.getState());
+
+ // create round 2 before round 1
+ try
+ {
+ alice.createRound2PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound1Payload aliceRound1Payload = alice.createRound1PayloadToSend();
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_1_CREATED, alice.getState());
+
+ // create round 1 payload twice
+ try
+ {
+ alice.createRound1PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // create round 2 before validating round 1
+ try
+ {
+ alice.createRound2PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // validate round 2 before validating round 1
+ try
+ {
+ alice.validateRound2PayloadReceived(null);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound1Payload bobRound1Payload = bob.createRound1PayloadToSend();
+
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_1_VALIDATED, alice.getState());
+
+ // validate round 1 payload twice
+ try
+ {
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ bob.validateRound1PayloadReceived(aliceRound1Payload);
+
+ // START ROUND 2 CHECKS
+
+ JPAKERound2Payload aliceRound2Payload = alice.createRound2PayloadToSend();
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_2_CREATED, alice.getState());
+
+ // create round 2 payload twice
+ try
+ {
+ alice.createRound2PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // create key before validating round 2
+ try
+ {
+ alice.calculateKeyingMaterial();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // validate round 3 before validating round 2
+ try
+ {
+ alice.validateRound3PayloadReceived(null, null);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound2Payload bobRound2Payload = bob.createRound2PayloadToSend();
+
+ alice.validateRound2PayloadReceived(bobRound2Payload);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_2_VALIDATED, alice.getState());
+
+ // validate round 2 payload twice
+ try
+ {
+ alice.validateRound2PayloadReceived(bobRound2Payload);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ bob.validateRound2PayloadReceived(aliceRound2Payload);
+
+ // create round 3 before calculating key
+ try
+ {
+ alice.createRound3PayloadToSend(BigInteger.ONE);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // START KEY CALCULATION CHECKS
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+
+ assertEquals(JPAKEParticipant.STATE_KEY_CALCULATED, alice.getState());
+
+ // calculate key twice
+ try
+ {
+ alice.calculateKeyingMaterial();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ // START ROUND 3 CHECKS
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_3_CREATED, alice.getState());
+
+ // create round 3 payload twice
+ try
+ {
+ alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_3_VALIDATED, alice.getState());
+
+ // validate round 3 payload twice
+ try
+ {
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+
+
+ }
+
+ /**
+ * Tests that {@link JPAKEParticipant#validateRound1PayloadReceived(JPAKERound1Payload)}
+ * calls the appropriate validate methods in {@link JPAKEUtil}.
+ * Note that {@link JPAKEUtilTest} tests the individual validate methods
+ * called by {@link JPAKEParticipant} more extensively.
+ */
+ public void testValidateRound1PayloadReceived()
+ throws CryptoException
+ {
+
+ // We're testing alice here. Bob is just used for help.
+
+ JPAKERound1Payload bobRound1Payload = createBob().createRound1PayloadToSend();
+
+ // should succeed
+ createAlice().validateRound1PayloadReceived(bobRound1Payload);
+
+ // alice verifies alice's payload
+ try
+ {
+ JPAKEParticipant alice = createAlice();
+ alice.validateRound1PayloadReceived(alice.createRound1PayloadToSend());
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // g^x4 == 1
+ try
+ {
+ createAlice().validateRound1PayloadReceived(new JPAKERound1Payload(
+ bobRound1Payload.getParticipantId(),
+ bobRound1Payload.getGx1(),
+ BigInteger.ONE,
+ bobRound1Payload.getKnowledgeProofForX1(),
+ bobRound1Payload.getKnowledgeProofForX2()));
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // zero knowledge proof for x3 fails
+ try
+ {
+ JPAKERound1Payload bobRound1Payload2 = createBob().createRound1PayloadToSend();
+ createAlice().validateRound1PayloadReceived(new JPAKERound1Payload(
+ bobRound1Payload.getParticipantId(),
+ bobRound1Payload.getGx1(),
+ bobRound1Payload.getGx2(),
+ bobRound1Payload2.getKnowledgeProofForX1(),
+ bobRound1Payload.getKnowledgeProofForX2()));
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // zero knowledge proof for x4 fails
+ try
+ {
+ JPAKERound1Payload bobRound1Payload2 = createBob().createRound1PayloadToSend();
+ createAlice().validateRound1PayloadReceived(new JPAKERound1Payload(
+ bobRound1Payload.getParticipantId(),
+ bobRound1Payload.getGx1(),
+ bobRound1Payload.getGx2(),
+ bobRound1Payload.getKnowledgeProofForX1(),
+ bobRound1Payload2.getKnowledgeProofForX2()));
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ }
+
+ /**
+ * Tests that {@link JPAKEParticipant#validateRound2PayloadReceived(JPAKERound2Payload)}
+ * calls the appropriate validate methods in {@link JPAKEUtil}.
+ * Note that {@link JPAKEUtilTest} tests the individual validate methods
+ * called by {@link JPAKEParticipant} more extensively.
+ */
+ public void testValidateRound2PayloadReceived()
+ throws CryptoException
+ {
+
+ // We're testing alice here. Bob is just used for help.
+
+ // should succeed
+ ExchangeAfterRound2Creation exchange1 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ exchange1.alice.validateRound2PayloadReceived(exchange1.bobRound2Payload);
+
+ // alice verifies alice's payload
+ ExchangeAfterRound2Creation exchange2 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ try
+ {
+ exchange2.alice.validateRound2PayloadReceived(exchange2.aliceRound2Payload);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong z
+ ExchangeAfterRound2Creation exchange3 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ ExchangeAfterRound2Creation exchange4 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ try
+ {
+ exchange3.alice.validateRound2PayloadReceived(exchange4.bobRound2Payload);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ }
+
+ private static class ExchangeAfterRound2Creation
+ {
+
+ public JPAKEParticipant alice;
+ public JPAKERound2Payload aliceRound2Payload;
+ public JPAKERound2Payload bobRound2Payload;
+
+ public ExchangeAfterRound2Creation(
+ JPAKEParticipant alice,
+ JPAKERound2Payload aliceRound2Payload,
+ JPAKERound2Payload bobRound2Payload)
+ {
+ this.alice = alice;
+ this.aliceRound2Payload = aliceRound2Payload;
+ this.bobRound2Payload = bobRound2Payload;
+ }
+
+ }
+
+ private ExchangeAfterRound2Creation runExchangeUntilRound2Creation(JPAKEParticipant alice, JPAKEParticipant bob)
+ throws CryptoException
+ {
+ JPAKERound1Payload aliceRound1Payload = alice.createRound1PayloadToSend();
+ JPAKERound1Payload bobRound1Payload = bob.createRound1PayloadToSend();
+
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+ bob.validateRound1PayloadReceived(aliceRound1Payload);
+
+ JPAKERound2Payload aliceRound2Payload = alice.createRound2PayloadToSend();
+ JPAKERound2Payload bobRound2Payload = bob.createRound2PayloadToSend();
+
+ return new ExchangeAfterRound2Creation(
+ alice,
+ aliceRound2Payload,
+ bobRound2Payload);
+ }
+
+ private JPAKEParticipant createAlice()
+ {
+ return createParticipant("alice", "password");
+ }
+
+ private JPAKEParticipant createBob()
+ {
+ return createParticipant("bob", "password");
+ }
+
+ private JPAKEParticipant createBobWithWrongPassword()
+ {
+ return createParticipant("bob", "wrong");
+ }
+
+ private JPAKEParticipant createParticipant(String participantId, String password)
+ {
+ return new JPAKEParticipant(
+ participantId,
+ password.toCharArray(),
+ JPAKEPrimeOrderGroups.SUN_JCE_1024);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java
new file mode 100644
index 000000000..b02a8f6ce
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java
@@ -0,0 +1,85 @@
+package org.spongycastle.crypto.agreement.test;
+
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+
+public class JPAKEPrimeOrderGroupTest
+ extends TestCase
+{
+
+ public void testConstruction()
+ throws CryptoException
+ {
+ // p-1 not evenly divisible by q
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(7), BigInteger.valueOf(5), BigInteger.valueOf(6));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // g < 2
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(11), BigInteger.valueOf(5), BigInteger.valueOf(1));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // g > p-1
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(11), BigInteger.valueOf(5), BigInteger.valueOf(11));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // g^q mod p not equal 1
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(11), BigInteger.valueOf(5), BigInteger.valueOf(6));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // p not prime
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(15), BigInteger.valueOf(2), BigInteger.valueOf(4));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // q not prime
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(7), BigInteger.valueOf(6), BigInteger.valueOf(3));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // should succeed
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(7), BigInteger.valueOf(3), BigInteger.valueOf(4));
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEUtilTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEUtilTest.java
new file mode 100644
index 000000000..beaa2be01
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/agreement/test/JPAKEUtilTest.java
@@ -0,0 +1,267 @@
+package org.spongycastle.crypto.agreement.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+import org.spongycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroups;
+import org.spongycastle.crypto.agreement.jpake.JPAKEUtil;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+
+public class JPAKEUtilTest
+ extends TestCase
+{
+ private static final BigInteger TEN = BigInteger.valueOf(10);
+
+ public void testValidateGx4()
+ throws CryptoException
+ {
+ JPAKEUtil.validateGx4(TEN);
+
+ try
+ {
+ JPAKEUtil.validateGx4(BigInteger.ONE);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateGa()
+ throws CryptoException
+ {
+ JPAKEUtil.validateGa(TEN);
+
+ try
+ {
+ JPAKEUtil.validateGa(BigInteger.ONE);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateParticipantIdsDiffer()
+ throws CryptoException
+ {
+ JPAKEUtil.validateParticipantIdsDiffer("a", "b");
+ JPAKEUtil.validateParticipantIdsDiffer("a", "A");
+
+ try
+ {
+ JPAKEUtil.validateParticipantIdsDiffer("a", "a");
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateParticipantIdsEqual()
+ throws CryptoException
+ {
+ JPAKEUtil.validateParticipantIdsEqual("a", "a");
+
+ try
+ {
+ JPAKEUtil.validateParticipantIdsEqual("a", "b");
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateMacTag()
+ throws CryptoException
+ {
+ JPAKEPrimeOrderGroup pg1 = JPAKEPrimeOrderGroups.SUN_JCE_1024;
+
+ SecureRandom random = new SecureRandom();
+ Digest digest = new SHA256Digest();
+
+ BigInteger x1 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger x2 = JPAKEUtil.generateX2(pg1.getQ(), random);
+ BigInteger x3 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger x4 = JPAKEUtil.generateX2(pg1.getQ(), random);
+
+ BigInteger gx1 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x1);
+ BigInteger gx2 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x2);
+ BigInteger gx3 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x3);
+ BigInteger gx4 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x4);
+
+ BigInteger gB = JPAKEUtil.calculateGA(pg1.getP(), gx3, gx1, gx2);
+
+ BigInteger s = JPAKEUtil.calculateS("password".toCharArray());
+
+ BigInteger xs = JPAKEUtil.calculateX2s(pg1.getQ(), x4, s);
+
+ BigInteger B = JPAKEUtil.calculateA(pg1.getP(), pg1.getQ(), gB, xs);
+
+ BigInteger keyingMaterial = JPAKEUtil.calculateKeyingMaterial(pg1.getP(), pg1.getQ(), gx4, x2, s, B);
+
+ BigInteger macTag = JPAKEUtil.calculateMacTag("participantId", "partnerParticipantId", gx1, gx2, gx3, gx4, keyingMaterial, digest);
+
+ // should succed
+ JPAKEUtil.validateMacTag("partnerParticipantId", "participantId", gx3, gx4, gx1, gx2, keyingMaterial, digest, macTag);
+
+ // validating own macTag (as opposed to the other party's mactag)
+ try
+ {
+ JPAKEUtil.validateMacTag("participantId", "partnerParticipantId", gx1, gx2, gx3, gx4, keyingMaterial, digest, macTag);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // participant ids switched
+ try
+ {
+ JPAKEUtil.validateMacTag("participantId", "partnerParticipantId", gx3, gx4, gx1, gx2, keyingMaterial, digest, macTag);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateNotNull()
+ {
+ JPAKEUtil.validateNotNull("a", "description");
+
+ try
+ {
+ JPAKEUtil.validateNotNull(null, "description");
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateZeroKnowledgeProof()
+ throws CryptoException
+ {
+ JPAKEPrimeOrderGroup pg1 = JPAKEPrimeOrderGroups.SUN_JCE_1024;
+
+ SecureRandom random = new SecureRandom();
+ Digest digest1 = new SHA256Digest();
+
+ BigInteger x1 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger gx1 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x1);
+ String participantId1 = "participant1";
+
+ BigInteger[] zkp1 = JPAKEUtil.calculateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, x1, participantId1, digest1, random);
+
+ // should succeed
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp1, participantId1, digest1);
+
+ // wrong group
+ JPAKEPrimeOrderGroup pg2 = JPAKEPrimeOrderGroups.NIST_3072;
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg2.getP(), pg2.getQ(), pg2.getG(), gx1, zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong digest
+ Digest digest2 = new SHA1Digest();
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp1, participantId1, digest2);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong participant
+ String participantId2 = "participant2";
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp1, participantId2, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong gx
+ BigInteger x2 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger gx2 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x2);
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx2, zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong zkp
+ BigInteger[] zkp2 = JPAKEUtil.calculateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx2, x2, participantId1, digest1, random);
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp2, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // gx <= 0
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), BigInteger.ZERO, zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // gx >= p
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), pg1.getP(), zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // gx mod q == 1
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), pg1.getQ().add(BigInteger.ONE), zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/AllTests.java
new file mode 100644
index 000000000..5186f4b26
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/AllTests.java
@@ -0,0 +1,39 @@
+package org.spongycastle.crypto.ec.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.spongycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testCrypto()
+ {
+ org.spongycastle.util.test.Test[] tests = { new ECElGamalTest(), new ECTransformationTest() };
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Lightweight EC ElGamal Tests");
+
+ suite.addTestSuite(AllTests.class);
+
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/ECElGamalTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/ECElGamalTest.java
new file mode 100644
index 000000000..9e1963a01
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/ECElGamalTest.java
@@ -0,0 +1,84 @@
+package org.spongycastle.crypto.ec.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.ec.ECDecryptor;
+import org.spongycastle.crypto.ec.ECElGamalDecryptor;
+import org.spongycastle.crypto.ec.ECElGamalEncryptor;
+import org.spongycastle.crypto.ec.ECEncryptor;
+import org.spongycastle.crypto.ec.ECPair;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ECElGamalTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ECElGamal";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ParametersWithRandom pRandom = new ParametersWithRandom(pubKey, new SecureRandom());
+
+ doTest(priKey, pRandom, BigInteger.valueOf(20));
+
+ BigInteger rand = new BigInteger(pubKey.getParameters().getN().bitLength() - 1, new SecureRandom());
+
+ doTest(priKey, pRandom, rand);
+ }
+
+ private void doTest(ECPrivateKeyParameters priKey, ParametersWithRandom pRandom, BigInteger value)
+ {
+ ECPoint data = priKey.getParameters().getG().multiply(value);
+
+ ECEncryptor encryptor = new ECElGamalEncryptor();
+
+ encryptor.init(pRandom);
+
+ ECPair pair = encryptor.encrypt(data);
+
+ ECDecryptor decryptor = new ECElGamalDecryptor();
+
+ decryptor.init(priKey);
+
+ ECPoint result = decryptor.decrypt(pair);
+
+ if (!data.equals(result))
+ {
+ fail("point pair failed to decrypt back to original");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new ECElGamalTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/ECTransformationTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/ECTransformationTest.java
new file mode 100644
index 000000000..31c66469b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/ec/test/ECTransformationTest.java
@@ -0,0 +1,145 @@
+package org.spongycastle.crypto.ec.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.ec.ECDecryptor;
+import org.spongycastle.crypto.ec.ECElGamalDecryptor;
+import org.spongycastle.crypto.ec.ECElGamalEncryptor;
+import org.spongycastle.crypto.ec.ECEncryptor;
+import org.spongycastle.crypto.ec.ECNewPublicKeyTransform;
+import org.spongycastle.crypto.ec.ECNewRandomnessTransform;
+import org.spongycastle.crypto.ec.ECPair;
+import org.spongycastle.crypto.ec.ECPairTransform;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ECTransformationTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ECTransformationTest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+
+ ParametersWithRandom pRandom = new ParametersWithRandom(pubKey, new SecureRandom());
+
+ doTest(priKey, pRandom, BigInteger.valueOf(20));
+
+ BigInteger rand = new BigInteger(pubKey.getParameters().getN().bitLength() - 1, new SecureRandom());
+
+ doTest(priKey, pRandom, rand);
+ doSameKeyTest(priKey, pRandom, rand);
+ }
+
+ private void doTest(ECPrivateKeyParameters priKey, ParametersWithRandom pRandom, BigInteger value)
+ {
+ ECPoint data = priKey.getParameters().getG().multiply(value);
+
+ ECEncryptor encryptor = new ECElGamalEncryptor();
+
+ encryptor.init(pRandom);
+
+ ECPair pair = encryptor.encrypt(data);
+
+ ECKeyPairGenerator ecGen = new ECKeyPairGenerator();
+
+ ecGen.init(new ECKeyGenerationParameters(priKey.getParameters(), new SecureRandom()));
+
+ AsymmetricCipherKeyPair reEncKP = ecGen.generateKeyPair();
+
+ ECPairTransform ecr = new ECNewPublicKeyTransform();
+
+ ecr.init(reEncKP.getPublic());
+
+ ECPair srcPair = pair;
+
+ // re-encrypt the message portion
+ pair = ecr.transform(srcPair);
+
+ ECDecryptor decryptor = new ECElGamalDecryptor();
+
+ decryptor.init(priKey);
+
+ // decrypt out the original private key
+ ECPoint p = decryptor.decrypt(new ECPair(srcPair.getX(), pair.getY()));
+
+ decryptor.init(reEncKP.getPrivate());
+
+ // decrypt the fully transformed point.
+ ECPoint result = decryptor.decrypt(new ECPair(pair.getX(), p));
+
+ if (!data.equals(result))
+ {
+ fail("point pair failed to decrypt back to original");
+ }
+ }
+
+ private void doSameKeyTest(ECPrivateKeyParameters priKey, ParametersWithRandom pRandom, BigInteger value)
+ {
+ ECPoint data = priKey.getParameters().getG().multiply(value);
+
+ ECEncryptor encryptor = new ECElGamalEncryptor();
+
+ encryptor.init(pRandom);
+
+ ECPair pair = encryptor.encrypt(data);
+
+ ECPairTransform ecr = new ECNewRandomnessTransform();
+
+ ecr.init(pRandom);
+
+ ECPair srcPair = pair;
+
+ // re-encrypt the message portion
+ pair = ecr.transform(srcPair);
+
+ ECDecryptor decryptor = new ECElGamalDecryptor();
+
+ decryptor.init(priKey);
+
+ // decrypt the fully transformed point.
+ ECPoint result = decryptor.decrypt(pair);
+
+ if (!data.equals(result))
+ {
+ fail("point pair failed to decrypt back to original");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new ECTransformationTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/AllTests.java
new file mode 100644
index 000000000..61a17dd41
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/AllTests.java
@@ -0,0 +1,39 @@
+package org.spongycastle.crypto.prng.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.spongycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testCrypto()
+ {
+ org.spongycastle.util.test.Test[] tests = RegressionTest.tests;
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Lightweight Crypto PRNG Tests");
+
+ suite.addTestSuite(AllTests.class);
+
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/CTRDRBGTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/CTRDRBGTest.java
new file mode 100644
index 000000000..0a4d09db2
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/CTRDRBGTest.java
@@ -0,0 +1,513 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.params.DESedeParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.prng.drbg.CTRSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * CTR DRBG Test
+ */
+public class CTRDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "CTRDRBGTest";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new CTRDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "ABC88224514D0316EA3D48AEE3C9A2B4",
+ "D3D3F372E43E7ABDC4FA293743EED076"
+ }
+ ),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "D4564EE072ACA5BD279536E14F94CB12",
+ "1CCD9AFEF15A9679BA75E35225585DEA"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "760BED7D92B083B10AF31CF0656081EB",
+ "FD1AC41482384D823CF3FD6F0E6C88B3"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "7A4C1D7ADC8A67FDB50100ED23583A2C",
+ "43044D311C0E07541CA5C8B0916976B2"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "8FB78ABCA75C9F284E974E36141866BC",
+ "9D9745FF31C42A4488CBB771B13B5D86"
+ }
+ ),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "0E389920A09B485AA4ABD0CA7E60D89C",
+ "F4478EC6659A0D3577625B0C73A211DD"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "64983055D014550B39DE699E43130B64",
+ "035FDDA8582A2214EC722C410A8D95D3"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "A29C1A8C42FBC562D7D1DBA7DC541FFE",
+ "0BDA66B049429061C013E4228C2F44C6"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "8CF59C8CF6888B96EB1C1E3E79D82387AF08A9E5FF75E23F1FBCD4559B6B997E",
+ "69CDEF912C692D61B1DA4C05146B52EB7B8849BD87937835328254EC25A9180E"
+ }
+ ),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "E8C74A4B7BFFB53BEB80E78CA86BB6DF70E2032AEB473E0DD54D2339CEFCE9D0",
+ "26B3F823B4DBAFC23B141375E10B3AEB7A0B5DEF1C7D760B6F827D01ECD17AC7"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "18FDEFBDC43D7A36D5D6D862205765D1D701C9F237007030DF1B8E70EE4EEE29",
+ "9888F1D38BB1CCE31B363AA1BD9B39616876C30DEE1FF0B7BD8C4C441715C833"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "BFF4B85D68C84529F24F69F9ACF1756E29BA648DDEB825C225FA32BA490EF4A9",
+ "9BD2635137A52AF7D0FCBEFEFB97EA93A0F4C438BD98956C0DACB04F15EE25B3"
+ }
+ ),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "4573AC8BBB33D7CC4DBEF3EEDF6EAE748B536C3A1082CEE4948CDB51C83A7F9C",
+ "99C628CDD87BD8C2F1FE443AA7F761DA16886436326323354DA6311FFF5BC678"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "F324104E2FA14F79D8AA60DF06B93B3BC157324958F0A7EE1E193677A70E0250",
+ "78F4C840134F40DC001BFAD3A90B5EF4DEBDBFAC3CFDF0CD69A89DC4FD34713F"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"),
+ new DRBGTestVector(
+ new AESFastEngine(), 192,
+ new Bit320EntropyProvider().get(320),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "E231244B3235B085C81604424357E85201E3828B5C45568679A5555F867AAC8C",
+ "DDD0F7BCCADADAA31A67652259CE569A271DD85CF66C3D6A7E9FAED61F38D219"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"),
+ new DRBGTestVector(
+ new AESFastEngine(), 192,
+ new Bit320EntropyProvider().get(320),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "F780D4A2C25CF8EE7407D948EC0B724A4235D8B20E65081392755CA7912AD7C0",
+ "BA14617F915BA964CB79276BDADC840C14B631BBD1A59097054FA6DFF863B238"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "47111E146562E9AA2FB2A1B095D37A8165AF8FC7CA611D632BE7D4C145C83900",
+ "98A28E3B1BA363C9DAF0F6887A1CF52B833D3354D77A7C10837DD63DD2E645F8"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "71BB3F9C9CEAF4E6C92A83EB4C7225010EE150AC75E23F5F77AD5073EF24D88A",
+ "386DEBBBF091BBF0502957B0329938FB836B82E594A2F5FDD5EB28D4E35528F4"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "1A2E3FEE9056E98D375525FDC2B63B95B47CE51FCF594D804BD5A17F2E01139B",
+ "601F95384F0D85946301D1EACE8F645A825CE38F1E2565B0C0C439448E9CA8AC"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "EAE6BCE781807E524D26605EA198077932D01EEB445B9AC6C5D99C101D29F46E",
+ "738E99C95AF59519AAD37FF3D5180986ADEBAB6E95836725097E50A8D1D0BD28"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF")
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new CTRSP800DRBG(tv.getCipher(), tv.keySizeInBits(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // DESede/TDEA key parity test
+ DRBGTestVector tv = tests[0];
+
+ SP80090DRBG drbg = new CTRSP800DRBG(new KeyParityCipher(tv.getCipher()), tv.keySizeInBits(), tv.securityStrength(), tv.entropySource(), tv.personalizationString(), tv.nonce());
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ drbg.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ // Exception tests
+ SP80090DRBG d;
+ try
+ {
+ d = new CTRSP800DRBG(new AESEngine(), 256, 256, new Bit232EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Not enough entropy for security strength required"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new CTRSP800DRBG(new DESedeEngine(), 256, 256, new Bit232EntropyProvider().get(232), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new CTRSP800DRBG(new DESedeEngine(), 168, 256, new Bit232EntropyProvider().get(232), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new CTRSP800DRBG(new AESEngine(), 192, 256, new Bit232EntropyProvider().get(232), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class Bit232EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit232EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C" +
+ "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC"), true);
+ }
+ }
+
+ private class Bit256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit256EntropyProvider()
+ {
+ super(Hex.decode(
+ "0001020304050607"+
+ "08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"+
+ "8081828384858687"+
+ "88898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F"+
+ "C0C1C2C3C4C5C6C7"+
+ "C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"), true);
+ }
+ }
+
+ private class Bit320EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit320EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F"+
+ "101112131415161718191A1B1C1D1E1F2021222324252627"+
+ "808182838485868788898A8B8C8D8E8F"+
+ "909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7"+
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
+ "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7"), true);
+ }
+ }
+
+ private class Bit384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F1011121314151617" +
+ "18191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F" +
+ "808182838485868788898A8B8C8D8E8F9091929394959697" +
+ "98999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7" +
+ "D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"), true);
+ }
+ }
+
+ private class KeyParityCipher
+ implements BlockCipher
+ {
+ private BlockCipher cipher;
+
+ KeyParityCipher(BlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ byte[] k = Arrays.clone(((KeyParameter)params).getKey());
+
+ DESedeParameters.setOddParity(k);
+
+ if (!Arrays.areEqual(((KeyParameter)params).getKey(), k))
+ {
+ fail("key not odd parity");
+ }
+
+ cipher.init(forEncryption, params);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return cipher.processBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ cipher.reset();
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/DRBGTestVector.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/DRBGTestVector.java
new file mode 100644
index 000000000..b0dd3b72a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/DRBGTestVector.java
@@ -0,0 +1,131 @@
+package org.spongycastle.crypto.prng.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.util.encoders.Hex;
+
+public class DRBGTestVector
+{
+ private Digest _digest;
+ private BlockCipher _cipher;
+ private int _keySizeInBits;
+ private EntropySource _eSource;
+ private boolean _pr;
+ private String _nonce;
+ private String _personalisation;
+ private int _ss;
+ private String[] _ev;
+ private List _ai = new ArrayList();
+
+ public DRBGTestVector(Digest digest, EntropySource eSource, boolean predictionResistance, String nonce, int securityStrength, String[] expected)
+ {
+ _digest = digest;
+ _eSource = eSource;
+ _pr = predictionResistance;
+ _nonce = nonce;
+ _ss = securityStrength;
+ _ev = expected;
+ _personalisation = null;
+ }
+
+ public DRBGTestVector(BlockCipher cipher, int keySizeInBits, EntropySource eSource, boolean predictionResistance, String nonce, int securityStrength, String[] expected)
+ {
+ _cipher = cipher;
+ _keySizeInBits = keySizeInBits;
+ _eSource = eSource;
+ _pr = predictionResistance;
+ _nonce = nonce;
+ _ss = securityStrength;
+ _ev = expected;
+ _personalisation = null;
+ }
+
+ public Digest getDigest()
+ {
+ return _digest;
+ }
+
+ public BlockCipher getCipher()
+ {
+ return _cipher;
+ }
+
+ public int keySizeInBits()
+ {
+ return _keySizeInBits;
+ }
+
+ public DRBGTestVector addAdditionalInput(String input)
+ {
+ _ai.add(input);
+
+ return this;
+ }
+
+ public DRBGTestVector setPersonalizationString(String p)
+ {
+ _personalisation = p;
+
+ return this;
+ }
+
+ public EntropySource entropySource()
+ {
+ return _eSource;
+ }
+
+ public boolean predictionResistance()
+ {
+ return _pr;
+ }
+
+ public byte[] nonce()
+ {
+ if (_nonce == null)
+ {
+ return null;
+ }
+
+ return Hex.decode(_nonce);
+ }
+
+ public byte[] personalizationString()
+ {
+ if (_personalisation == null)
+ {
+ return null;
+ }
+
+ return Hex.decode(_personalisation);
+ }
+
+ public int securityStrength()
+ {
+ return _ss;
+ }
+
+ public byte[] expectedValue(int index)
+ {
+ return Hex.decode(_ev[index]);
+ }
+
+ public byte[] additionalInput(int position)
+ {
+ int len = _ai.size();
+ byte[] rv;
+ if (position >= len)
+ {
+ rv = null;
+ }
+ else
+ {
+ rv = Hex.decode((String)(_ai.get(position)));
+ }
+ return rv;
+ }
+
+ }
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/DualECDRBGTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/DualECDRBGTest.java
new file mode 100644
index 000000000..4aa857085
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/DualECDRBGTest.java
@@ -0,0 +1,415 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.prng.drbg.DualECSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Dual EC SP800-90 DRBG test
+ */
+public class DualECDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "DualECDRBG";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new DualECDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "FF5163C388F791E96F1052D5C8F0BD6FBF7144839C4890FF85487C5C12702E4C9849AF518AE68DEB14D3A62702BBDE4B98AB211765FD87ACA12FC2A6",
+ "9A0A11F2DFB88F7260559DD8DA6134EB2B34CC0415FA8FD0474DB6B85E1A08385F41B435DF81296B1B4EDF66E0107C0844E3D28A89B05046B89177F2"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "C08E954FCD486D0B0934A0236692AC705A835D1A3C94D2ACD4684AB26E978D7D42E73CC06D6EC1472C63E51BED7F71518395836E2052BBD73A20CABB",
+ "1D76DEE36FCC5F9478C112EAFA1C4CCD0635435A6F3A247A3BA3849790B5245070E95C1A67BE7A39BFB213F2C0EFCC171A3253DA6D54DA4362EA2099"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "3AB095CC493A8730D70DE923108B2E4710799044FFC27D0A1156250DDF97E8B05ACE055E49F3E3F5B928CCD18317A3E68FCB0B6F0459ADF9ECF79C87",
+ "7B902FC35B0AF50F57F8822936D08A96E41B16967C6B1AA0BC05032F0D53919DC587B664C883E2FE8F3948002FCD8BCBFC4706BCAA2075EF6BF41167"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "3B68A1D95ED0312150AC1991189780F37EC50E75249F915CD806BBA0C44F9E3A919B2390805E1E90C1D2D1C823B17B96DB44535B72E0CFB62723529D",
+ "250B933475E3BD4FC85D97FD797834B599DEDEDF8B6F15474E1F31B4AF215CFA7A8C0A0296A2E374B3886BB0CC7E49DBB19324564B451E64F12864F9"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "8C77288EDBEA9A742464F78D55E33593C1BF5F9D8CD8609D6D53BAC4E4B42252A227A99BAD0F2358B05955CD35723B549401C71C9C1F32F8A2018E24",
+ "56ECA61C64F69C1C232E992623C71418BD0B96D783118FAAD94A09E3A9DB74D15E805BA7F14625995CA77612B2EF7A05863699ECBABF70D3D422C014"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "A5C397DFEB540E86F0470E9625D5C5AC2D50016FB201E8DF574F2201DFBB42A799FEB9E238AAD301A493382250EEE60D2E2927E500E848E57535ABD1",
+ "BF9894630BEBAF0A0EDFE726285EB055FD2ED678B76673803DD327F49DBEDE87D3E447A6EB73B5D5C52A40078132677F412E9E7DE32B9B1CB32421B9"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "1F858858B65357D6360E1ED8F8475767B08DAB30718CCA01C6FAE77A4BDCE2702C76D0FB4758EA1ED6AA587CFD26B9011DC8A75D0B4154193BB2C1798FFA52BCAB208310" +
+ "3CD2AAD44BEED56D042FC2B8915D7D9BED6437EFEB1582EE",
+ "6E4AAB63938212C870F24BB067A32CA9E7FC2343" +
+ "5D411729268C8BA6F90E87074D04888CE2CC5A916B7AC93F" +
+ "EDE85E2995645DFCC4CE44B9FB41F1BFCC5E9F59EE3A8E1B" +
+ "8F85247F741B7C480521EE6BF8BA319B59048E65F08FAA76"
+ }),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "E6A30AB0C9AFCBA673E4F1C94B3DB1F0C7D78B3D" +
+ "87B967281BE1E7B3CAF5200AED502C26B84FC169FE8336BD" +
+ "23271CB299812F2CF1955AA63FC362044ABA246EF1610F9E" +
+ "DC613924A84A00F8DB3FC65C13373F3171EB20848FA9A70E",
+ "8585764DF1C86EA12ACCB882525BF6217B447486" +
+ "5EBFDA367B8657FA80471139BAC626172B9F219DF2CE9099" +
+ "F65833E07CD1A8DD80468779EA3C26620A2C9C9F5C7EFCDD" +
+ "C036E6F6C8BF70316D3C37FC246A4CC79B3F1DB971D72ED0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F5051525354555657"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "13F6EA9BBA7BABDC2A52A3B9FD73D65ECAA638A0" +
+ "4C74BCCA2ACDE6FD29FEA4B5D884E095E87D1B7C0DEB9D37" +
+ "7AD81FBFEEA2D5EF82C0F6F52B9FCC359E769AC9DF2A876C" +
+ "58BAF21657814F3E66D1680B1D4EBD65581E42534F85197D",
+ "FC0A36F4D20F8F83BE3430AA3C36A49191821A82" +
+ "072BBC3D5AFF8D7EC39484D646277CE87599B6FE8CCA9862" +
+ "559703A10F4DE1066BFD30B80C325E774B512525BC6D3734" +
+ "4C93906368243D31F89E99C4D2A6E9BEB24D5F7267360DCA"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F5051525354555657")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F7071727374757677")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "FE55601BF734493013705CCEB76E44AAD48373F7" +
+ "42E72B83D4701FA6549255F1CDE6217953522FF973BA4F6E" +
+ "C96D2BDCF14A76BE7DEB61781E34B99335BD714F17C91739" +
+ "B4E2AB57E36E9C3116E215D3D94FCFAD532636874875CAC7",
+ "F5E59D0ABADE81F62FFAB9D4A6A26FF200016608" +
+ "A7215E389858FFED83FBC75CFD33DBA6688C89AA32AD22E4" +
+ "80EA3D04EADFB35567B67564207E64B77844E8E4A87502D5" +
+ "02DBBB6D8277F1CACDB7CF8D293D09DB7DD59A950821507A"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F7071727374757677")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "CC788F70FB08F256D9604333630D85936D400F45" +
+ "718DC3F939A8B9F6F75D3E4EC17D68FBB924AEACB7021295" +
+ "48FA63CE9BCB82176639B64DE890A47025B5582312FE934E" +
+ "F0D0A12697C0F05D2DA108CCADB511BA0EB62F4051BB2354",
+ "2C922EA620D76E4137B315EBC29E518F80951B3F" +
+ "0E6173FA2BFD94A230EE513EE2E4EB330D802F620DD24911" +
+ "534EC0F95A1F1D44A2125F5D57476A666FC372092B55D0D6" +
+ "8B49738F5BC466EC206AB3CF6A972B38BCFAE5FCD53C7E21 "
+ }),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(256),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "7A8313798EE1" +
+ "D1898712683F2D0B0DEE5804146ABA64FDA8DB4E539CC8D1" +
+ "E59C74EE5AA48E73E958C8EC85DD529D42E68B4F7E02FFAF" +
+ "3E3EF8312AEA68BC08A414885E60A7DF0B55F9D90210B319" +
+ "E9B8FD23E078A4153636F29AA3CAC8198CB1D5D846151653" +
+ "ECE275A591089261238014E5058410065AB8229EB9115E8E",
+ "918B5D79E646" +
+ "64966D954BC5E2946BF48F061BF0C2701C3C2D1F75EA821E" +
+ "1DA05D5B3C2C4EEA246E806B53BF6BDB3F3D53A3AE756C2A" +
+ "45C72603973A3DE1BC367C283CA124A5589CEAB30E5D2D74" +
+ "8A40DD874FF15B032CF4F4B2AAD590B0DB91A0D38FCE93C5" +
+ "AAD4E55AC482F86FF06FAE66B7C7CCA7E45557E1A5A3B85D"
+ }),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(256),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "C7ED88A2C690" +
+ "1C04802BA2BB04262921B19664835A4A3C002CB9F13E35E3" +
+ "DEB3698A436BF1C85B070E9E6977CA78A5130905AA0C01A9" +
+ "4130F5133DF904A4ACF59A7DD01227E8FCA1C8D51F093839" +
+ "46ECD950113104760D7E216CAF581FE9D3AACE6FC4CDDC4C" +
+ "CD736D26A60BE8BE2A6A78CD752D1EC7CCC802638B177307",
+ "83B78B206785" +
+ "4412EEB24AEA86064D510C68FD96DBF94EAC1BC2022752D7" +
+ "558AEB9F97B9CBC1B9648FE4D88E2C82A6F530675E1DB92D" +
+ "396D6D85BDAD2A23CBD10AD808ECCCFBFC811EB68AE835E4" +
+ "912E011DD10A4399C8DE2D9D88F81B6168B05D282B9DAC1E" +
+ "65E0A45F61043E1FA047870DD582295E6C50DD1185B13594 "
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(256),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "CC7035C73040" +
+ "5CF5DF7137ED9E10744B75B540AFFC68EB564B71C0F737E8" +
+ "F656B6171940497FA90D8F383EFB6FC6717BA14AAA164EF5" +
+ "6641C0F513312551DCD21D0A5B0DBDCD97F627E968DFD752" +
+ "56C11CF2BCCA5822EAACE796A34CB7D2F8CD8CC6DBE76274" +
+ "498289BBC4C2F1CADA6185D82605CF992EC285BC4945EE9E",
+ "0E6C329AD1BE" +
+ "681EB1E6F5E03A89E3D80153D6CCDD5A3ECF865003EE4A2D" +
+ "E5A23B7F43681361CFAFC3A3FEF17777E75CF9D6685573C8" +
+ "87A3962CB955076D45D6F1E45EE4B8CB31A4731CDA031FA2" +
+ "815B6D34E29F2603526CE186576F4CCA3FEDF7F8ACDB37C9" +
+ "9D762706ABE4967D44739C8CFCFCC76C58B1ED243AC394C0"
+ }),
+ // From http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip
+ // modified to test partial block processing.
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new TestEntropySourceProvider(Hex.decode("a826f1cd3fa24b9e71c316e5bf2bafff"), false).get(128),
+ false,
+ "82bc3bf050614b34",
+ 128,
+ new String[]
+ {
+ "14949b876e30f832331f59f2e687350bea9ba22b78549521a70748ca916c74ebff0b638266aa" +
+ "d81e089545eb60bfe332f7d134d91ed3c104f975fae0f71391add71e3380a725251ed5552a84" +
+ "650637eddfc88b5ab26311277cbc429aa152b2cfac61c67846512d7564114177a622f25e870a" +
+ "acec37c0977d",
+ "7050bf74a887809673ecd295071f7a457d1e2e227f68ef4b4445e34f3904b95d4833180ee522" +
+ "104bfc996234063e2c76173937b883c66b0e64a56643877228cad5212cddbf839270ef80889b" +
+ "c83424c141c2419f2231004c8860f8fd95435e2c9f8ac7409fcbfb6a74851fadc7d99bf5d68b" +
+ "591892f0e3a1"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new TestEntropySourceProvider(Hex.decode("a826f1cd3fa24b9e71c316e5bf2bafff"), false).get(128),
+ false,
+ "82bc3bf050614b34",
+ 128,
+ new String[]
+ {
+ "14949b876e30f832331f59f2e687350bea9ba22b78549521a70748ca916c74ebff0b638266aa" +
+ "d81e089545eb60bfe332f7d134d91ed3c104f975fae0f71391add71e3380a725251ed5552a84" +
+ "650637eddfc88b5ab26311277cbc429aa152b2cfac61c67846512d7564114177a622f25e870a" +
+ "acec37c0977d",
+ "7050bf74a887809673ecd295071f7a457d1e2e227f68ef4b4445e34f3904b95d4833180ee522" +
+ "104bfc996234063e2c76173937b883c66b0e64a56643877228cad5212cddbf839270ef80889b" +
+ "c83424c141c2419f2231004c8860f8fd95435e2c9f8ac7409fcbfb6a74851fadc7d99bf5d68b" +
+ "591892f0e3"
+ })
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new DualECSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(1).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // Exception tests
+ //
+ SP80090DRBG d;
+
+ try
+ {
+ d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(1 << (13 - 1) + 1), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new DualECSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by digest"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F " +
+ "808182838485868788898A8B8C8D8E8F" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"), true);
+ }
+ }
+
+ private class SHA384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F1011121314151617" +
+ "808182838485868788898A8B8C8D8E8F9091929394959697" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7"), true);
+ }
+ }
+
+ private class SHA512EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA512EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" +
+ "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"), true);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/FixedSecureRandomTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/FixedSecureRandomTest.java
new file mode 100644
index 000000000..b10ad367f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/FixedSecureRandomTest.java
@@ -0,0 +1,68 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.crypto.prng.FixedSecureRandom;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class FixedSecureRandomTest
+ extends SimpleTest
+{
+ byte[] base = Hex.decode("deadbeefdeadbeef");
+ byte[] r1 = Hex.decode("cafebabecafebabe");
+ byte[] r2 = Hex.decode("ffffffffcafebabedeadbeef");
+
+ public String getName()
+ {
+ return "FixedSecureRandom";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ FixedSecureRandom fixed = new FixedSecureRandom(base);
+ byte[] buf = new byte[8];
+
+ fixed.nextBytes(buf);
+
+ if (!Arrays.areEqual(buf, base))
+ {
+ fail("wrong data returned");
+ }
+
+ fixed = new FixedSecureRandom(base);
+
+ byte[] seed = fixed.generateSeed(8);
+
+ if (!Arrays.areEqual(seed, base))
+ {
+ fail("wrong seed data returned");
+ }
+
+ if (!fixed.isExhausted())
+ {
+ fail("not exhausted");
+ }
+
+ fixed = new FixedSecureRandom(new byte[][] { r1, r2 });
+
+ seed = fixed.generateSeed(12);
+
+ if (!Arrays.areEqual(seed, Hex.decode("cafebabecafebabeffffffff")))
+ {
+ fail("wrong seed data returned - composite");
+ }
+
+ fixed.nextBytes(buf);
+
+ if (!Arrays.areEqual(buf, Hex.decode("cafebabedeadbeef")))
+ {
+ fail("wrong data returned");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new FixedSecureRandomTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/HMacDRBGTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/HMacDRBGTest.java
new file mode 100644
index 000000000..cfbd386d6
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/HMacDRBGTest.java
@@ -0,0 +1,508 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.prng.drbg.HMacSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * HMAC SP800-90 DRBG
+ */
+public class HMacDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "HMacDRBG";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HMacDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "5A7D3B449F481CB38DF79AD2B1FCC01E57F8135E8C0B22CD0630BFB0127FB5408C8EFC17A929896E",
+ "82cf772ec3e84b00fc74f5df104efbfb2428554e9ce367d03aeade37827fa8e9cb6a08196115d948"
+ }),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "B3BD05246CBA12A64735A4E3FDE599BC1BE30F439BD060208EEA7D71F9D123DF47B3CE069D98EDE6",
+ "B5DADA380E2872DF935BCA55B882C8C9376902AB639765472B71ACEBE2EA8B1B6B49629CB67317E0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "C7AAAC583C6EF6300714C2CC5D06C148CFFB40449AD0BB26FAC0497B5C57E161E36681BCC930CE80",
+ "6EBD2B7B5E0A2AD7A24B1BF9A1DBA47D43271719B9C37B7FE81BA94045A14A7CB514B446666EA5A7"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "FEC4597F06A3A8CC8529D59557B9E661053809C0BC0EFC282ABD87605CC90CBA9B8633DCB1DAE02E",
+ "84ADD5E2D2041C01723A4DE4335B13EFDF16B0E51A0AD39BD15E862E644F31E4A2D7D843E57C5968"
+ }),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "6C37FDD729AA40F80BC6AB08CA7CC649794F6998B57081E4220F22C5C283E2C91B8E305AB869C625",
+ "CAF57DCFEA393B9236BF691FA456FEA7FDF1DF8361482CA54D5FA723F4C88B4FA504BF03277FA783"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "A1BA8FA58BB5013F43F7B6ED52B4539FA16DC77957AEE815B9C07004C7E992EB8C7E591964AFEEA2",
+ "84264A73A818C95C2F424B37D3CC990B046FB50C2DC64A164211889A010F2471A0912FFEA1BF0195"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "D67B8C1734F46FA3F763CF57C6F9F4F2" +
+ "DC1089BD8BC1F6F023950BFC5617635208C8501238AD7A44" +
+ "00DEFEE46C640B61AF77C2D1A3BFAA90EDE5D207406E5403",
+ "8FDAEC20F8B421407059E3588920DA7E" +
+ "DA9DCE3CF8274DFA1C59C108C1D0AA9B0FA38DA5C792037C" +
+ "4D33CD070CA7CD0C5608DBA8B885654639DE2187B74CB263"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "FABD0AE25C69DC2EFDEFB7F20C5A31B5" +
+ "7AC938AB771AA19BF8F5F1468F665C938C9A1A5DF0628A56" +
+ "90F15A1AD8A613F31BBD65EEAD5457D5D26947F29FE91AA7",
+ "6BD925B0E1C232EFD67CCD84F722E927" +
+ "ECB46AB2B740014777AF14BA0BBF53A45BDBB62B3F7D0B9C" +
+ "8EEAD057C0EC754EF8B53E60A1F434F05946A8B686AFBC7A"
+ }),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]{
+ "03AB8BCE4D1DBBB636C5C5B7E1C58499FEB1C619CDD11D35" +
+ "CD6CF6BB8F20EF27B6F5F9054FF900DB9EBF7BF30ED4DCBB" +
+ "BC8D5B51C965EA226FFEE2CA5AB2EFD00754DC32F357BF7A" +
+ "E42275E0F7704DC44E50A5220AD05AB698A22640AC634829",
+ "B907E77144FD55A54E9BA1A6A0EED0AAC780020C41A15DD8" +
+ "9A6C163830BA1D094E6A17100FF71EE30A96E1EE04D2A966" +
+ "03832A4E404F1966C2B5F4CB61B9927E8D12AC1E1A24CF23" +
+ "88C14E8EC96C35181EAEE32AAA46330DEAAFE5E7CE783C74"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]{
+ "804A3AD720F4FCE8738D0632514FEF16430CB7D63A8DF1A5" +
+ "F02A3CE3BD7ED6A668B69E63E2BB93F096EE753D6194A0F1" +
+ "A32711063653009636337D22167CC4402D019AC216FA574F" +
+ "091CF6EA283568D737A77BE38E8F09382C69E76B142ABC3A",
+ "73B8E55C753202176A17B9B9754A9FE6F23B01861FCD4059" +
+ "6AEAA301AF1AEF8AF0EAF22FBF34541EFFAB1431666ACACC" +
+ "759338C7E28672819D53CFEF10A3E19DAFBD53295F1980A9" +
+ "F491504A2725506784B7AC826D92C838A8668171CAAA86E7"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "2A5FF6520C20F66E" +
+ "D5EA431BD4AEAC58F975EEC9A015137D5C94B73AA09CB8B5" +
+ "9D611DDEECEB34A52BB999424009EB9EAC5353F92A6699D2" +
+ "0A02164EEBBC6492941E10426323898465DFD731C7E04730" +
+ "60A5AA8973841FDF3446FB6E72A58DA8BDA2A57A36F3DD98" +
+ "6DF85C8A5C6FF31CDE660BF8A841B21DD6AA9D3AC356B87B",
+ "0EDC8D7D7CEEC7FE" +
+ "36333FB30C0A9A4B27AA0BECBF075568B006C1C3693B1C29" +
+ "0F84769C213F98EB5880909EDF068FDA6BFC43503987BBBD" +
+ "4FC23AFBE982FE4B4B007910CC4874EEC217405421C8D8A1" +
+ "BA87EC684D0AF9A6101D9DB787AE82C3A6A25ED478DF1B12" +
+ "212CEC325466F3AC7C48A56166DD0B119C8673A1A9D54F67"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "AAE4DC3C9ECC74D9" +
+ "061DD527117EF3D29E1E52B26853C539D6CA797E8DA3D0BB" +
+ "171D8E30B8B194D8C28F7F6BE3B986B88506DC6A01B294A7" +
+ "165DD1C3470F7BE7B396AA0DB7D50C4051E7C7E1C8A7D21A" +
+ "2B5878C0BCB163CAA79366E7A1162FDC88429616CD3E6977" +
+ "8D327520A6BBBF71D8AA2E03EC4A9DAA0E77CF93E1EE30D2 ",
+ "129FF6D31A23FFBC" +
+ "870632B35EE477C2280DDD2ECDABEDB900C78418BE2D243B" +
+ "B9D8E5093ECE7B6BF48638D8F704D134ADDEB7F4E9D5C142" +
+ "CD05683E72B516486AF24AEC15D61E81E270DD4EBED91B62" +
+ "12EB8896A6250D5C8BC3A4A12F7E3068FBDF856F47EB23D3" +
+ "79F82C1EBCD1585FB260B9C0C42625FBCEE68CAD773CD5B1"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "7AE31A2DEC31075F" +
+ "E5972660C16D22ECC0D415C5693001BE5A468B590BC1AE2C" +
+ "43F647F8D681AEEA0D87B79B0B4E5D089CA2C9D327534234" +
+ "0254E6B04690D77A71A294DA9568479EEF8BB2A2110F18B6" +
+ "22F60F35235DE0E8F9D7E98105D84AA24AF0757AF005DFD5" +
+ "2FA51DE3F44FCE0C5F3A27FCE8B0F6E4A3F7C7B53CE34A3D",
+ "D83A8084630F286D" +
+ "A4DB49B9F6F608C8993F7F1397EA0D6F4A72CF3EF2733A11" +
+ "AB823C29F2EBDEC3EDE962F93D920A1DB59C84E1E879C29F" +
+ "5F9995FC3A6A3AF9B587CA7C13EA197D423E81E1D6469942" +
+ "B6E2CA83A97E91F6B298266AC148A1809776C26AF5E239A5" +
+ "5A2BEB9E752203A694E1F3FE2B3E6A0C9C314421CDB55FBD "})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "28FD6060C4F35F4D" +
+ "317AB2060EE32019E0DAA330F3F5650BBCA57CB67EE6AF1C" +
+ "6F25D1B01F3601EDA85DC2ED29A9B2BA4C85CF491CE7185F" +
+ "1A2BD9378AE3C655BD1CEC2EE108AE7FC382989F6D4FEA8A" +
+ "B01499697C2F07945CE02C5ED617D04287FEAF3BA638A4CE" +
+ "F3BB6B827E40AF16279580FCF1FDAD830930F7FDE341E2AF",
+ "C0B1601AFE39338B" +
+ "58DC2BE7C256AEBE3C21C5A939BEEC7E97B3528AC420F0C6" +
+ "341847187666E0FF578A8EB0A37809F877365A28DF2FA0F0" +
+ "6354A6F02496747369375B9A9D6B756FDC4A8FB308E08256" +
+ "9D79A85BB960F747256626389A3B45B0ABE7ECBC39D5CD7B" +
+ "2C18DF2E5FDE8C9B8D43474C54B6F9839468445929B438C7"}),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "72691D2103FB567C" +
+ "CD30370715B36666F63430087B1C688281CA0974DB456BDB" +
+ "A7EB5C48CFF62EA05F9508F3B530CE995A272B11EC079C13" +
+ "923EEF8E011A93C19B58CC6716BC7CB8BD886CAA60C14D85" +
+ "C023348BD77738C475D6C7E1D9BFF4B12C43D8CC73F838DC" +
+ "4F8BD476CF8328EEB71B3D873D6B7B859C9B21065638FF95",
+ "8570DA3D47E1E160" +
+ "5CF3E44B8D328B995EFC64107B6292D1B1036B5F88CE3160" +
+ "2F12BEB71D801C0942E7C0864B3DB67A9356DB203490D881" +
+ "24FE86BCE38AC2269B4FDA6ABAA884039DF80A0336A24D79" +
+ "1EB3067C8F5F0CF0F18DD73B66A7B316FB19E02835CC6293" +
+ "65FCD1D3BE640178ED9093B91B36E1D68135F2785BFF505C"})
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "AAE4DC3C9ECC74D9" +
+ "061DD527117EF3D29E1E52B26853C539D6CA797E8DA3D0BB" +
+ "171D8E30B8B194D8C28F7F6BE3B986B88506DC6A01B294A7" +
+ "165DD1C3470F7BE7B396AA0DB7D50C4051E7C7E1C8A7D21A" +
+ "2B5878C0BCB163CAA79366E7A1162FDC88429616CD3E6977" +
+ "8D327520A6BBBF71D8AA2E03EC4A9DAA0E77CF93E1EE30D2 ",
+ "129FF6D31A23FFBC" +
+ "870632B35EE477C2280DDD2ECDABEDB900C78418BE2D243B" +
+ "B9D8E5093ECE7B6BF48638D8F704D134ADDEB7F4E9D5C142" +
+ "CD05683E72B516486AF24AEC15D61E81E270DD4EBED91B62" +
+ "12EB8896A6250D5C8BC3A4A12F7E3068FBDF856F47EB23D3" +
+ "79F82C1EBCD1585FB260B9C0C42625FBCEE68CAD773CD5B1"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "B8E827652175E6E0" +
+ "6E513C7BE94B5810C14ED94AD903647940CAEB7EE014C848" +
+ "8DCBBE6D4D6616D06656A3DC707CDAC4F02EE6D8408C065F" +
+ "CB068C0760DA47C5D60E5D70D09DC3929B6979615D117F7B" +
+ "EDCC661A98514B3A1F55B2CBABDCA59F11823E4838065F1F" +
+ "8431CBF28A577738234AF3F188C7190CC19739E72E9BBFFF",
+ "7ED41B9CFDC8C256" +
+ "83BBB4C553CC2DC61F690E62ABC9F038A16B8C519690CABE" +
+ "BD1B5C196C57CF759BB9871BE0C163A57315EA96F615136D" +
+ "064572F09F26D659D24211F9610FFCDFFDA8CE23FFA96735" +
+ "7595182660877766035EED800B05364CE324A75EB63FD9B3" +
+ "EED956D147480B1D0A42DF8AA990BB628666F6F61D60CBE2"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E")
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new HMacSP800DRBG(new HMac(tv.getDigest()), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // Exception tests
+ //
+ SP80090DRBG d;
+ try
+ {
+ d = new HMacSP800DRBG(new HMac(new SHA256Digest()), 256, new SHA256EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Not enough entropy for security strength required"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class SHA1EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA1EntropyProvider()
+ {
+ super(
+ Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536"
+ + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6"
+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "00010203040506" +
+ "0708090A0B0C0D0E0F101112131415161718191A1B1C1D1E" +
+ "1F202122232425262728292A2B2C2D2E2F30313233343536" +
+ "80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "C0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526"
+ + "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556"
+ + "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+
+ private class SHA512EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA512EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E" +
+ "0F101112131415161718191A1B1C1D1E1F20212223242526" +
+ "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E" +
+ "3F404142434445464748494A4B4C4D4E4F50515253545556" +
+ "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/HashDRBGTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/HashDRBGTest.java
new file mode 100644
index 000000000..294ae72c9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/HashDRBGTest.java
@@ -0,0 +1,481 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.prng.drbg.HashSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * DRBG Test
+ */
+public class HashDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "HashDRBG";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HashDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "9F7CFF1ECA23E750F66326969F11800F12088BA68E441D15D888B3FE12BF66FE057494F4546DE2F1",
+ "B77AA5C0CD55BBCEED7574AF223AFD988C7EEC8EFF4A94E5E89D26A04F58FA79F5E0D3702D7A9A6A"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "AB438BD3B01A0AF85CFEE29F7D7B71621C4908B909124D430E7B406FB1086EA994C582E0D656D989",
+ "29D9098F987E7005314A0F51B3DD2B8122F4AED706735DE6AD5DDBF223177C1E5F3AEBC52FAB90B9"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "E76B4EDD5C865BC8AFD809A59B69B429AC7F4352A579BCF3F75E56249A3491F87C3CA6848B0FAB25",
+ "6577B6B4F87A93240B199FE51A3B335313683103DECE171E3256FB7E803586CA4E45DD242EB01F70"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "56EF4913373994D5539F4D7D17AFE7448CDF5E72416CC6A71A340059FA0D5AE526B23250C46C0944",
+ "575B37A2739814F966C63B60A2C4F149CA9ACC84FC4B25493289B085C67B2E30F5F0B99A2C349E2A"
+ }),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "532CA1165DCFF21C55592687639884AF4BC4B057DF8F41DE653AB44E2ADEC7C9303E75ABE277EDBF",
+ "73C2C67C696D686D0C4DBCEB5C2AF7DDF6F020B6874FAE4390F102117ECAAFF54418529A367005A0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "183C242A1430E46C4ED70B4DBE1BF9AB0AB8721CDCA2A2D1820AD6F6C956858543B2AA191D8D1287",
+ "F196F9BD021C745CBD5AC7BFCE48EAAF0D0E7C091FBF436940E63A198EE770D9A4F0718669AF2BC9"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "77E05A0E7DC78AB5D8934D5E93E82C06" +
+ "A07C04CEE6C9C53045EEB485872777CF3B3E35C474F976B8" +
+ "94BF301A86FA651F463970E89D4A0534B2ECAD29EC044E7E",
+ "5FF4BA493C40CFFF3B01E472C575668C" +
+ "CE3880B9290B05BFEDE5EC96ED5E9B2898508B09BC800EEE" +
+ "099A3C90602ABD4B1D4F343D497C6055C87BB956D53BF351"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "92275523C70E567BCF9B35EC50B933F8" +
+ "12616DF586B7F72EE1BC7735A5C2654373CBBC72316DFF84" +
+ "20A33BF02B97AC8D1952583F270ACD7005CC027F4CF1187E",
+ "681A46B2AA8694A0FE4DEEA720927A84" +
+ "EAAA985E59C19F8BE0984D8CBEF8C69B754167641946E040" +
+ "EE2043E1CCB29DCF063C0A50830E428E6DCA262ECD77C542"
+ }),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "04FF23AD15E78790ADD36B438BBC097C7A11747CC2CCEEDE" +
+ "2C978B23B3DC63B732C953061D7764990ABFEFC47A581B92" +
+ "1BC0428C4F12212460E406A0F0651E7F0CB9A90ABFDB07B5" +
+ "25565C74F0AA085082F6CF213AAFAD0C0646895078F1E1FE",
+ "4F35B85F95DEE3E873054905CFD02341653E18F529930CBE" +
+ "14D909F37FEAF2C790D22FAE7516B4590BE35D53E2FE1A35" +
+ "AFE4B6607CB358589C3B4D094A1D81FE0717F1DF5BDDEB3E" +
+ "114F130BB781E66C22B5B770E8AE115FF39F8ADAF66DEEDF"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "97993B78F7C31C0E876DC92EB7D6C408E09D608AD6B99D0E" +
+ "A2229B05A578C426334FCC8A1C7E676ED2D89A5B4CDF5B3F" +
+ "4ADF11936BF14F4E10909DBA9C24F4FDFFDE72351DA8E2CC" +
+ "3B135A395373899E5F1A5955B880CA9B9E9DD4C9CA7FA4D4",
+ "F5983946320E36C64EF283CA1F65D197CF81624EC6778E77" +
+ "0E78949D84EF21A45CDD62D1DB76920D4C2836FC6AE5299F" +
+ "AF1357D9701FAD10FBD88D1E2832239436D76EB271BDC3CA" +
+ "04425EC88BC0E89A4D5C37FFCE7C6C3ABDE9C413AE6D3FEA"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "DA126CF95C6BF97E" +
+ "2F731F2137A907ACC70FD7AC9EBACD1C6E31C74029B052E3" +
+ "AABC48F3B00993F2B2381F7650A55322A968C86E05DE88E6" +
+ "367F6EF89A601DB4342E9086C7AC13B5E56C32E9E668040B" +
+ "73847893C5BFD38A1CF44F348B4EEE4CD68ADB7E7B8C837F" +
+ "19BC4F902761F7CFF24AB1D704FD11C4E929D8553753B55D",
+ "400B977CE8A2BB6A" +
+ "84C6FD1CF901459685ABF5408CFF4588CEDF52E2D2DC300A" +
+ "A9B4FAED8CD0161C2172B1FD269253195883D6EBF21020F2" +
+ "C20E5F2C81AE60C8595B834A229B1F5B726C1125717E6207" +
+ "8886EF38E61E32707AD5F8116C6393DFB6E7C7AE0E8E92BB" +
+ "D7E0C3D04BBA02F5169F2F569A58158915FEE4C9D28D45DB"
+ }
+ )
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "F93CA6855590A77F" +
+ "07354097E90E026648B6115DF008FFEDBD9D9811F54E8286" +
+ "EF00FDD6BA1E58DF2535E3FBDD9A9BA3754A97F36EE83322" +
+ "1582060A1F37FCE4EE8826636B28EAD589593F4CA8B64738" +
+ "8F24EB3F0A34796968D21BDEE6F81FD5DF93536F935937B8" +
+ "025EC8CBF57DDB0C61F2E41463CC1516D657DA2829C6BF90",
+ "4817618F48C60FB1" +
+ "CE5BFBDA0CAF4591882A31F6EE3FE0F78779992A06EC60F3" +
+ "7FB9A8D6108C231F0A927754B0599FA4FA27A4E25E065EF0" +
+ "3085B892979DC0E7A1080883CAEBFDFD3665A8F2D061C521" +
+ "F7D6E3DA2AF8B97B6B43B6EC831AF515070A83BBB9AC95ED" +
+ "4EF49B756A2377A5F0833D847E27A88DDB0C2CE4AD782E7B "
+ }
+ ),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "0455DD4AD7DBACB2" +
+ "410BE58DF7248D765A4547ABAEE1743B0BCAD37EBD06DA7C" +
+ "F7CE5E2216E525327E9E2005EBEF2CE53BD733B18128627D" +
+ "3FD6153089373AF2606A1584646A0EA488BFEF45228699A0" +
+ "89CEA8AEC44502D86D9591F3552C688B7F7B45FCB0C3C2B9" +
+ "43C1CD8A6FC63DF4D81C3DA543C9CF2843855EA84E4F959C",
+ "C047D46D7F614E4E" +
+ "4A7952C79A451F8F7ACA379967E2977C401C626A2ED70D74" +
+ "A63660579A354115BC8C8C8CC3AEA3050686A0CFCDB6FA9C" +
+ "F78D4C2165BAF851C6F9B1CD16A2E14C15C6DAAC56C16E75" +
+ "FC84A14D58B41622E88B0F1B1995587FD8BAA999CBA98025" +
+ "4C8AB9A9691DF7B84D88B639A9A3106DEABEB63748B99C09"
+ }
+ )
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "22EB93A67911DA73" +
+ "85D9180C78127DE1A04FF713114C07C9C615F7CC5EF72744" +
+ "A2DDCD7C3CB85E65DED8EF5F240FBDCBEBBDE2BAAC8ECF7D" +
+ "CBC8AC333E54607AD41DC495D83DF72A05EF55B127C1441C" +
+ "9A0EFFDA2C7954DB6C2D04342EB812E5E0B11D6C395F41ED" +
+ "A2702ECE5BA479E2DFA18F953097492636C12FE30CE5C968",
+ "E66698CFBF1B3F2E" +
+ "919C03036E584EAA81CF1C6666240AF05F70637043733954" +
+ "D8A1E5A66A04C53C6900FDC145D4A3A80A31F5868ACE9AC9" +
+ "4E14E2051F624A05EEA1F8B684AA5410BCE315E76EA07C71" +
+ "5D6F34731320FF0DCF78D795E6EFA2DF92B98BE636CDFBA2" +
+ "9008DD392112AEC202F2E481CB9D83F987FEA69CD1B368BB"
+ }
+ )
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "7596A76372308BD5" +
+ "A5613439934678B35521A94D81ABFE63A21ACF61ABB88B61" +
+ "E86A12C37F308F2BBBE32BE4B38D03AE808386494D70EF52" +
+ "E9E1365DD18B7784CAB826F31D47579E4D57F69D8BF3152B" +
+ "95741946CEBE58571DF58ED39980D9AF44E69F01E8989759" +
+ "8E40171101A0E3302838E0AD9E849C01988993CF9F6E5263",
+ "DBE5EE36FCD85301" +
+ "303E1C3617C1AC5E23C08885D0BEFAAD0C85A0D89F85B9F1" +
+ "6ECE3D88A24EB96504F2F13EFA7049621782F5DE2C416A0D" +
+ "294CCFE53545C4E309C48E1E285A2B829A574B72B3C2FBE1" +
+ "34D01E3706B486F2401B9820E17298A342666918E15B8462" +
+ "87F8C5AF2D96B20FAF3D0BB392E15F4A06CDB0DECD1B6AD7"
+ }
+ )
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E")
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new HashSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // Exception tests
+ //
+ SP80090DRBG d;
+ try
+ {
+ d = new HashSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Not enough entropy for security strength required"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new HashSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by the derivation function"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class SHA1EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA1EntropyProvider()
+ {
+ super(
+ Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536"
+ + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6"
+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "00010203040506" +
+ "0708090A0B0C0D0E0F101112131415161718191A1B1C1D1E" +
+ "1F202122232425262728292A2B2C2D2E2F30313233343536" +
+ "80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "C0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526"
+ + "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556"
+ + "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+
+ private class SHA512EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA512EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E" +
+ "0F101112131415161718191A1B1C1D1E1F20212223242526" +
+ "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E" +
+ "3F404142434445464748494A4B4C4D4E4F50515253545556" +
+ "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/RegressionTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/RegressionTest.java
new file mode 100644
index 000000000..4c16e89de
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/RegressionTest.java
@@ -0,0 +1,33 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class RegressionTest
+{
+ public static Test[] tests = {
+ new CTRDRBGTest(),
+ new DualECDRBGTest(),
+ new HashDRBGTest(),
+ new HMacDRBGTest(),
+ new SP800RandomTest(),
+ new FixedSecureRandomTest()
+ };
+
+ public static void main(
+ String[] args)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace();
+ }
+
+ System.out.println(result);
+ }
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/SP800RandomTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/SP800RandomTest.java
new file mode 100644
index 000000000..ce37d5b65
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/SP800RandomTest.java
@@ -0,0 +1,319 @@
+package org.spongycastle.crypto.prng.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.prng.SP800SecureRandomBuilder;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SP800RandomTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "SP800RandomTest";
+ }
+
+ private void testHashRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "532CA1165DCFF21C55592687639884AF4BC4B057DF8F41DE653AB44E2ADEC7C9303E75ABE277EDBF",
+ "73C2C67C696D686D0C4DBCEB5C2AF7DDF6F020B6874FAE4390F102117ECAAFF54418529A367005A0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576");
+
+ doHashTest(0, tv);
+
+ tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "AB438BD3B01A0AF85CFEE29F7D7B71621C4908B909124D430E7B406FB1086EA994C582E0D656D989",
+ "29D9098F987E7005314A0F51B3DD2B8122F4AED706735DE6AD5DDBF223177C1E5F3AEBC52FAB90B9"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576");
+
+ doHashTest(1, tv);
+ }
+
+ private void doHashTest(int index, DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new SHA1EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.entropySource().getEntropy().length * 8);
+
+ SecureRandom random = rBuild.buildHash(tv.getDigest(), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Hash SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Hash SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ private void testHMACRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "6C37FDD729AA40F80BC6AB08CA7CC649794F6998B57081E4220F22C5C283E2C91B8E305AB869C625",
+ "CAF57DCFEA393B9236BF691FA456FEA7FDF1DF8361482CA54D5FA723F4C88B4FA504BF03277FA783"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576");
+
+ doHMACTest(tv);
+
+ tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "5A7D3B449F481CB38DF79AD2B1FCC01E57F8135E8C0B22CD0630BFB0127FB5408C8EFC17A929896E",
+ "82cf772ec3e84b00fc74f5df104efbfb2428554e9ce367d03aeade37827fa8e9cb6a08196115d948"
+ });
+
+ doHMACTest(tv);
+ }
+
+ private void doHMACTest(DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new SHA1EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.entropySource().getEntropy().length * 8);
+
+ SecureRandom random = rBuild.buildHMAC(new HMac(tv.getDigest()), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 HMAC SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 HMAC SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ private void testDualECRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "3AB095CC493A8730D70DE923108B2E4710799044FFC27D0A1156250DDF97E8B05ACE055E49F3E3F5B928CCD18317A3E68FCB0B6F0459ADF9ECF79C87",
+ "7B902FC35B0AF50F57F8822936D08A96E41B16967C6B1AA0BC05032F0D53919DC587B664C883E2FE8F3948002FCD8BCBFC4706BCAA2075EF6BF41167"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F");
+
+ doDualECTest(1, tv);
+
+ tv = new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "8C77288EDBEA9A742464F78D55E33593C1BF5F9D8CD8609D6D53BAC4E4B42252A227A99BAD0F2358B05955CD35723B549401C71C9C1F32F8A2018E24",
+ "56ECA61C64F69C1C232E992623C71418BD0B96D783118FAAD94A09E3A9DB74D15E805BA7F14625995CA77612B2EF7A05863699ECBABF70D3D422C014"
+ });
+
+ doDualECTest(2, tv);
+ }
+
+ private void doDualECTest(int index, DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new SHA256EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.securityStrength());
+
+ SecureRandom random = rBuild.buildDualEC(tv.getDigest(), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Dual EC SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Dual EC SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ private void testCTRRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "ABC88224514D0316EA3D48AEE3C9A2B4",
+ "D3D3F372E43E7ABDC4FA293743EED076"
+ }
+ );
+
+ doCTRTest(tv);
+
+ tv = new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "64983055D014550B39DE699E43130B64",
+ "035FDDA8582A2214EC722C410A8D95D3"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C");
+
+ doCTRTest(tv);
+ }
+
+ private void doCTRTest(DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new Bit232EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.entropySource().getEntropy().length * 8);
+
+ SecureRandom random = rBuild.buildCTR(tv.getCipher(), tv.keySizeInBits(), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 CTR SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 CTR SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testHashRandom();
+ testHMACRandom();
+ testCTRRandom();
+ testDualECRandom();
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SP800RandomTest());
+ }
+
+ // for HMAC/Hash
+ private class SHA1EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA1EntropyProvider()
+ {
+ super(
+ Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536"
+ + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6"
+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ // for Dual EC
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F " +
+ "808182838485868788898A8B8C8D8E8F" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"), true);
+ }
+ }
+
+ private class Bit232EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit232EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C" +
+ "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC"), true);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/TestEntropySourceProvider.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/TestEntropySourceProvider.java
new file mode 100644
index 000000000..d630f4c97
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/prng/test/TestEntropySourceProvider.java
@@ -0,0 +1,46 @@
+package org.spongycastle.crypto.prng.test;
+
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.crypto.prng.EntropySourceProvider;
+
+public class TestEntropySourceProvider
+ implements EntropySourceProvider
+{
+ private final byte[] data;
+ private final boolean isPredictionResistant;
+
+ protected TestEntropySourceProvider(byte[] data, boolean isPredictionResistant)
+ {
+ this.data = data;
+ this.isPredictionResistant = isPredictionResistant;
+ }
+
+ public EntropySource get(final int bitsRequired)
+ {
+ return new EntropySource()
+ {
+ int index = 0;
+
+ public boolean isPredictionResistant()
+ {
+ return isPredictionResistant;
+ }
+
+ public byte[] getEntropy()
+ {
+ byte[] rv = new byte[bitsRequired / 8];
+
+ System.arraycopy(data, index, rv, 0, rv.length);
+
+ index += bitsRequired / 8;
+
+ return rv;
+ }
+
+ public int entropySize()
+ {
+ return bitsRequired;
+ }
+ };
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AEADTestUtil.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AEADTestUtil.java
new file mode 100644
index 000000000..2ef521434
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AEADTestUtil.java
@@ -0,0 +1,192 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestFailedException;
+
+public class AEADTestUtil
+{
+
+ public static void testTampering(Test test, AEADBlockCipher cipher, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ byte[] plaintext = new byte[1000];
+ for (int i = 0; i < plaintext.length; i++)
+ {
+ plaintext[i] = (byte)i;
+ }
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
+ int len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
+ cipher.doFinal(ciphertext, len);
+
+ int macLength = cipher.getMac().length;
+
+ // Test tampering with a single byte
+ cipher.init(false, params);
+ byte[] tampered = new byte[ciphertext.length];
+ byte[] output = new byte[plaintext.length];
+ System.arraycopy(ciphertext, 0, tampered, 0, tampered.length);
+ tampered[0] += 1;
+
+ cipher.processBytes(tampered, 0, tampered.length, output, 0);
+ try
+ {
+ cipher.doFinal(output, 0);
+ throw new TestFailedException(
+ new SimpleTestResult(false, test + " : tampering of ciphertext not detected."));
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Expected
+ }
+
+ // Test truncation of ciphertext to < tag length
+ cipher.init(false, params);
+ byte[] truncated = new byte[macLength - 1];
+ System.arraycopy(ciphertext, 0, truncated, 0, truncated.length);
+
+ cipher.processBytes(truncated, 0, truncated.length, output, 0);
+ try
+ {
+ cipher.doFinal(output, 0);
+ fail(test, "tampering of ciphertext not detected.");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Expected
+ }
+ }
+
+ private static void fail(Test test, String message)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(test, message));
+ }
+
+ private static void fail(Test test, String message, String expected, String result)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(test, message, expected, result));
+ }
+
+ public static void testReset(Test test, AEADBlockCipher cipher1, AEADBlockCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ cipher1.init(true, params);
+
+ byte[] plaintext = new byte[1000];
+ byte[] ciphertext = new byte[cipher1.getOutputSize(plaintext.length)];
+
+ // Establish baseline answer
+ crypt(cipher1, plaintext, ciphertext);
+
+ // Test encryption resets
+ checkReset(test, cipher1, params, true, plaintext, ciphertext);
+
+ // Test decryption resets with fresh instance
+ cipher2.init(false, params);
+ checkReset(test, cipher2, params, false, ciphertext, plaintext);
+ }
+
+ private static void checkReset(Test test,
+ AEADBlockCipher cipher,
+ CipherParameters params,
+ boolean encrypt,
+ byte[] pretext,
+ byte[] posttext)
+ throws InvalidCipherTextException
+ {
+ // Do initial run
+ byte[] output = new byte[posttext.length];
+ crypt(cipher, pretext, output);
+
+ // Check encrypt resets cipher
+ crypt(cipher, pretext, output);
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, (encrypt ? "Encrypt" : "Decrypt") + " did not reset cipher.");
+ }
+
+ // Check init resets data
+ cipher.processBytes(pretext, 0, 100, output, 0);
+ cipher.init(encrypt, params);
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Init did not reset data.", new String(Hex.encode(posttext)), new String(Hex.encode(output)));
+ }
+
+ // Check init resets AD
+ cipher.processAADBytes(pretext, 0, 100);
+ cipher.init(encrypt, params);
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset additional data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Init did not reset additional data.");
+ }
+
+ // Check reset resets data
+ cipher.processBytes(pretext, 0, 100, output, 0);
+ cipher.reset();
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Reset did not reset data.");
+ }
+
+ // Check reset resets AD
+ cipher.processAADBytes(pretext, 0, 100);
+ cipher.reset();
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Reset did not reset additional data.");
+ }
+ }
+
+ private static void crypt(AEADBlockCipher cipher, byte[] plaintext, byte[] output)
+ throws InvalidCipherTextException
+ {
+ int len = cipher.processBytes(plaintext, 0, plaintext.length, output, 0);
+ cipher.doFinal(output, len);
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESFastTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESFastTest.java
new file mode 100644
index 000000000..6892ed77f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESFastTest.java
@@ -0,0 +1,150 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ *
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ */
+public class AESFastTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new AESFastEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new AESFastEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new AESFastEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new AESFastEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(9, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(10, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(11, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(12, new AESFastEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(13, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(14, new AESFastEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(15, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(16, new AESFastEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(17, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(18, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(19, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(20, new AESFastEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(21, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(22, new AESFastEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(23, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168")
+ };
+
+ private BlockCipher _engine = new AESFastEngine();
+
+ AESFastTest()
+ {
+ super(tests, new AESFastEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "AESFast";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] keyBytes = new byte[16];
+
+ _engine.init(true, new KeyParameter(keyBytes));
+
+ //
+ // init tests
+ //
+ try
+ {
+ byte[] dudKey = new byte[6];
+
+ _engine.init(true, new KeyParameter(dudKey));
+
+ fail("failed key length check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ byte[] iv = new byte[16];
+
+ _engine.init(true, new ParametersWithIV(null, iv));
+
+ fail("failed parameter check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESFastTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESLightTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESLightTest.java
new file mode 100644
index 000000000..a3c4ca533
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESLightTest.java
@@ -0,0 +1,150 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.engines.AESLightEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ *
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ */
+public class AESLightTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new AESLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new AESLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new AESLightEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new AESLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(9, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(10, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(11, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(12, new AESLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(13, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(14, new AESLightEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(15, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(16, new AESLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(17, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(18, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(19, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(20, new AESLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(21, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(22, new AESLightEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(23, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168")
+ };
+
+ private BlockCipher _engine = new AESLightEngine();
+
+ AESLightTest()
+ {
+ super(tests, new AESLightEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "AESLight";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] keyBytes = new byte[16];
+
+ _engine.init(true, new KeyParameter(keyBytes));
+
+ //
+ // init tests
+ //
+ try
+ {
+ byte[] dudKey = new byte[6];
+
+ _engine.init(true, new KeyParameter(dudKey));
+
+ fail("failed key length check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ byte[] iv = new byte[16];
+
+ _engine.init(true, new ParametersWithIV(null, iv));
+
+ fail("failed parameter check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESLightTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESTest.java
new file mode 100644
index 000000000..2b71e0d70
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESTest.java
@@ -0,0 +1,295 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.OFBBlockCipher;
+import org.spongycastle.crypto.modes.SICBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ *
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ */
+public class AESTest
+ extends CipherTest
+{
+ private static final byte[] tData = Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114F3F6752AE8D7831138F041560631B1145A01020304050607");
+ private static final byte[] outCBC1 = Hex.decode("a444a9a4d46eb30cb7ed34d62873a89f8fdf2bf8a54e1aeadd06fd85c9cb46f021ee7cd4f418fa0bb72e9d07c70d5d20");
+ private static final byte[] outCBC2 = Hex.decode("585681354f0e01a86b32f94ebb6a675045d923cf201263c2aaecca2b4de82da0edd74ca5efd654c688f8a58e61955b11");
+ private static final byte[] outSIC1 = Hex.decode("82a1744e8ebbd053ca72362d5e570326e0b6fdaf824ab673fbf029042886b23c75129a015852913790f81f94447475a0");
+ private static final byte[] outSIC2 = Hex.decode("146cbb581d9e12c3333dd9c736fbb93043c92019f78580da48f81f80b3f551d58ea836fed480fc6912fefa9c5c89cc24");
+ private static final byte[] outCFB1 = Hex.decode("82a1744e8ebbd053ca72362d5e5703264b4182de3208c374b8ac4fa36af9c5e5f4f87d1e3b67963d06acf5eb13914c90");
+ private static final byte[] outCFB2 = Hex.decode("146cbb581d9e12c3333dd9c736fbb9303c8a3eb5185e2809e9d3c28e25cc2d2b6f5c11ee28d6530f72c412b1438a816a");
+ private static final byte[] outOFB1 = Hex.decode("82a1744e8ebbd053ca72362d5e5703261ebf1fdbec05e57b3465b583132f84b43bf95b2c89040ad1677b22d42db69a7a");
+ private static final byte[] outOFB2 = Hex.decode("146cbb581d9e12c3333dd9c736fbb9309ea4c2a7696c84959a2dada49f2f1c5905db1f0cec3a31acbc4701e74ab05e1f");
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new AESEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new AESEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new AESEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new AESEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(9, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(10, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(11, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(12, new AESEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(13, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(14, new AESEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(15, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(16, new AESEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(17, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(18, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(19, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(20, new AESEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(21, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(22, new AESEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(23, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168")
+ };
+
+ private BlockCipher _engine = new AESEngine();
+
+ public AESTest()
+ {
+ super(tests, new AESEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "AES";
+ }
+
+ private void testNullSIC()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new SICBlockCipher(new AESEngine()));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outSIC1, out))
+ {
+ fail("no match on first nullSIC check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outSIC2, out))
+ {
+ fail("no match on second nullSIC check");
+ }
+ }
+
+ private void testNullCBC()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCBC1, out))
+ {
+ fail("no match on first nullCBC check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCBC2, out))
+ {
+ fail("no match on second nullCBC check");
+ }
+ }
+
+ private void testNullOFB()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new OFBBlockCipher(new AESEngine(), 128));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outOFB1, out))
+ {
+ fail("no match on first nullOFB check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outOFB2, out))
+ {
+ fail("no match on second nullOFB check");
+ }
+ }
+
+ private void testNullCFB()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new CFBBlockCipher(new AESEngine(), 128));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCFB1, out))
+ {
+ fail("no match on first nullCFB check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCFB2, out))
+ {
+ fail("no match on second nullCFB check");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] keyBytes = new byte[16];
+
+ _engine.init(true, new KeyParameter(keyBytes));
+
+ //
+ // init tests
+ //
+ try
+ {
+ byte[] dudKey = new byte[6];
+
+ _engine.init(true, new KeyParameter(dudKey));
+
+ fail("failed key length check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ byte[] iv = new byte[16];
+
+ _engine.init(true, new ParametersWithIV(null, iv));
+
+ fail("failed parameter check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ testNullCBC();
+ testNullSIC();
+ testNullOFB();
+ testNullCFB();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESVectorFileTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESVectorFileTest.java
new file mode 100644
index 000000000..504b4bd40
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESVectorFileTest.java
@@ -0,0 +1,258 @@
+package org.spongycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.AESLightEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ *
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ */
+public class AESVectorFileTest
+ implements Test
+{
+
+ private int countOfTests = 0;
+ private int testNum = 0;
+
+ protected BlockCipher createNewEngineForTest()
+ {
+ return new AESEngine();
+ }
+
+ private Test[] readTestVectors(InputStream inStream)
+ {
+ // initialize key, plaintext, ciphertext = null
+ // read until find BLOCKSIZE=
+ // return if not 128
+ // read KEYSIZE= or ignore
+ // loop
+ // read a line
+ // if starts with BLOCKSIZE=
+ // parse the rest. return if not 128
+ // if starts with KEY=
+ // parse the rest and set KEY
+ // if starts with PT=
+ // parse the rest and set plaintext
+ // if starts with CT=
+ // parse the rest and set ciphertext
+ // if starts with TEST= or end of file
+ // if key, plaintext, ciphertext are all not null
+ // save away their values as the next test
+ // until end of file
+ List tests = new ArrayList();
+ String key = null;
+ String plaintext = null;
+ String ciphertext = null;
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
+
+ try
+ {
+ String line = in.readLine();
+
+ while (line != null)
+ {
+ line = line.trim().toLowerCase();
+ if (line.startsWith("blocksize="))
+ {
+ int i = 0;
+ try
+ {
+ i = Integer.parseInt(line.substring(10).trim());
+ }
+ catch (Exception e)
+ {
+ }
+ if (i != 128)
+ {
+ return null;
+ }
+ }
+ else if (line.startsWith("keysize="))
+ {
+ int i = 0;
+ try
+ {
+ i = Integer.parseInt(line.substring(10).trim());
+ }
+ catch (Exception e)
+ {
+ }
+ if ((i != 128) && (i != 192) && (i != 256))
+ {
+ return null;
+ }
+ }
+ else if (line.startsWith("key="))
+ {
+ key = line.substring(4).trim();
+ }
+ else if (line.startsWith("pt="))
+ {
+ plaintext = line.substring(3).trim();
+ }
+ else if (line.startsWith("ct="))
+ {
+ ciphertext = line.substring(3).trim();
+ }
+ else if (line.startsWith("test="))
+ {
+ if ((key != null) && (plaintext != null)
+ && (ciphertext != null))
+ {
+ tests.add(new BlockCipherVectorTest(testNum++,
+ createNewEngineForTest(), new KeyParameter(Hex
+ .decode(key)), plaintext, ciphertext));
+ }
+ }
+
+ line = in.readLine();
+ }
+ try
+ {
+ in.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ if ((key != null) && (plaintext != null) && (ciphertext != null))
+ {
+ tests.add(new BlockCipherVectorTest(testNum++,
+ createNewEngineForTest(),
+ new KeyParameter(Hex.decode(key)), plaintext, ciphertext));
+ }
+ return (Test[])(tests.toArray(new Test[tests.size()]));
+ }
+
+ public String getName()
+ {
+ return "AES";
+ }
+
+ private TestResult performTestsFromZipFile(File zfile)
+ {
+ try
+ {
+ ZipFile inZip = new ZipFile(zfile);
+ for (Enumeration files = inZip.entries(); files.hasMoreElements();)
+ {
+ Test[] tests = null;
+ try
+ {
+ tests = readTestVectors(inZip
+ .getInputStream((ZipEntry)(files.nextElement())));
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": threw "
+ + e);
+ }
+ if (tests != null)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult res = tests[i].perform();
+ countOfTests++;
+
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+ }
+ }
+ }
+ inZip.close();
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": threw " + e);
+ }
+ }
+
+ private static final String[] zipFileNames = { "rijn.tv.ecbnk.zip",
+ "rijn.tv.ecbnt.zip", "rijn.tv.ecbvk.zip", "rijn.tv.ecbvt.zip" };
+
+ public TestResult perform()
+ {
+ countOfTests = 0;
+ for (int i = 0; i < zipFileNames.length; i++)
+ {
+ File inf = new File(zipFileNames[i]);
+ TestResult res = performTestsFromZipFile(inf);
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+ }
+ return new SimpleTestResult(true, getName() + ": " + countOfTests
+ + " performed Okay");
+ }
+
+ public static void main(String[] args)
+ {
+ AESVectorFileTest test = new AESVectorFileTest();
+ TestResult result = test.perform();
+ System.out.println(result);
+
+ test = new AESLightVectorFileTest();
+ result = test.perform();
+ System.out.println(result);
+
+ test = new AESFastVectorFileTest();
+ result = test.perform();
+ System.out.println(result);
+
+ }
+
+ private static class AESLightVectorFileTest extends AESVectorFileTest
+ {
+ protected BlockCipher createNewEngineForTest()
+ {
+ return new AESLightEngine();
+ }
+
+ public String getName()
+ {
+ return "AESLight";
+ }
+
+ }
+
+ private static class AESFastVectorFileTest extends AESVectorFileTest
+ {
+ protected BlockCipher createNewEngineForTest()
+ {
+ return new AESFastEngine();
+ }
+
+ public String getName()
+ {
+ return "AESFast";
+ }
+
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESWrapTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESWrapTest.java
new file mode 100644
index 000000000..38f4af700
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AESWrapTest.java
@@ -0,0 +1,238 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.engines.AESWrapEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * Wrap Test
+ */
+public class AESWrapTest
+ implements Test
+{
+ public String getName()
+ {
+ return "AESWrap";
+ }
+
+ private TestResult wrapTest(
+ int id,
+ byte[] kek,
+ byte[] in,
+ byte[] out)
+ {
+ Wrapper wrapper = new AESWrapEngine();
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ try
+ {
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test exception " + e.toString());
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ try
+ {
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test exception.", e);
+ }
+
+ //
+ // offset test
+ //
+ byte[] pText = new byte[5 + in.length];
+ byte[] cText;
+
+ System.arraycopy(in, 0, pText, 5, in.length);
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ try
+ {
+ cText = wrapper.wrap(pText, 5, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test exception " + e.toString());
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ cText = new byte[6 + out.length];
+ System.arraycopy(out, 0, cText, 6, out.length);
+
+ try
+ {
+ pText = wrapper.unwrap(cText, 6, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test exception.", e);
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public TestResult perform()
+ {
+ byte[] kek1 = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] in1 = Hex.decode("00112233445566778899aabbccddeeff");
+ byte[] out1 = Hex.decode("1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5");
+ TestResult result = wrapTest(1, kek1, in1, out1);
+
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ byte[] kek2 = Hex.decode("000102030405060708090a0b0c0d0e0f1011121314151617");
+ byte[] in2 = Hex.decode("00112233445566778899aabbccddeeff");
+ byte[] out2 = Hex.decode("96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d");
+ result = wrapTest(2, kek2, in2, out2);
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ byte[] kek3 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in3 = Hex.decode("00112233445566778899aabbccddeeff");
+ byte[] out3 = Hex.decode("64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7");
+ result = wrapTest(3, kek3, in3, out3);
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ byte[] kek4 = Hex.decode("000102030405060708090a0b0c0d0e0f1011121314151617");
+ byte[] in4 = Hex.decode("00112233445566778899aabbccddeeff0001020304050607");
+ byte[] out4 = Hex.decode("031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2");
+ result = wrapTest(4, kek4, in4, out4);
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ byte[] kek5 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in5 = Hex.decode("00112233445566778899aabbccddeeff0001020304050607");
+ byte[] out5 = Hex.decode("a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1");
+ result = wrapTest(5, kek5, in5, out5);
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ byte[] kek6 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in6 = Hex.decode("00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f");
+ byte[] out6 = Hex.decode("28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21");
+ result = wrapTest(6, kek6, in6, out6);
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ Wrapper wrapper = new AESWrapEngine();
+ KeyParameter key = new KeyParameter(new byte[16]);
+ byte[] buf = new byte[16];
+
+ try
+ {
+ wrapper.init(true, key);
+
+ wrapper.unwrap(buf, 0, buf.length);
+
+ return new SimpleTestResult(false, getName() + ": failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ catch (InvalidCipherTextException e)
+ {
+ return new SimpleTestResult(false, getName() + ": unexpected exception: " + e, e);
+ }
+
+ try
+ {
+ wrapper.init(false, key);
+
+ wrapper.wrap(buf, 0, buf.length);
+
+ return new SimpleTestResult(false, getName() + ": failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ //
+ // short test
+ //
+ try
+ {
+ wrapper.init(false, key);
+
+ wrapper.unwrap(buf, 0, buf.length / 2);
+
+ return new SimpleTestResult(false, getName() + ": failed unwrap short test.");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ wrapper.init(true, key);
+
+ wrapper.wrap(buf, 0, 15);
+
+ return new SimpleTestResult(false, getName() + ": failed wrap length test.");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ AESWrapTest test = new AESWrapTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AllTests.java
new file mode 100644
index 000000000..edc7b1b26
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/AllTests.java
@@ -0,0 +1,41 @@
+package org.spongycastle.crypto.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.spongycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testCrypto()
+ {
+ org.spongycastle.util.test.Test[] tests = RegressionTest.tests;
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Lightweight Crypto Tests");
+
+ suite.addTestSuite(AllTests.class);
+ suite.addTestSuite(GCMReorderTest.class);
+
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherMonteCarloTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherMonteCarloTest.java
new file mode 100644
index 000000000..0b08d4610
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherMonteCarloTest.java
@@ -0,0 +1,82 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * a basic test that takes a cipher, key parameter, and an input
+ * and output string. This test wraps the engine in a buffered block
+ * cipher with padding disabled.
+ */
+public class BlockCipherMonteCarloTest
+ extends SimpleTest
+{
+ int id;
+ int iterations;
+ BlockCipher engine;
+ CipherParameters param;
+ byte[] input;
+ byte[] output;
+
+ public BlockCipherMonteCarloTest(
+ int id,
+ int iterations,
+ BlockCipher engine,
+ CipherParameters param,
+ String input,
+ String output)
+ {
+ this.id = id;
+ this.iterations = iterations;
+ this.engine = engine;
+ this.param = param;
+ this.input = Hex.decode(input);
+ this.output = Hex.decode(output);
+ }
+
+ public String getName()
+ {
+ return engine.getAlgorithmName() + " Monte Carlo Test " + id;
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new BufferedBlockCipher(engine);
+
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ System.arraycopy(input, 0, out, 0, out.length);
+
+ for (int i = 0; i != iterations; i++)
+ {
+ int len1 = cipher.processBytes(out, 0, out.length, out, 0);
+
+ cipher.doFinal(out, len1);
+ }
+
+ if (!areEqual(out, output))
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ cipher.init(false, param);
+
+ for (int i = 0; i != iterations; i++)
+ {
+ int len1 = cipher.processBytes(out, 0, out.length, out, 0);
+
+ cipher.doFinal(out, len1);
+ }
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal");
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherResetTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherResetTest.java
new file mode 100644
index 000000000..9e95cfd8f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherResetTest.java
@@ -0,0 +1,206 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.AESLightEngine;
+import org.spongycastle.crypto.engines.BlowfishEngine;
+import org.spongycastle.crypto.engines.CAST5Engine;
+import org.spongycastle.crypto.engines.CAST6Engine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.NoekeonEngine;
+import org.spongycastle.crypto.engines.RC6Engine;
+import org.spongycastle.crypto.engines.SEEDEngine;
+import org.spongycastle.crypto.engines.SerpentEngine;
+import org.spongycastle.crypto.engines.TEAEngine;
+import org.spongycastle.crypto.engines.TwofishEngine;
+import org.spongycastle.crypto.engines.XTEAEngine;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.GOFBBlockCipher;
+import org.spongycastle.crypto.modes.OFBBlockCipher;
+import org.spongycastle.crypto.modes.OpenPGPCFBBlockCipher;
+import org.spongycastle.crypto.modes.PGPCFBBlockCipher;
+import org.spongycastle.crypto.modes.SICBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test whether block ciphers implement reset contract on init, encrypt/decrypt and reset.
+ */
+public class BlockCipherResetTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "Block Cipher Reset";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ // 128 bit block ciphers
+ testReset("AESFastEngine", new AESFastEngine(), new AESFastEngine(), new KeyParameter(new byte[16]));
+ testReset("AESEngine", new AESEngine(), new AESEngine(), new KeyParameter(new byte[16]));
+ testReset("AESLightEngine", new AESLightEngine(), new AESLightEngine(), new KeyParameter(new byte[16]));
+ testReset("Twofish", new TwofishEngine(), new TwofishEngine(), new KeyParameter(new byte[16]));
+ testReset("NoekeonEngine", new NoekeonEngine(), new NoekeonEngine(), new KeyParameter(new byte[16]));
+ testReset("SerpentEngine", new SerpentEngine(), new SerpentEngine(), new KeyParameter(new byte[16]));
+ testReset("SEEDEngine", new SEEDEngine(), new SEEDEngine(), new KeyParameter(new byte[16]));
+ testReset("CAST6Engine", new CAST6Engine(), new CAST6Engine(), new KeyParameter(new byte[16]));
+ testReset("RC6Engine", new RC6Engine(), new RC6Engine(), new KeyParameter(new byte[16]));
+
+ // 64 bit block ciphers
+ testReset("DESEngine", new DESEngine(), new DESEngine(), new KeyParameter(new byte[8]));
+ testReset("BlowfishEngine", new BlowfishEngine(), new BlowfishEngine(), new KeyParameter(new byte[8]));
+ testReset("CAST5Engine", new CAST5Engine(), new CAST5Engine(), new KeyParameter(new byte[8]));
+ testReset("DESedeEngine", new DESedeEngine(), new DESedeEngine(), new KeyParameter(new byte[24]));
+ testReset("TEAEngine", new TEAEngine(), new TEAEngine(), new KeyParameter(new byte[16]));
+ testReset("XTEAEngine", new XTEAEngine(), new XTEAEngine(), new KeyParameter(new byte[16]));
+
+ // primitive block cipher modes (don't reset on processBlock)
+ testModeReset("AES/CBC", new CBCBlockCipher(new AESEngine()), new CBCBlockCipher(new AESEngine()),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/SIC", new SICBlockCipher(new AESEngine()), new SICBlockCipher(new AESEngine()),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/CFB", new CFBBlockCipher(new AESEngine(), 128), new CFBBlockCipher(new AESEngine(), 128),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/OFB", new OFBBlockCipher(new AESEngine(), 128), new OFBBlockCipher(new AESEngine(), 128),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/GCTR", new GOFBBlockCipher(new DESEngine()), new GOFBBlockCipher(new DESEngine()),
+ new ParametersWithIV(new KeyParameter(new byte[8]), new byte[8]));
+ testModeReset("AES/OpenPGPCFB", new OpenPGPCFBBlockCipher(new AESEngine()), new OpenPGPCFBBlockCipher(
+ new AESEngine()), new KeyParameter(new byte[16]));
+ testModeReset("AES/PGPCFB", new PGPCFBBlockCipher(new AESEngine(), false), new PGPCFBBlockCipher(
+ new AESEngine(), false), new KeyParameter(new byte[16]));
+
+ // PGPCFB with IV is broken (it's also not a PRP, so probably shouldn't be a BlockCipher)
+ // testModeReset("AES/PGPCFBwithIV", new PGPCFBBlockCipher(new AESEngine(), true), new
+ // PGPCFBBlockCipher(
+ // new AESEngine(), true), new ParametersWithIV(new KeyParameter(new byte[16]), new
+ // byte[16]));
+ // testModeReset("AES/PGPCFBwithIV_NoIV", new PGPCFBBlockCipher(new AESEngine(), true), new
+ // PGPCFBBlockCipher(
+ // new AESEngine(), true), new KeyParameter(new byte[16]));
+
+ }
+
+ private void testModeReset(String test, BlockCipher cipher1, BlockCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ testReset(test, false, cipher1, cipher2, params);
+ }
+
+ private void testReset(String test, BlockCipher cipher1, BlockCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ testReset(test, true, cipher1, cipher2, params);
+ }
+
+ private void testReset(String test,
+ boolean testCryptReset,
+ BlockCipher cipher1,
+ BlockCipher cipher2,
+ CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ cipher1.init(true, params);
+
+ byte[] plaintext = new byte[cipher1.getBlockSize()];
+ byte[] ciphertext = new byte[(cipher1.getAlgorithmName().indexOf("PGPCFBwithIV")) > -1 ? 2 * cipher1.getBlockSize() + 2
+ : cipher1.getBlockSize()];
+
+ // Establish baseline answer
+ crypt(cipher1, true, plaintext, ciphertext);
+
+ // Test encryption resets
+ checkReset(test, testCryptReset, cipher1, params, true, plaintext, ciphertext);
+
+ // Test decryption resets with fresh instance
+ cipher2.init(false, params);
+ checkReset(test, testCryptReset, cipher2, params, false, ciphertext, plaintext);
+ }
+
+ private void checkReset(String test,
+ boolean testCryptReset,
+ BlockCipher cipher,
+ CipherParameters params,
+ boolean encrypt,
+ byte[] pretext,
+ byte[] posttext)
+ throws InvalidCipherTextException
+ {
+ // Do initial run
+ byte[] output = new byte[posttext.length];
+ crypt(cipher, encrypt, pretext, output);
+
+ // Check encrypt resets cipher
+ if (testCryptReset)
+ {
+ crypt(cipher, encrypt, pretext, output);
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test + (encrypt ? " encrypt" : " decrypt") + " did not reset cipher.");
+ }
+ }
+
+ // Check init resets data
+ cipher.processBlock(pretext, 0, output, 0);
+ cipher.init(encrypt, params);
+
+ try
+ {
+ crypt(cipher, encrypt, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test + " init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test + " init did not reset data.", new String(Hex.encode(posttext)), new String(Hex.encode(output)));
+ }
+
+ // Check reset resets data
+ cipher.processBlock(pretext, 0, output, 0);
+ cipher.reset();
+
+ try
+ {
+ crypt(cipher, encrypt, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test + " reset did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test + " reset did not reset data.");
+ }
+ }
+
+ private static void crypt(BlockCipher cipher1, boolean encrypt, byte[] plaintext, byte[] output)
+ throws InvalidCipherTextException
+ {
+ cipher1.processBlock(plaintext, 0, output, 0);
+ if ((cipher1.getAlgorithmName().indexOf("PGPCFBwithIV") > -1) && !encrypt)
+ {
+ // Process past IV in first block
+ cipher1.processBlock(plaintext, cipher1.getBlockSize(), output, 0);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new BlockCipherResetTest());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherVectorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherVectorTest.java
new file mode 100644
index 000000000..322a580df
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlockCipherVectorTest.java
@@ -0,0 +1,71 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * a basic test that takes a cipher, key parameter, and an input
+ * and output string. This test wraps the engine in a buffered block
+ * cipher with padding disabled.
+ */
+public class BlockCipherVectorTest
+ extends SimpleTest
+{
+ int id;
+ BlockCipher engine;
+ CipherParameters param;
+ byte[] input;
+ byte[] output;
+
+ public BlockCipherVectorTest(
+ int id,
+ BlockCipher engine,
+ CipherParameters param,
+ String input,
+ String output)
+ {
+ this.id = id;
+ this.engine = engine;
+ this.param = param;
+ this.input = Hex.decode(input);
+ this.output = Hex.decode(output);
+ }
+
+ public String getName()
+ {
+ return engine.getAlgorithmName() + " Vector Test " + id;
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new BufferedBlockCipher(engine);
+
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ int len1 = cipher.processBytes(input, 0, input.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (!areEqual(out, output))
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ cipher.init(false, param);
+
+ int len2 = cipher.processBytes(output, 0, output.length, out, 0);
+
+ cipher.doFinal(out, len2);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal got " + new String(Hex.encode(out)));
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlowfishTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlowfishTest.java
new file mode 100644
index 000000000..57e38a3e3
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/BlowfishTest.java
@@ -0,0 +1,57 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.BlowfishEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * blowfish tester - vectors from http://www.counterpane.com/vectors.txt
+ */
+public class BlowfishTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("0000000000000000")),
+ "0000000000000000", "4EF997456198DD78"),
+ new BlockCipherVectorTest(1, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("FFFFFFFFFFFFFFFF")),
+ "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"),
+ new BlockCipherVectorTest(2, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("3000000000000000")),
+ "1000000000000001", "7D856F9A613063F2"),
+ new BlockCipherVectorTest(3, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("1111111111111111")),
+ "1111111111111111", "2466DD878B963C9D"),
+ new BlockCipherVectorTest(4, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "1111111111111111", "61F9C3802281B096"),
+ new BlockCipherVectorTest(5, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("FEDCBA9876543210")),
+ "0123456789ABCDEF", "0ACEAB0FC6A0A28D"),
+ new BlockCipherVectorTest(6, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("7CA110454A1A6E57")),
+ "01A1D6D039776742", "59C68245EB05282B"),
+ new BlockCipherVectorTest(7, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("0131D9619DC1376E")),
+ "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"),
+ };
+
+ BlowfishTest()
+ {
+ super(tests, new BlowfishEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "Blowfish";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new BlowfishTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CAST5Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CAST5Test.java
new file mode 100644
index 000000000..339edd485
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CAST5Test.java
@@ -0,0 +1,44 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.CAST5Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * cast tester - vectors from http://www.ietf.org/rfc/rfc2144.txt
+ */
+public class CAST5Test
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new CAST5Engine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0123456789ABCDEF",
+ "238B4FE5847E44B2"),
+ new BlockCipherVectorTest(0, new CAST5Engine(),
+ new KeyParameter(Hex.decode("01234567123456782345")),
+ "0123456789ABCDEF",
+ "EB6A711A2C02271B"),
+ new BlockCipherVectorTest(0, new CAST5Engine(),
+ new KeyParameter(Hex.decode("0123456712")),
+ "0123456789ABCDEF",
+ "7Ac816d16E9B302E"),
+ };
+
+ CAST5Test()
+ {
+ super(tests, new CAST5Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "CAST5";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CAST5Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CAST6Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CAST6Test.java
new file mode 100644
index 000000000..bd62fd2be
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CAST6Test.java
@@ -0,0 +1,44 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.CAST6Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * cast6 tester - vectors from http://www.ietf.org/rfc/rfc2612.txt
+ */
+public class CAST6Test
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new CAST6Engine(),
+ new KeyParameter(Hex.decode("2342bb9efa38542c0af75647f29f615d")),
+ "00000000000000000000000000000000",
+ "c842a08972b43d20836c91d1b7530f6b"),
+ new BlockCipherVectorTest(0, new CAST6Engine(),
+ new KeyParameter(Hex.decode("2342bb9efa38542cbed0ac83940ac298bac77a7717942863")),
+ "00000000000000000000000000000000",
+ "1b386c0210dcadcbdd0e41aa08a7a7e8"),
+ new BlockCipherVectorTest(0, new CAST6Engine(),
+ new KeyParameter(Hex.decode("2342bb9efa38542cbed0ac83940ac2988d7c47ce264908461cc1b5137ae6b604")),
+ "00000000000000000000000000000000",
+ "4f6a2038286897b9c9870136553317fa")
+ };
+
+ CAST6Test()
+ {
+ super(tests, new CAST6Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "CAST6";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CAST6Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CCMTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CCMTest.java
new file mode 100644
index 000000000..71bd7cb71
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CCMTest.java
@@ -0,0 +1,301 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.modes.CCMBlockCipher;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * First four test vectors from
+ * NIST Special Publication 800-38C.
+ */
+public class CCMTest
+ extends SimpleTest
+{
+ private byte[] K1 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N1 = Hex.decode("10111213141516");
+ private byte[] A1 = Hex.decode("0001020304050607");
+ private byte[] P1 = Hex.decode("20212223");
+ private byte[] C1 = Hex.decode("7162015b4dac255d");
+ private byte[] T1 = Hex.decode("6084341b");
+
+ private byte[] K2 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N2 = Hex.decode("1011121314151617");
+ private byte[] A2 = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ private byte[] P2 = Hex.decode("202122232425262728292a2b2c2d2e2f");
+ private byte[] C2 = Hex.decode("d2a1f0e051ea5f62081a7792073d593d1fc64fbfaccd");
+ private byte[] T2 = Hex.decode("7f479ffca464");
+
+ private byte[] K3 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N3 = Hex.decode("101112131415161718191a1b");
+ private byte[] A3 = Hex.decode("000102030405060708090a0b0c0d0e0f10111213");
+ private byte[] P3 = Hex.decode("202122232425262728292a2b2c2d2e2f3031323334353637");
+ private byte[] C3 = Hex.decode("e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5484392fbc1b09951");
+ private byte[] T3 = Hex.decode("67c99240c7d51048");
+
+ private byte[] K4 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N4 = Hex.decode("101112131415161718191a1b1c");
+ private byte[] A4 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+ private byte[] P4 = Hex.decode("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
+ private byte[] C4 = Hex.decode("69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72b4ac6bec93e8598e7f0dadbcea5b");
+ private byte[] T4 = Hex.decode("f4dd5d0ee404617225ffe34fce91");
+
+ //
+ // long data vector
+ //
+ private byte[] C5 = Hex.decode("49b17d8d3ea4e6174a48e2b65e6d8b417ac0dd3f8ee46ce4a4a2a509661cef52528c1cd9805333a5cfd482fa3f095a3c2fdd1cc47771c5e55fddd60b5c8d6d3fa5c8dd79d08b16242b6642106e7c0c28bd1064b31e6d7c9800c8397dbc3fa8071e6a38278b386c18d65d39c6ad1ef9501a5c8f68d38eb6474799f3cc898b4b9b97e87f9c95ce5c51bc9d758f17119586663a5684e0a0daf6520ec572b87473eb141d10471e4799ded9e607655402eca5176bbf792ef39dd135ac8d710da8e9e854fd3b95c681023f36b5ebe2fb213d0b62dd6e9e3cfe190b792ccb20c53423b2dca128f861a61d306910e1af418839467e466f0ec361d2539eedd99d4724f1b51c07beb40e875a87491ec8b27cd1");
+ private byte[] T5 = Hex.decode("5c768856796b627b13ec8641581b");
+
+ public void performTest()
+ throws Exception
+ {
+ CCMBlockCipher ccm = new CCMBlockCipher(new AESEngine());
+
+ checkVectors(0, ccm, K1, 32, N1, A1, P1, T1, C1);
+ checkVectors(1, ccm, K2, 48, N2, A2, P2, T2, C2);
+ checkVectors(2, ccm, K3, 64, N3, A3, P3, T3, C3);
+
+ ivParamTest(0, ccm, K1, N1);
+
+ //
+ // 4 has a reduced associated text which needs to be replicated
+ //
+ byte[] a4 = new byte[65536]; // 524288 / 8
+
+ for (int i = 0; i < a4.length; i += A4.length)
+ {
+ System.arraycopy(A4, 0, a4, i, A4.length);
+ }
+
+ checkVectors(3, ccm, K4, 112, N4, a4, P4, T4, C4);
+
+ //
+ // long data test
+ //
+ checkVectors(4, ccm, K4, 112, N4, A4, A4, T5, C5);
+
+ // decryption with output specified, non-zero offset.
+ ccm.init(false, new AEADParameters(new KeyParameter(K2), 48, N2, A2));
+
+ byte[] inBuf = new byte[C2.length + 10];
+ byte[] outBuf = new byte[ccm.getOutputSize(C2.length) + 10];
+
+ System.arraycopy(C2, 0, inBuf, 10, C2.length);
+
+ int len = ccm.processPacket(inBuf, 10, C2.length, outBuf, 10);
+ byte[] out = ccm.processPacket(C2, 0, C2.length);
+
+ if (len != out.length || !isEqual(out, outBuf, 10))
+ {
+ fail("decryption output incorrect");
+ }
+
+ // encryption with output specified, non-zero offset.
+ ccm.init(true, new AEADParameters(new KeyParameter(K2), 48, N2, A2));
+
+ int inLen = len;
+ inBuf = outBuf;
+ outBuf = new byte[ccm.getOutputSize(inLen) + 10];
+
+ len = ccm.processPacket(inBuf, 10, inLen, outBuf, 10);
+ out = ccm.processPacket(inBuf, 10, inLen);
+
+ if (len != out.length || !isEqual(out, outBuf, 10))
+ {
+ fail("encryption output incorrect");
+ }
+
+ //
+ // exception tests
+ //
+
+ try
+ {
+ ccm.init(false, new AEADParameters(new KeyParameter(K1), 32, N2, A2));
+
+ ccm.processPacket(C2, 0, C2.length);
+
+ fail("invalid cipher text not picked up");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ccm = new CCMBlockCipher(new DESEngine());
+
+ fail("incorrect block size not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ccm.init(false, new KeyParameter(K1));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ AEADTestUtil.testReset(this, new CCMBlockCipher(new AESEngine()), new CCMBlockCipher(new AESEngine()), new AEADParameters(new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testTampering(this, ccm, new AEADParameters(new KeyParameter(K1), 32, N2));
+ }
+
+ private boolean isEqual(byte[] exp, byte[] other, int off)
+ {
+ for (int i = 0; i != exp.length; i++)
+ {
+ if (exp[i] != other[off + i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void checkVectors(
+ int count,
+ CCMBlockCipher ccm,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ byte[] fa = new byte[a.length / 2];
+ byte[] la = new byte[a.length - (a.length / 2)];
+ System.arraycopy(a, 0, fa, 0, fa.length);
+ System.arraycopy(a, fa.length, la, 0, la.length);
+
+ checkVectors(count, ccm, "all initial associated data", k, macSize, n, a, null, p, t, c);
+ checkVectors(count, ccm, "subsequent associated data", k, macSize, n, null, a, p, t, c);
+ checkVectors(count, ccm, "split associated data", k, macSize, n, fa, la, p, t, c);
+ checkVectors(count, ccm, "reuse key", null, macSize, n, fa, la, p, t, c);
+ }
+
+ private void checkVectors(
+ int count,
+ CCMBlockCipher ccm,
+ String additionalDataType,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] sa,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ KeyParameter keyParam = (k == null) ? null : new KeyParameter(k);
+
+ ccm.init(true, new AEADParameters(keyParam, macSize, n, a));
+
+ byte[] enc = new byte[c.length];
+
+ if (sa != null)
+ {
+ ccm.processAADBytes(sa, 0, sa.length);
+ }
+
+ int len = ccm.processBytes(p, 0, p.length, enc, 0);
+
+ len += ccm.doFinal(enc, len);
+
+ if (!areEqual(c, enc))
+ {
+ fail("encrypted stream fails to match in test " + count + " with " + additionalDataType);
+ }
+
+ ccm.init(false, new AEADParameters(keyParam, macSize, n, a));
+
+ byte[] tmp = new byte[enc.length];
+
+ if (sa != null)
+ {
+ ccm.processAADBytes(sa, 0, sa.length);
+ }
+
+ len = ccm.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += ccm.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count + " with " + additionalDataType,
+ new String(Hex.encode(p)), new String(Hex.encode(dec)));
+ }
+
+ if (!areEqual(t, ccm.getMac()))
+ {
+ fail("MAC fails to match in test " + count + " with " + additionalDataType);
+ }
+ }
+
+ private void ivParamTest(
+ int count,
+ CCMBlockCipher ccm,
+ byte[] k,
+ byte[] n)
+ throws InvalidCipherTextException
+ {
+ byte[] p = Strings.toByteArray("hello world!!");
+
+ ccm.init(true, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] enc = new byte[p.length + 8];
+
+ int len = ccm.processBytes(p, 0, p.length, enc, 0);
+
+ len += ccm.doFinal(enc, len);
+
+ ccm.init(false, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] tmp = new byte[enc.length];
+
+ len = ccm.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += ccm.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "CCM";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CCMTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CMacTest.java
new file mode 100644
index 000000000..e7ea06551
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CMacTest.java
@@ -0,0 +1,279 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.macs.CMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * CMAC tester - Official Test Vectors.
+ */
+public class CMacTest
+ extends SimpleTest
+{
+ private static final byte[] keyBytes128 = Hex.decode("2b7e151628aed2a6abf7158809cf4f3c");
+ private static final byte[] keyBytes192 = Hex.decode(
+ "8e73b0f7da0e6452c810f32b809079e5"
+ + "62f8ead2522c6b7b");
+ private static final byte[] keyBytes256 = Hex.decode(
+ "603deb1015ca71be2b73aef0857d7781"
+ + "1f352c073b6108d72d9810a30914dff4");
+
+ private static final byte[] input0 = Hex.decode("");
+ private static final byte[] input16 = Hex.decode("6bc1bee22e409f96e93d7e117393172a");
+ private static final byte[] input40 = Hex.decode(
+ "6bc1bee22e409f96e93d7e117393172a"
+ + "ae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411");
+ private static final byte[] input64 = Hex.decode(
+ "6bc1bee22e409f96e93d7e117393172a"
+ + "ae2d8a571e03ac9c9eb76fac45af8e51"
+ + "30c81c46a35ce411e5fbc1191a0a52ef"
+ + "f69f2445df4f9b17ad2b417be66c3710");
+
+ private static final byte[] output_k128_m0 = Hex.decode("bb1d6929e95937287fa37d129b756746");
+ private static final byte[] output_k128_m16 = Hex.decode("070a16b46b4d4144f79bdd9dd04a287c");
+ private static final byte[] output_k128_m40 = Hex.decode("dfa66747de9ae63030ca32611497c827");
+ private static final byte[] output_k128_m64 = Hex.decode("51f0bebf7e3b9d92fc49741779363cfe");
+
+ private static final byte[] output_k192_m0 = Hex.decode("d17ddf46adaacde531cac483de7a9367");
+ private static final byte[] output_k192_m16 = Hex.decode("9e99a7bf31e710900662f65e617c5184");
+ private static final byte[] output_k192_m40 = Hex.decode("8a1de5be2eb31aad089a82e6ee908b0e");
+ private static final byte[] output_k192_m64 = Hex.decode("a1d5df0eed790f794d77589659f39a11");
+
+ private static final byte[] output_k256_m0 = Hex.decode("028962f61b7bf89efc6b551f4667d983");
+ private static final byte[] output_k256_m16 = Hex.decode("28a7023f452e8f82bd4bf28d8c37c35c");
+ private static final byte[] output_k256_m40 = Hex.decode("aaf3d8f1de5640c232f5b169b9c911e6");
+ private static final byte[] output_k256_m64 = Hex.decode("e1992190549f6ed5696a2c056c315410");
+
+ public CMacTest()
+ {
+ }
+
+ public void performTest()
+ {
+ BlockCipher cipher = new AESFastEngine();
+ Mac mac = new CMac(cipher, 128);
+
+ //128 bytes key
+
+ KeyParameter key = new KeyParameter(keyBytes128);
+
+ // 0 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input0, 0, input0.length);
+
+ byte[] out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m0))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m0))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 16 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 40 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input40, 0, input40.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m40))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m40))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 64 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input64, 0, input64.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m64))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m64))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ //192 bytes key
+
+ key = new KeyParameter(keyBytes192);
+
+ // 0 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input0, 0, input0.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m0))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m0))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 16 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 40 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input40, 0, input40.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m40))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m40))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 64 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input64, 0, input64.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m64))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m64))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ //256 bytes key
+
+ key = new KeyParameter(keyBytes256);
+
+ // 0 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input0, 0, input0.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m0))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m0))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 16 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 40 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input40, 0, input40.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m40))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m40))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 64 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input64, 0, input64.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m64))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m64))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ testExceptions();
+ }
+
+ private void testExceptions()
+ {
+ try
+ {
+ CMac mac = new CMac(new AESEngine());
+ mac.init(new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ fail("CMac does not accept IV");
+ } catch(IllegalArgumentException e)
+ {
+ // Expected
+ }
+ }
+
+ public String getName()
+ {
+ return "CMac";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new CMacTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CTSTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CTSTest.java
new file mode 100644
index 000000000..768461936
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CTSTest.java
@@ -0,0 +1,212 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.engines.SkipjackEngine;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CTSBlockCipher;
+import org.spongycastle.crypto.modes.OldCTSBlockCipher;
+import org.spongycastle.crypto.modes.SICBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * CTS tester
+ */
+public class CTSTest
+ extends SimpleTest
+{
+ static byte[] in1 = Hex.decode("4e6f7720697320746865207420");
+ static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa");
+ static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98");
+ static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994");
+ static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6");
+
+ private void testCTS(
+ int id,
+ BlockCipher cipher,
+ CipherParameters params,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ byte[] out = new byte[input.length];
+ BufferedBlockCipher engine = new CTSBlockCipher(cipher);
+
+ engine.init(true, params);
+
+ int len = engine.processBytes(input, 0, input.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(output, out))
+ {
+ fail("failed encryption expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ engine.init(false, params);
+
+ len = engine.processBytes(output, 0, output.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed decryption expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void testOldCTS(
+ int id,
+ BlockCipher cipher,
+ CipherParameters params,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ byte[] out = new byte[input.length];
+ BufferedBlockCipher engine = new OldCTSBlockCipher(cipher);
+
+ engine.init(true, params);
+
+ int len = engine.processBytes(input, 0, input.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(output, out))
+ {
+ fail("failed encryption expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ engine.init(false, params);
+
+ len = engine.processBytes(output, 0, output.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed decryption expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ BufferedBlockCipher engine = new CTSBlockCipher(new DESEngine());
+ CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]);
+ engine.init(true, params);
+
+ byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())];
+
+ engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ fail("Expected CTS encrypt error on < 1 block input");
+ } catch(DataLengthException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, params);
+ engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ } catch(DataLengthException e)
+ {
+ fail("Unexpected CTS encrypt error on == 1 block input");
+ }
+
+ engine.init(false, params);
+ engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ fail("Expected CTS decrypt error on < 1 block input");
+ } catch(DataLengthException e)
+ {
+ // Expected
+ }
+
+ engine.init(false, params);
+ engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ } catch(DataLengthException e)
+ {
+ fail("Unexpected CTS decrypt error on == 1 block input");
+ }
+
+ try
+ {
+ new CTSBlockCipher(new SICBlockCipher(new AESEngine()));
+ fail("Expected CTS construction error - only ECB/CBC supported.");
+ } catch(IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ }
+
+ public String getName()
+ {
+ return "CTS";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ byte[] key1 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF };
+ byte[] key2 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, (byte)0xee, (byte)0xff };
+ byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ testCTS(1, new DESEngine(), new KeyParameter(key1), in1, out1);
+ testCTS(2, new CBCBlockCipher(new DESEngine()), new ParametersWithIV(new KeyParameter(key1), iv), in1, out2);
+ testCTS(3, new CBCBlockCipher(new SkipjackEngine()), new ParametersWithIV(new KeyParameter(key2), iv), in2, out3);
+
+ //
+ // test vectors from rfc3962
+ //
+ byte[] aes128 = Hex.decode("636869636b656e207465726979616b69");
+ byte[] aesIn1 = Hex.decode("4920776f756c64206c696b652074686520");
+ byte[] aesOut1 = Hex.decode("c6353568f2bf8cb4d8a580362da7ff7f97");
+ byte[] aesIn2 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c20476175277320");
+ byte[] aesOut2 = Hex.decode("fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5");
+ byte[] aesIn3 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c2047617527732043");
+ byte[] aesOut3 = Hex.decode("39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584");
+
+ testCTS(4, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn1, aesOut1);
+ testCTS(5, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn2, aesOut2);
+ testCTS(6, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn3, aesOut3);
+
+ testOldCTS(4, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn1, aesOut1);
+ testOldCTS(5, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn2, aesOut2);
+ testOldCTS(6, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn3, aesOut3);
+
+ byte[] aes1Block = Hex.decode("4920776f756c64206c696b6520746865");
+ byte[] preErrata = Hex.decode("e7664c13ff28c965b0d2a0e7ec353706"); // CTS style one block
+ byte[] pstErrata = Hex.decode("97687268d6ecccc0c07b25e25ecfe584"); // CBC style one block
+ byte[] pstErrataNonZeroIV = Hex.decode("571f5108c53fe95ab52df783df933fa3");
+
+ testCTS(7, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aes1Block, pstErrata);
+ testCTS(8, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), aes1Block), aes1Block, pstErrataNonZeroIV);
+ testOldCTS(7, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aes1Block, preErrata);
+
+ testExceptions();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CTSTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CamelliaLightTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CamelliaLightTest.java
new file mode 100644
index 000000000..71a89fba7
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CamelliaLightTest.java
@@ -0,0 +1,66 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.CamelliaLightEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Camellia tester - vectors from https://www.cosic.esat.kuleuven.be/nessie/testvectors/ and RFC 3713
+ */
+public class CamelliaLightTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "07923A39EB0A817D1C4D87BDB82D1F1C"),
+ new BlockCipherVectorTest(1, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "6C227F749319A3AA7DA235A9BBA05A2C"),
+ new BlockCipherVectorTest(2, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")),
+ "0123456789abcdeffedcba9876543210", "67673138549669730857065648eabe43"),
+ //
+ // 192 bit
+ //
+ new BlockCipherVectorTest(3, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba98765432100011223344556677")),
+ "0123456789abcdeffedcba9876543210", "b4993401b3e996f84ee5cee7d79b09b9"),
+ new BlockCipherVectorTest(4, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00040000000000000000000000000000", "9BCA6C88B928C1B0F57F99866583A9BC"),
+ new BlockCipherVectorTest(5, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("949494949494949494949494949494949494949494949494")),
+ "636EB22D84B006381235641BCF0308D2", "94949494949494949494949494949494"),
+ //
+ // 256 bit
+ //
+ new BlockCipherVectorTest(6, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff")),
+ "0123456789abcdeffedcba9876543210", "9acc237dff16d76c20ef7c919e3a7509"),
+ new BlockCipherVectorTest(7, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A")),
+ "057764FE3A500EDBD988C5C3B56CBA9A", "4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A"),
+ new BlockCipherVectorTest(8, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")),
+ "7968B08ABA92193F2295121EF8D75C8A", "03030303030303030303030303030303"),
+ };
+
+ CamelliaLightTest()
+ {
+ super(tests, new CamelliaLightEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "CamelliaLight";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CamelliaLightTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CamelliaTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CamelliaTest.java
new file mode 100644
index 000000000..a3d481939
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/CamelliaTest.java
@@ -0,0 +1,70 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.CamelliaEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * Camellia tester - vectors from https://www.cosic.esat.kuleuven.be/nessie/testvectors/ and RFC 3713
+ */
+public class CamelliaTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "07923A39EB0A817D1C4D87BDB82D1F1C"),
+ new BlockCipherVectorTest(1, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "6C227F749319A3AA7DA235A9BBA05A2C"),
+ new BlockCipherVectorTest(2, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")),
+ "0123456789abcdeffedcba9876543210", "67673138549669730857065648eabe43"),
+ //
+ // 192 bit
+ //
+ new BlockCipherVectorTest(3, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba98765432100011223344556677")),
+ "0123456789abcdeffedcba9876543210", "b4993401b3e996f84ee5cee7d79b09b9"),
+ new BlockCipherVectorTest(4, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00040000000000000000000000000000", "9BCA6C88B928C1B0F57F99866583A9BC"),
+ new BlockCipherVectorTest(5, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("949494949494949494949494949494949494949494949494")),
+ "636EB22D84B006381235641BCF0308D2", "94949494949494949494949494949494"),
+ //
+ // 256 bit
+ //
+ new BlockCipherVectorTest(6, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff")),
+ "0123456789abcdeffedcba9876543210", "9acc237dff16d76c20ef7c919e3a7509"),
+ new BlockCipherVectorTest(7, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A")),
+ "057764FE3A500EDBD988C5C3B56CBA9A", "4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A"),
+ new BlockCipherVectorTest(8, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")),
+ "7968B08ABA92193F2295121EF8D75C8A", "03030303030303030303030303030303"),
+ };
+
+ CamelliaTest()
+ {
+ super(tests, new CamelliaEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Camellia";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ CamelliaTest test = new CamelliaTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ChaChaTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ChaChaTest.java
new file mode 100644
index 000000000..3da3f3e7b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ChaChaTest.java
@@ -0,0 +1,273 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.ChaChaEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ChaCha Test
+ *
+ */
+ BigInteger r = new BigInteger("29700980915817952874371204983938256990422752107994319651632687982059210933395");
+ BigInteger s = new BigInteger("574973400270084654178925310019147038455227042649098563933718999175515839552");
+
+ byte[] kData = new BigInteger("53854137677348463731403841147996619241504003434302020712960838528893196233395").toByteArray();
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ private void ecGOST3410_TEST()
+ {
+ BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041"); //p
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("7"), // a
+ new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414")); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("2"), // x
+ new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y
+ new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619")); // q
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("55441196065363246126355624130324183196576709222340016572108097750006097525544"), // d
+ params);
+
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ byte[] mVal = new BigInteger("20798893674476452017134061561508270130637142515379653289952617252661468872421").toByteArray();
+ byte[] message = new byte[mVal.length];
+
+ for (int i = 0; i != mVal.length; i++)
+ {
+ message[i] = mVal[mVal.length - 1 - i];
+ }
+
+ BigInteger[] sig = ecgost3410.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong.", r, sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong.", s, sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.createPoint(
+ new BigInteger("57520216126176808443631405023338071176630104906313632182896741342206604859403"), // x
+ new BigInteger("17614944419213781543809391949654080031942662045363639260709847859438286763994")), // y
+ params);
+
+ ecgost3410.init(false, pubKey);
+ if (!ecgost3410.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("verification fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with test parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-TestParamSet P.46
+ */
+ private void ecGOST3410_TestParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041"); //p
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("7"), // a
+ new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414")); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("2"), // x
+ new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y
+ new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619")); // q
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ //get hash message using the digest GOST3411.
+ byte[] message = "Message for sign".getBytes();
+ GOST3411Digest gost3411 = new GOST3411Digest();
+ gost3411.update(message, 0, message.length);
+ byte[] hashmessage = new byte[gost3411.getDigestSize()];
+ gost3411.doFinal(hashmessage, 0);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with A parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-CryptoPro-A-ParamSet P.47
+ */
+ public void ecGOST3410_AParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); //p
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a
+ new BigInteger("166")); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("1"), // x
+ new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
+ new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323")); // q
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with B parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-CryptoPro-B-ParamSet P.47-48
+ */
+ private void ecGOST3410_BParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823193"); //p
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a
+ new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595")); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("1"), // x
+ new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124")), // y
+ new BigInteger("57896044618658097711785492504343953927102133160255826820068844496087732066703")); // q
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with C parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-CryptoPro-C-ParamSet P.48
+ */
+ private void ecGOST3410_CParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); //p
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a
+ new BigInteger("32858")); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("0"), // x
+ new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), // y
+ new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601")); // q
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ public String getName()
+ {
+ return "ECGOST3410";
+ }
+
+ public void performTest()
+ {
+ ecGOST3410_TEST();
+ ecGOST3410_TestParam();
+ ecGOST3410_AParam();
+ ecGOST3410_BParam();
+ ecGOST3410_CParam();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECGOST3410Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECIESKeyEncapsulationTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECIESKeyEncapsulationTest.java
new file mode 100755
index 000000000..10a734110
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECIESKeyEncapsulationTest.java
@@ -0,0 +1,138 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.sec.SECNamedCurves;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
+import org.spongycastle.crypto.generators.KDF2BytesGenerator;
+import org.spongycastle.crypto.kems.ECIESKeyEncapsulation;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Tests for the ECIES Key Encapsulation Mechanism
+ */
+public class ECIESKeyEncapsulationTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ECIESKeyEncapsulation";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+
+ // Set EC domain parameters and generate key pair
+ X9ECParameters spec = SECNamedCurves.getByName("secp224r1");
+ ECDomainParameters ecDomain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN());
+ ECKeyPairGenerator ecGen = new ECKeyPairGenerator();
+
+ ecGen.init(new ECKeyGenerationParameters(ecDomain, new SecureRandom()));
+
+ AsymmetricCipherKeyPair keys = ecGen.generateKeyPair();
+
+ // Set ECIES-KEM parameters
+ ECIESKeyEncapsulation kem;
+ KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest());
+ SecureRandom rnd = new SecureRandom();
+ byte[] out = new byte[57];
+ KeyParameter key1, key2;
+
+ // Test basic ECIES-KEM
+ kem = new ECIESKeyEncapsulation(kdf, rnd);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed basic test");
+ }
+
+ // Test ECIES-KEM using new cofactor mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, false);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed cofactor test");
+ }
+
+ // Test ECIES-KEM using old cofactor mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, false);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed old cofactor test");
+ }
+
+ // Test ECIES-KEM using single hash mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, false, false, true);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed single hash test");
+ }
+
+ // Test ECIES-KEM using new cofactor mode and single hash mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, true);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed cofactor and single hash test");
+ }
+
+ // Test ECIES-KEM using old cofactor mode and single hash mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, true);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed old cofactor and single hash test");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECIESKeyEncapsulationTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECIESTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECIESTest.java
new file mode 100644
index 000000000..70d0501e9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECIESTest.java
@@ -0,0 +1,349 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.KeyEncoder;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.engines.IESEngine;
+import org.spongycastle.crypto.engines.TwofishEngine;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
+import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator;
+import org.spongycastle.crypto.generators.KDF2BytesGenerator;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.IESParameters;
+import org.spongycastle.crypto.params.IESWithCipherParameters;
+import org.spongycastle.crypto.parsers.ECIESPublicKeyParser;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * test for ECIES - Elliptic Curve Integrated Encryption Scheme
+ */
+public class ECIESTest
+ extends SimpleTest
+{
+ ECIESTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "ECIES";
+ }
+
+ private void staticTest()
+ throws Exception
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ AsymmetricCipherKeyPair p1 = new AsymmetricCipherKeyPair(pubKey, priKey);
+ AsymmetricCipherKeyPair p2 = new AsymmetricCipherKeyPair(pubKey, priKey);
+
+ //
+ // stream test
+ //
+ IESEngine i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ IESEngine i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ IESParameters p = new IESParameters(d, e, 64);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ byte[] message = Hex.decode("1234567890abcdef");
+
+ byte[] out1 = i1.processBlock(message, 0, message.length);
+
+ if (!areEqual(out1, Hex.decode("468d89877e8238802403ec4cb6b329faeccfa6f3a730f2cdb3c0a8e8")))
+ {
+ fail("stream cipher test failed on enc");
+ }
+
+ byte[] out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("stream cipher test failed");
+ }
+
+ //
+ // twofish with CBC
+ //
+ BufferedBlockCipher c1 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ BufferedBlockCipher c2 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c1);
+ i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c2);
+ d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ p = new IESWithCipherParameters(d, e, 64, 128);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ message = Hex.decode("1234567890abcdef");
+
+ out1 = i1.processBlock(message, 0, message.length);
+
+ if (!areEqual(out1, Hex.decode("b8a06ea5c2b9df28b58a0a90a734cde8c9c02903e5c220021fe4417410d1e53a32a71696")))
+ {
+ fail("twofish cipher test failed on enc");
+ }
+
+ out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("twofish cipher test failed");
+ }
+ }
+
+ private void doEphemeralTest()
+ throws Exception
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ AsymmetricCipherKeyPair p1 = new AsymmetricCipherKeyPair(pubKey, priKey);
+ AsymmetricCipherKeyPair p2 = new AsymmetricCipherKeyPair(pubKey, priKey);
+
+ // Generate the ephemeral key pair
+ ECKeyPairGenerator gen = new ECKeyPairGenerator();
+ gen.init(new ECKeyGenerationParameters(params, new SecureRandom()));
+
+ EphemeralKeyPairGenerator ephKeyGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder()
+ {
+ public byte[] getEncoded(AsymmetricKeyParameter keyParameter)
+ {
+ return ((ECPublicKeyParameters)keyParameter).getQ().getEncoded();
+ }
+ });
+
+ //
+ // stream test
+ //
+ IESEngine i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ IESEngine i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+
+ byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ IESParameters p = new IESParameters(d, e, 64);
+
+ i1.init(p2.getPublic(), p, ephKeyGen);
+ i2.init(p2.getPrivate(), p, new ECIESPublicKeyParser(params));
+
+ byte[] message = Hex.decode("1234567890abcdef");
+
+ byte[] out1 = i1.processBlock(message, 0, message.length);
+
+ byte[] out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("stream cipher test failed");
+ }
+
+ //
+ // twofish with CBC
+ //
+ BufferedBlockCipher c1 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ BufferedBlockCipher c2 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c1);
+ i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c2);
+ d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ p = new IESWithCipherParameters(d, e, 64, 128);
+
+ i1.init(p2.getPublic(), p, ephKeyGen);
+ i2.init(p2.getPrivate(), p, new ECIESPublicKeyParser(params));
+
+ message = Hex.decode("1234567890abcdef");
+
+ out1 = i1.processBlock(message, 0, message.length);
+
+ out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("twofish cipher test failed");
+ }
+ }
+
+ private void doTest(AsymmetricCipherKeyPair p1, AsymmetricCipherKeyPair p2)
+ throws Exception
+ {
+ //
+ // stream test
+ //
+ IESEngine i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ IESEngine i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ IESParameters p = new IESParameters(d, e, 64);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ byte[] message = Hex.decode("1234567890abcdef");
+
+ byte[] out1 = i1.processBlock(message, 0, message.length);
+
+ byte[] out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("stream cipher test failed");
+ }
+
+ //
+ // twofish with CBC
+ //
+ BufferedBlockCipher c1 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ BufferedBlockCipher c2 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c1);
+ i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c2);
+ d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ p = new IESWithCipherParameters(d, e, 64, 128);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ message = Hex.decode("1234567890abcdef");
+
+ out1 = i1.processBlock(message, 0, message.length);
+
+ out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("twofish cipher test failed");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ staticTest();
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
+
+ ECKeyPairGenerator eGen = new ECKeyPairGenerator();
+ KeyGenerationParameters gParam = new ECKeyGenerationParameters(params, new SecureRandom());
+
+ eGen.init(gParam);
+
+ AsymmetricCipherKeyPair p1 = eGen.generateKeyPair();
+ AsymmetricCipherKeyPair p2 = eGen.generateKeyPair();
+
+ doTest(p1, p2);
+
+ doEphemeralTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECIESTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECNRTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECNRTest.java
new file mode 100644
index 000000000..1f3dfe721
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECNRTest.java
@@ -0,0 +1,95 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.ECNRSigner;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.FixedSecureRandom;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ECNR tests.
+ */
+public class ECNRTest
+ extends SimpleTest
+{
+ /**
+ * a basic regression test with 239 bit prime
+ */
+ BigInteger r = new BigInteger("308636143175167811492623515537541734843573549327605293463169625072911693");
+ BigInteger s = new BigInteger("852401710738814635664888632022555967400445256405412579597015412971797143");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("700000017569056646655505781757157107570501575775705779575555657156756655"));
+
+ SecureRandom k = new FixedSecureRandom(true, kData);
+
+ private void ecNR239bitPrime()
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
+ params);
+
+ ECNRSigner ecnr = new ECNRSigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecnr.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecnr.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong.", r, sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong.", s, sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
+ params);
+
+ ecnr.init(false, pubKey);
+ if (!ecnr.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ public String getName()
+ {
+ return "ECNR";
+ }
+
+ public void performTest()
+ {
+ ecNR239bitPrime();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECNRTest());
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECTest.java
new file mode 100644
index 000000000..07ed4c7fa
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ECTest.java
@@ -0,0 +1,901 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.asn1.sec.SECNamedCurves;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.BasicAgreement;
+import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
+import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.spongycastle.crypto.agreement.ECMQVBasicAgreement;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.MQVPrivateParameters;
+import org.spongycastle.crypto.params.MQVPublicParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.ECDSASigner;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.FixedSecureRandom;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ECDSA tests are taken from X9.62.
+ */
+public class ECTest
+ extends SimpleTest
+{
+ /**
+ * X9.62 - 1998,
+ * J.3.1, Page 152, ECDSA over the field Fp
+ * an example with 192 bit prime
+ */
+ private void testECDSA192bitPrime()
+ {
+ BigInteger r = new BigInteger("3342403536405981729393488334694600415596881826869351677613");
+ BigInteger s = new BigInteger("5735822328888155254683894997897571951568553642892029982342");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("6140507067065001063065065565667405560006161556565665656654"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void decodeTest()
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECPoint p = curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")).normalize();
+
+ if (!p.getAffineXCoord().toBigInteger().equals(new BigInteger("188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", 16)))
+ {
+ fail("x uncompressed incorrectly");
+ }
+
+ if (!p.getAffineYCoord().toBigInteger().equals(new BigInteger("7192b95ffc8da78631011ed6b24cdd573f977a11e794811", 16)))
+ {
+ fail("y uncompressed incorrectly");
+ }
+
+ byte[] encoding = p.getEncoded();
+
+ if (!areEqual(encoding, Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")))
+ {
+ fail("point compressed incorrectly");
+ }
+ }
+
+ /**
+ * X9.62 - 1998,
+ * J.3.2, Page 155, ECDSA over the field Fp
+ * an example with 239 bit prime
+ */
+ private void testECDSA239bitPrime()
+ {
+ BigInteger r = new BigInteger("308636143175167811492622547300668018854959378758531778147462058306432176");
+ BigInteger s = new BigInteger("323813553209797357708078776831250505931891051755007842781978505179448783");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("700000017569056646655505781757157107570501575775705779575555657156756655"));
+
+ SecureRandom k = new FixedSecureRandom(true, kData);
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+
+ /**
+ * X9.62 - 1998,
+ * J.2.1, Page 100, ECDSA over the field F2m
+ * an example with 191 bit binary field
+ */
+ private void testECDSA191bitBinary()
+ {
+ BigInteger r = new BigInteger("87194383164871543355722284926904419997237591535066528048");
+ BigInteger s = new BigInteger("308992691965804947361541664549085895292153777025772063598");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("1542725565216523985789236956265265265235675811949404040041"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ ECCurve.F2m curve = new ECCurve.F2m(
+ 191, // m
+ 9, //k
+ new BigInteger("2866537B676752636A68F56554E12640276B649EF7526267", 16), // a
+ new BigInteger("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0436B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D765BE73433B3F95E332932E70EA245CA2418EA0EF98018FB")), // G
+ new BigInteger("1569275433846670190958947355803350458831205595451630533029"), // n
+ BigInteger.valueOf(2)); // h
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("1275552191113212300012030439187146164646146646466749494799"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("045DE37E756BD55D72E3768CB396FFEB962614DEA4CE28A2E755C0E0E02F5FB132CAF416EF85B229BBB8E1352003125BA1")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+
+ /**
+ * X9.62 - 1998,
+ * J.2.1, Page 100, ECDSA over the field F2m
+ * an example with 191 bit binary field
+ */
+ private void testECDSA239bitBinary()
+ {
+ BigInteger r = new BigInteger("21596333210419611985018340039034612628818151486841789642455876922391552");
+ BigInteger s = new BigInteger("197030374000731686738334997654997227052849804072198819102649413465737174");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ ECCurve.F2m curve = new ECCurve.F2m(
+ 239, // m
+ 36, //k
+ new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
+ new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
+ new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783"), // n
+ BigInteger.valueOf(4)); // h
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ // L 4.1 X9.62 2005
+ private void testECDSAP224sha224()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-224");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("6081831502424510080126737029209236539191290354021104541805484120491"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("15456715103636396133226117016818339719732885723579037388121116732601")));
+
+ byte[] M = Hex.decode("8797A3C693CC292441039A4E6BAB7387F3B4F2A63D00ED384B378C79");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("26477406756127720855365980332052585411804331993436302005017227573742");
+ BigInteger s = new BigInteger("17694958233103667059888193972742186995283044672015112738919822429978");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("03FD44EC11F9D43D9D23B1E1D1C9ED6519B40ECF0C79F48CF476CC43F1")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ private void testECDSASecP224k1sha256()
+ {
+ X9ECParameters p = SECNamedCurves.getByName("secp224k1");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("BE6F6E91FE96840A6518B56F3FE21689903A64FA729057AB872A9F51", 16), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(Hex.decode("00c39beac93db21c3266084429eb9b846b787c094f23a4de66447efbb3"));
+
+ byte[] M = Hex.decode("E5D5A7ADF73C5476FAEE93A2C76CE94DC0557DB04CDC189504779117920B896D");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("8163E5941BED41DA441B33E653C632A55A110893133351E20CE7CB75", 16);
+ BigInteger s = new BigInteger("D12C3FC289DDD5F6890DCE26B65792C8C50E68BF551D617D47DF15A8", 16);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("04C5C9B38D3603FCCD6994CBB9594E152B658721E483669BB42728520F484B537647EC816E58A8284D3B89DFEDB173AFDC214ECA95A836FA7C")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ // L4.2 X9.62 2005
+ private void testECDSAP256sha256()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-256");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("20186677036482506117540275567393538695075300175221296989956723148347484984008"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("72546832179840998877302529996971396893172522460793442785601695562409154906335")));
+
+ byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("97354732615802252173078420023658453040116611318111190383344590814578738210384");
+ BigInteger s = new BigInteger("98506158880355671805367324764306888225238061309262649376965428126566081727535");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("03596375E6CE57E0F20294FC46BDFCFD19A39F8161B58695B3EC5B3D16427C274D")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ private void testECDSAP224OneByteOver()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-224");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("6081831502424510080126737029209236539191290354021104541805484120491"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("15456715103636396133226117016818339719732885723579037388121116732601")));
+
+ byte[] M = Hex.decode("8797A3C693CC292441039A4E6BAB7387F3B4F2A63D00ED384B378C79FF");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("26477406756127720855365980332052585411804331993436302005017227573742");
+ BigInteger s = new BigInteger("17694958233103667059888193972742186995283044672015112738919822429978");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("03FD44EC11F9D43D9D23B1E1D1C9ED6519B40ECF0C79F48CF476CC43F1")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ // L4.3 X9.62 2005
+ private void testECDSAP521sha512()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-521");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("617573726813476282316253885608633222275541026607493641741273231656161177732180358888434629562647985511298272498852936680947729040673640492310550142822667389"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("6806532878215503520845109818432174847616958675335397773700324097584974639728725689481598054743894544060040710846048585856076812050552869216017728862957612913")));
+
+ byte[] M = Hex.decode("6893B64BD3A9615C39C3E62DDD269C2BAAF1D85915526083183CE14C2E883B48B193607C1ED871852C9DF9C3147B574DC1526C55DE1FE263A676346A20028A66");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("1368926195812127407956140744722257403535864168182534321188553460365652865686040549247096155740756318290773648848859639978618869784291633651685766829574104630");
+ BigInteger s = new BigInteger("1624754720348883715608122151214003032398685415003935734485445999065609979304811509538477657407457976246218976767156629169821116579317401249024208611945405790");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("020145E221AB9F71C5FE740D8D2B94939A09E2816E2167A7D058125A06A80C014F553E8D6764B048FB6F2B687CEC72F39738F223D4CE6AFCBFF2E34774AA5D3C342CB3")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * General test for long digest.
+ */
+ private void testECDSA239bitBinaryAndLargeDigest()
+ {
+ BigInteger r = new BigInteger("21596333210419611985018340039034612628818151486841789642455876922391552");
+ BigInteger s = new BigInteger("144940322424411242416373536877786566515839911620497068645600824084578597");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ ECCurve.F2m curve = new ECCurve.F2m(
+ 239, // m
+ 36, //k
+ new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
+ new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
+ new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783"), // n
+ BigInteger.valueOf(4)); // h
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517968236873715988614170569073515315707566766479517968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * key generation test
+ */
+ private void testECDSAKeyGenTest()
+ {
+ SecureRandom random = new SecureRandom();
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ ecdsa.init(false, pair.getPublic());
+
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Basic Key Agreement Test
+ */
+ private void testECBasicAgreementTest()
+ {
+ SecureRandom random = new SecureRandom();
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair p1 = pGen.generateKeyPair();
+ AsymmetricCipherKeyPair p2 = pGen.generateKeyPair();
+
+ //
+ // two way
+ //
+ BasicAgreement e1 = new ECDHBasicAgreement();
+ BasicAgreement e2 = new ECDHBasicAgreement();
+
+ e1.init(p1.getPrivate());
+ e2.init(p2.getPrivate());
+
+ BigInteger k1 = e1.calculateAgreement(p2.getPublic());
+ BigInteger k2 = e2.calculateAgreement(p1.getPublic());
+
+ if (!k1.equals(k2))
+ {
+ fail("calculated agreement test failed");
+ }
+
+ //
+ // two way
+ //
+ e1 = new ECDHCBasicAgreement();
+ e2 = new ECDHCBasicAgreement();
+
+ e1.init(p1.getPrivate());
+ e2.init(p2.getPrivate());
+
+ k1 = e1.calculateAgreement(p2.getPublic());
+ k2 = e2.calculateAgreement(p1.getPublic());
+
+ if (!k1.equals(k2))
+ {
+ fail("calculated agreement test failed");
+ }
+ }
+
+ private void testECMQVTestVector1()
+ {
+ // Test Vector from GEC-2
+
+ X9ECParameters x9 = SECNamedCurves.getByName("secp160r1");
+ ECDomainParameters p = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+ AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("0251B4496FECC406ED0E75A24A3C03206251419DC0")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("AA374FFC3CE144E6B073307972CB6D57B2A4E982", 16), p));
+
+ AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03D99CE4D8BF52FA20BD21A962C6556B0F71F4CA1F")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("149EC7EA3A220A887619B3F9E5B4CA51C7D1779C", 16), p));
+
+ AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("0349B41E0E9C0369C2328739D90F63D56707C6E5BC")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("45FB58A92A17AD4B15101C66E74F277E2B460866", 16), p));
+
+ AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("02706E5D6E1F640C6E9C804E75DBC14521B1E5F3B5")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("18C13FCED9EADF884F7C595C8CB565DEFD0CB41E", 16), p));
+
+ BigInteger x = calculateAgreement(U1, U2, V1, V2);
+
+ if (x == null
+ || !x.equals(new BigInteger("5A6955CEFDB4E43255FB7FCF718611E4DF8E05AC", 16)))
+ {
+ fail("MQV Test Vector #1 agreement failed");
+ }
+ }
+
+ private void testECMQVTestVector2()
+ {
+ // Test Vector from GEC-2
+
+ X9ECParameters x9 = SECNamedCurves.getByName("sect163k1");
+ ECDomainParameters p = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+ AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03037D529FA37E42195F10111127FFB2BB38644806BC")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("03A41434AA99C2EF40C8495B2ED9739CB2155A1E0D", 16), p));
+
+ AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("02015198E74BC2F1E5C9A62B80248DF0D62B9ADF8429")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("032FC4C61A8211E6A7C4B8B0C03CF35F7CF20DBD52", 16), p));
+
+ AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03072783FAAB9549002B4F13140B88132D1C75B3886C")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("57E8A78E842BF4ACD5C315AA0569DB1703541D96", 16), p));
+
+ AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03067E3AEA3510D69E8EDD19CB2A703DDC6CF5E56E32")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("02BD198B83A667A8D908EA1E6F90FD5C6D695DE94F", 16), p));
+
+ BigInteger x = calculateAgreement(U1, U2, V1, V2);
+
+ if (x == null
+ || !x.equals(new BigInteger("038359FFD30C0D5FC1E6154F483B73D43E5CF2B503", 16)))
+ {
+ fail("MQV Test Vector #2 agreement failed");
+ }
+ }
+
+ private void testECMQVRandom()
+ {
+ SecureRandom random = new SecureRandom();
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
+
+ ECDomainParameters parameters = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+
+ pGen.init(new ECKeyGenerationParameters(parameters, random));
+
+
+ // Pre-established key pairs
+ AsymmetricCipherKeyPair U1 = pGen.generateKeyPair();
+ AsymmetricCipherKeyPair V1 = pGen.generateKeyPair();
+
+ // Ephemeral key pairs
+ AsymmetricCipherKeyPair U2 = pGen.generateKeyPair();
+ AsymmetricCipherKeyPair V2 = pGen.generateKeyPair();
+
+ BigInteger x = calculateAgreement(U1, U2, V1, V2);
+
+ if (x == null)
+ {
+ fail("MQV Test Vector (random) agreement failed");
+ }
+ }
+
+ private static BigInteger calculateAgreement(
+ AsymmetricCipherKeyPair U1,
+ AsymmetricCipherKeyPair U2,
+ AsymmetricCipherKeyPair V1,
+ AsymmetricCipherKeyPair V2)
+ {
+ ECMQVBasicAgreement u = new ECMQVBasicAgreement();
+ u.init(new MQVPrivateParameters(
+ (ECPrivateKeyParameters)U1.getPrivate(),
+ (ECPrivateKeyParameters)U2.getPrivate(),
+ (ECPublicKeyParameters)U2.getPublic()));
+ BigInteger ux = u.calculateAgreement(new MQVPublicParameters(
+ (ECPublicKeyParameters)V1.getPublic(),
+ (ECPublicKeyParameters)V2.getPublic()));
+
+ ECMQVBasicAgreement v = new ECMQVBasicAgreement();
+ v.init(new MQVPrivateParameters(
+ (ECPrivateKeyParameters)V1.getPrivate(),
+ (ECPrivateKeyParameters)V2.getPrivate(),
+ (ECPublicKeyParameters)V2.getPublic()));
+ BigInteger vx = v.calculateAgreement(new MQVPublicParameters(
+ (ECPublicKeyParameters)U1.getPublic(),
+ (ECPublicKeyParameters)U2.getPublic()));
+
+ if (ux.equals(vx))
+ {
+ return ux;
+ }
+
+ return null;
+ }
+
+ public String getName()
+ {
+ return "EC";
+ }
+
+ public void performTest()
+ {
+ decodeTest();
+ testECDSA192bitPrime();
+ testECDSA239bitPrime();
+ testECDSA191bitBinary();
+ testECDSA239bitBinary();
+ testECDSAKeyGenTest();
+ testECBasicAgreementTest();
+
+ testECDSAP224sha224();
+ testECDSAP224OneByteOver();
+ testECDSAP256sha256();
+ testECDSAP521sha512();
+ testECDSASecP224k1sha256();
+ testECDSA239bitBinaryAndLargeDigest();
+
+ testECMQVTestVector1();
+ testECMQVTestVector2();
+ testECMQVRandom();
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECTest());
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ElGamalTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ElGamalTest.java
new file mode 100644
index 000000000..7fdf500ef
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ElGamalTest.java
@@ -0,0 +1,260 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.engines.ElGamalEngine;
+import org.spongycastle.crypto.generators.ElGamalKeyPairGenerator;
+import org.spongycastle.crypto.generators.ElGamalParametersGenerator;
+import org.spongycastle.crypto.params.ElGamalKeyGenerationParameters;
+import org.spongycastle.crypto.params.ElGamalParameters;
+import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.spongycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.test.SimpleTest;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.Arrays;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class ElGamalTest
+ extends SimpleTest
+{
+ private BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ private BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ private BigInteger g768 = new BigInteger("7c240073c1316c621df461b71ebb0cdcc90a6e5527e5e126633d131f87461c4dc4afc60c2cb0f053b6758871489a69613e2a8b4c8acde23954c08c81cbd36132cfd64d69e4ed9f8e51ed6e516297206672d5c0a69135df0a5dcf010d289a9ca1", 16);
+ private BigInteger p768 = new BigInteger("8c9dd223debed1b80103b8b309715be009d48860ed5ae9b9d5d8159508efd802e3ad4501a7f7e1cfec78844489148cd72da24b21eddd01aa624291c48393e277cfc529e37075eccef957f3616f962d15b44aeab4039d01b817fde9eaa12fd73f", 16);
+
+ private BigInteger g1024 = new BigInteger("1db17639cdf96bc4eabba19454f0b7e5bd4e14862889a725c96eb61048dcd676ceb303d586e30f060dbafd8a571a39c4d823982117da5cc4e0f89c77388b7a08896362429b94a18a327604eb7ff227bffbc83459ade299e57b5f77b50fb045250934938efa145511166e3197373e1b5b1e52de713eb49792bedde722c6717abf", 16);
+ private BigInteger p1024 = new BigInteger("a00e283b3c624e5b2b4d9fbc2653b5185d99499b00fd1bf244c6f0bb817b4d1c451b2958d62a0f8a38caef059fb5ecd25d75ed9af403f5b5bdab97a642902f824e3c13789fed95fa106ddfe0ff4a707c85e2eb77d49e68f2808bcea18ce128b178cd287c6bc00efa9a1ad2a673fe0dceace53166f75b81d6709d5f8af7c66bb7", 16);
+
+ private BigInteger yPgpBogusPSamp = new BigInteger("de4688497cc05b45fe8559bc9918c45afcad69b74123a7236eba409fd9de8ea34c7869839ee9df35e3d97576145d089841aa65b5b4e061fae52c37e430354269a02496b8ed8456f2d0d7c9b0db985fbcb21ae9f78507ed6e3a29db595b201b1a4f931c7d791eede65ccf918e8a61cf146859151c78c41ad48853694623467d78", 16);
+ private BigInteger xPgpBogusPSamp = new BigInteger("cbaf780f2cfe4f987bbc5fcb0738bbd7912060ccfdf37cbfeea65c0fd857e74a8df6cc359375f28cf5725d081813c614410a78cbe4b06d677beea9ff0fa10b1dbc47a6ed8c5b8466d6a95d6574029dbdf72596392e1b6b230faf9916dc8455821c10527a375a4d1c8a54947d1fe714d321aca25ad486b4b456506999fd2fd11a", 16);
+ private BigInteger gPgpBogusPSamp = new BigInteger("153ffe9522076d1cbd6e75f0816a0fc2ebd8b0e0091406587387a1763022088a03b411eed07ff50efb82b21f1608c352d10f63ba7e7e981a2f3387cec8af2915953d00493857663ae8919f517fe90f1d2abe7af4305a344b10d1a25d75f65902cd7fd775853d3ac43d7c5253ad666e1e63ee98cdcb10af81273d4ff053ff07d51", 16);
+ private BigInteger pPgpBogusPSamp = new BigInteger("15061b26cdab4e865098a01c86f13b03220104c5443e950658b36b85245aa0c616a0c0d8d99c454bea087c172315e45b3bc9b925443948a2b6ba47608a6035b9a79a4ef34a78d7274a12ede8364f02d5030db864988643d7e92753df603bd69fbd2682ab0af64d1a866d1131a2cb13333cedb0a9e6eefddd9fff8154d34c2daab", 16);
+ private int lPgpBogusPSamp = 0;
+
+ public String getName()
+ {
+ return "ElGamal";
+ }
+
+ private void testEnc(
+ int size,
+ int privateValueSize,
+ BigInteger g,
+ BigInteger p)
+ {
+ ElGamalParameters dhParams = new ElGamalParameters(p, g, privateValueSize);
+ ElGamalKeyGenerationParameters params = new ElGamalKeyGenerationParameters(new SecureRandom(), dhParams);
+ ElGamalKeyPairGenerator kpGen = new ElGamalKeyPairGenerator();
+
+ kpGen.init(params);
+
+ //
+ // generate pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ ElGamalPublicKeyParameters pu = (ElGamalPublicKeyParameters)pair.getPublic();
+ ElGamalPrivateKeyParameters pv = (ElGamalPrivateKeyParameters)pair.getPrivate();
+
+ checkKeySize(privateValueSize, pv);
+
+ ElGamalEngine e = new ElGamalEngine();
+
+ e.init(true, pu);
+
+ if (e.getOutputBlockSize() != size / 4)
+ {
+ fail(size + " getOutputBlockSize() on encryption failed.");
+ }
+
+ byte[] message = Hex.decode("5468697320697320612074657374");
+
+ byte[] pText = message;
+ byte[] cText = e.processBlock(pText, 0, pText.length);
+
+ e.init(false, pv);
+
+ if (e.getOutputBlockSize() != (size / 8) - 1)
+ {
+ fail(size + " getOutputBlockSize() on decryption failed.");
+ }
+
+ pText = e.processBlock(cText, 0, cText.length);
+
+ if (!Arrays.areEqual(message, pText))
+ {
+ fail(size + " bit test failed");
+ }
+
+ e.init(true, pu);
+
+ byte[] bytes = new byte[e.getInputBlockSize() + 2];
+
+ try
+ {
+ e.processBlock(bytes, 0, bytes.length);
+
+ fail("out of range block not detected");
+ }
+ catch (DataLengthException ex)
+ {
+ // expected
+ }
+
+ try
+ {
+ bytes[0] = (byte)0xff;
+
+ e.processBlock(bytes, 0, bytes.length - 1);
+
+ fail("out of range block not detected");
+ }
+ catch (DataLengthException ex)
+ {
+ // expected
+ }
+
+ try
+ {
+ bytes[0] = (byte)0x7f;
+
+ e.processBlock(bytes, 0, bytes.length - 1);
+ }
+ catch (DataLengthException ex)
+ {
+ fail("in range block failed");
+ }
+ }
+
+ private void checkKeySize(
+ int privateValueSize,
+ ElGamalPrivateKeyParameters priv)
+ {
+ if (privateValueSize != 0)
+ {
+ if (priv.getX().bitLength() != privateValueSize)
+ {
+ fail("limited key check failed for key size " + privateValueSize);
+ }
+ }
+ }
+
+ /**
+ * this test is can take quiet a while
+ *
+ * @param size size of key in bits.
+ */
+ private void testGeneration(
+ int size)
+ {
+ ElGamalParametersGenerator pGen = new ElGamalParametersGenerator();
+
+ pGen.init(size, 10, new SecureRandom());
+
+ ElGamalParameters elParams = pGen.generateParameters();
+
+ if (elParams.getL() != 0)
+ {
+ fail("ElGamalParametersGenerator failed to set L to 0 in generated ElGamalParameters");
+ }
+
+ ElGamalKeyGenerationParameters params = new ElGamalKeyGenerationParameters(new SecureRandom(), elParams);
+
+ ElGamalKeyPairGenerator kpGen = new ElGamalKeyPairGenerator();
+
+ kpGen.init(params);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ ElGamalPublicKeyParameters pu = (ElGamalPublicKeyParameters)pair.getPublic();
+ ElGamalPrivateKeyParameters pv = (ElGamalPrivateKeyParameters)pair.getPrivate();
+
+ ElGamalEngine e = new ElGamalEngine();
+
+ e.init(true, new ParametersWithRandom(pu, new SecureRandom()));
+
+ byte[] message = Hex.decode("5468697320697320612074657374");
+
+ byte[] pText = message;
+ byte[] cText = e.processBlock(pText, 0, pText.length);
+
+ e.init(false, pv);
+
+ pText = e.processBlock(cText, 0, cText.length);
+
+ if (!Arrays.areEqual(message, pText))
+ {
+ fail("generation test failed");
+ }
+ }
+
+ private void testInitCheck()
+ {
+ try
+ {
+ new ElGamalEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ }
+
+ private void testInvalidP()
+ {
+ ElGamalParameters dhParams = new ElGamalParameters(pPgpBogusPSamp, gPgpBogusPSamp, lPgpBogusPSamp);
+ ElGamalPublicKeyParameters pu = new ElGamalPublicKeyParameters(yPgpBogusPSamp, dhParams);
+ ElGamalPrivateKeyParameters pv = new ElGamalPrivateKeyParameters(xPgpBogusPSamp, dhParams);
+
+ ElGamalEngine e = new ElGamalEngine();
+
+ e.init(true, pu);
+
+ byte[] message = Hex.decode("5468697320697320612074657374");
+
+ byte[] pText = message;
+ byte[] cText = e.processBlock(pText, 0, pText.length);
+
+ e.init(false, pv);
+
+ pText = e.processBlock(cText, 0, cText.length);
+
+ if (Arrays.areEqual(message, pText))
+ {
+ fail("invalid test failed");
+ }
+ }
+
+ public void performTest()
+ {
+ testInvalidP();
+
+ testEnc(512, 0, g512, p512);
+ testEnc(768, 0, g768, p768);
+ testEnc(1024, 0, g1024, p1024);
+
+ testEnc(512, 64, g512, p512);
+ testEnc(768, 128, g768, p768);
+
+ //
+ // generation test.
+ //
+ testGeneration(258);
+
+ testInitCheck();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ElGamalTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/EqualsHashCodeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/EqualsHashCodeTest.java
new file mode 100644
index 000000000..3966f8e84
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/EqualsHashCodeTest.java
@@ -0,0 +1,261 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.generators.DHKeyPairGenerator;
+import org.spongycastle.crypto.generators.ElGamalKeyPairGenerator;
+import org.spongycastle.crypto.params.DHKeyGenerationParameters;
+import org.spongycastle.crypto.params.DHKeyParameters;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.crypto.params.DHPrivateKeyParameters;
+import org.spongycastle.crypto.params.DHPublicKeyParameters;
+import org.spongycastle.crypto.params.DHValidationParameters;
+import org.spongycastle.crypto.params.DSAParameters;
+import org.spongycastle.crypto.params.DSAValidationParameters;
+import org.spongycastle.crypto.params.ElGamalKeyGenerationParameters;
+import org.spongycastle.crypto.params.ElGamalKeyParameters;
+import org.spongycastle.crypto.params.ElGamalParameters;
+import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.spongycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.spongycastle.crypto.params.GOST3410Parameters;
+import org.spongycastle.crypto.params.GOST3410ValidationParameters;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+class DHTestKeyParameters
+ extends DHKeyParameters
+{
+ protected DHTestKeyParameters(boolean isPrivate, DHParameters params)
+ {
+ super(isPrivate, params);
+ }
+}
+
+class ElGamalTestKeyParameters
+ extends ElGamalKeyParameters
+{
+ protected ElGamalTestKeyParameters(boolean isPrivate, ElGamalParameters params)
+ {
+ super(isPrivate, params);
+ }
+}
+
+public class EqualsHashCodeTest
+ extends SimpleTest
+{
+ private static Object OTHER = new Object();
+
+ public String getName()
+ {
+ return "EqualsHashCode";
+ }
+
+ private void doTest(Object a, Object equalsA, Object notEqualsA)
+ {
+ if (a.equals(null))
+ {
+ fail("a equaled null");
+ }
+
+ if (!a.equals(equalsA) || !equalsA.equals(a))
+ {
+ fail("equality failed");
+ }
+
+ if (a.equals(OTHER))
+ {
+ fail("other inequality failed");
+ }
+
+ if (a.equals(notEqualsA) || notEqualsA.equals(a))
+ {
+ fail("inequality failed");
+ }
+
+ if (a.hashCode() != equalsA.hashCode())
+ {
+ fail("hashCode equality failed");
+ }
+ }
+
+ private void dhTest()
+ {
+ BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ DHParameters dhParams = new DHParameters(p512, g512);
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams); DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
+
+ kpGen.init(params);
+
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ DHPublicKeyParameters pu2 = new DHPublicKeyParameters(pu1.getY(), pu1.getParameters());
+ DHPrivateKeyParameters pv2 = new DHPrivateKeyParameters(pv1.getX(), pv1.getParameters());
+ DHPublicKeyParameters pu3 = new DHPublicKeyParameters(pv1.getX(), pu1.getParameters());
+ DHPrivateKeyParameters pv3 = new DHPrivateKeyParameters(pu1.getY(), pu1.getParameters());
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ DHParameters pr1 = pu1.getParameters();
+ DHParameters pr2 = new DHParameters(pr1.getP(), pr1.getG(), pr1.getQ(), pr1.getM(), pr1.getL(), pr1.getJ(), pr1.getValidationParameters());
+ DHParameters pr3 = new DHParameters(pr1.getG(), pr1.getP(), pr1.getQ(), pr1.getM(), pr1.getL(), pr1.getJ(), pr1.getValidationParameters());
+
+ doTest(pr1, pr2, pr3);
+
+ pr3 = new DHParameters(pr1.getG(), pr1.getP(), null, pr1.getM(), pr1.getL(), pr1.getJ(), pr1.getValidationParameters());
+
+ doTest(pr1, pr2, pr3);
+
+ pu2 = new DHPublicKeyParameters(pu1.getY(), pr2);
+ pv2 = new DHPrivateKeyParameters(pv1.getX(), pr2);
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ DHValidationParameters vp1 = new DHValidationParameters(new byte[20], 1024);
+ DHValidationParameters vp2 = new DHValidationParameters(new byte[20], 1024);
+ DHValidationParameters vp3 = new DHValidationParameters(new byte[24], 1024);
+
+ doTest(vp1, vp1, vp3);
+ doTest(vp1, vp2, vp3);
+
+ byte[] bytes = new byte[20];
+ bytes[0] = 1;
+
+ vp3 = new DHValidationParameters(bytes, 1024);
+
+ doTest(vp1, vp2, vp3);
+
+ vp3 = new DHValidationParameters(new byte[20], 2048);
+
+ doTest(vp1, vp2, vp3);
+
+ DHTestKeyParameters k1 = new DHTestKeyParameters(false, null);
+ DHTestKeyParameters k2 = new DHTestKeyParameters(false, null);
+ DHTestKeyParameters k3 = new DHTestKeyParameters(false, pu1.getParameters());
+
+ doTest(k1, k2, k3);
+ }
+
+ private void elGamalTest()
+ {
+ BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ ElGamalParameters dhParams = new ElGamalParameters(p512, g512);
+ ElGamalKeyGenerationParameters params = new ElGamalKeyGenerationParameters(new SecureRandom(), dhParams); ElGamalKeyPairGenerator kpGen = new ElGamalKeyPairGenerator();
+
+ kpGen.init(params);
+
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+ ElGamalPublicKeyParameters pu1 = (ElGamalPublicKeyParameters)pair.getPublic();
+ ElGamalPrivateKeyParameters pv1 = (ElGamalPrivateKeyParameters)pair.getPrivate();
+
+ ElGamalPublicKeyParameters pu2 = new ElGamalPublicKeyParameters(pu1.getY(), pu1.getParameters());
+ ElGamalPrivateKeyParameters pv2 = new ElGamalPrivateKeyParameters(pv1.getX(), pv1.getParameters());
+ ElGamalPublicKeyParameters pu3 = new ElGamalPublicKeyParameters(pv1.getX(), pu1.getParameters());
+ ElGamalPrivateKeyParameters pv3 = new ElGamalPrivateKeyParameters(pu1.getY(), pu1.getParameters());
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ ElGamalParameters pr1 = pu1.getParameters();
+ ElGamalParameters pr2 = new ElGamalParameters(pr1.getP(), pr1.getG());
+ ElGamalParameters pr3 = new ElGamalParameters(pr1.getG(), pr1.getP());
+
+ doTest(pr1, pr2, pr3);
+
+ pu2 = new ElGamalPublicKeyParameters(pu1.getY(), pr2);
+ pv2 = new ElGamalPrivateKeyParameters(pv1.getX(), pr2);
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ ElGamalTestKeyParameters k1 = new ElGamalTestKeyParameters(false, null);
+ ElGamalTestKeyParameters k2 = new ElGamalTestKeyParameters(false, null);
+ ElGamalTestKeyParameters k3 = new ElGamalTestKeyParameters(false, pu1.getParameters());
+
+ doTest(k1, k2, k3);
+ }
+
+ private void dsaTest()
+ {
+ BigInteger a = BigInteger.valueOf(1), b = BigInteger.valueOf(2), c = BigInteger.valueOf(3);
+
+ DSAParameters dsaP1 = new DSAParameters(a, b, c);
+ DSAParameters dsaP2 = new DSAParameters(a, b, c);
+ DSAParameters dsaP3 = new DSAParameters(b, c, a);
+
+ doTest(dsaP1, dsaP2, dsaP3);
+
+ DSAValidationParameters vp1 = new DSAValidationParameters(new byte[20], 1024);
+ DSAValidationParameters vp2 = new DSAValidationParameters(new byte[20], 1024);
+ DSAValidationParameters vp3 = new DSAValidationParameters(new byte[24], 1024);
+
+ doTest(vp1, vp1, vp3);
+ doTest(vp1, vp2, vp3);
+
+ byte[] bytes = new byte[20];
+ bytes[0] = 1;
+
+ vp3 = new DSAValidationParameters(bytes, 1024);
+
+ doTest(vp1, vp2, vp3);
+
+ vp3 = new DSAValidationParameters(new byte[20], 2048);
+
+ doTest(vp1, vp2, vp3);
+ }
+
+ private void gost3410Test()
+ {
+ BigInteger a = BigInteger.valueOf(1), b = BigInteger.valueOf(2), c = BigInteger.valueOf(3);
+
+ GOST3410Parameters g1 = new GOST3410Parameters(a, b, c);
+ GOST3410Parameters g2 = new GOST3410Parameters(a, b, c);
+ GOST3410Parameters g3 = new GOST3410Parameters(a, c, c);
+
+ doTest(g1, g2, g3);
+
+ GOST3410ValidationParameters v1 = new GOST3410ValidationParameters(100, 1);
+ GOST3410ValidationParameters v2 = new GOST3410ValidationParameters(100, 1);
+ GOST3410ValidationParameters v3 = new GOST3410ValidationParameters(101, 1);
+
+ doTest(v1, v2, v3);
+
+ v3 = new GOST3410ValidationParameters(100, 2);
+
+ doTest(v1, v2, v3);
+
+ v1 = new GOST3410ValidationParameters(100L, 1L);
+ v2 = new GOST3410ValidationParameters(100L, 1L);
+ v3 = new GOST3410ValidationParameters(101L, 1L);
+
+ doTest(v1, v2, v3);
+
+ v3 = new GOST3410ValidationParameters(100L, 2L);
+
+ doTest(v1, v2, v3);
+
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ dhTest();
+ elGamalTest();
+ gost3410Test();
+ dsaTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new EqualsHashCodeTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GCMReorderTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GCMReorderTest.java
new file mode 100644
index 000000000..f946f2876
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GCMReorderTest.java
@@ -0,0 +1,348 @@
+package org.spongycastle.crypto.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.spongycastle.crypto.modes.gcm.GCMExponentiator;
+import org.spongycastle.crypto.modes.gcm.GCMMultiplier;
+import org.spongycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
+import org.spongycastle.crypto.modes.gcm.Tables64kGCMMultiplier;
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+
+public class GCMReorderTest
+ extends TestCase
+{
+ private static final byte[] H;
+ private static final SecureRandom random = new SecureRandom();
+ private static final GCMMultiplier mul = new Tables64kGCMMultiplier();
+ private static final GCMExponentiator exp = new Tables1kGCMExponentiator();
+ private static final byte[] EMPTY = new byte[0];
+
+ static
+ {
+ H = new byte[16];
+ random.nextBytes(H);
+ mul.init(Arrays.clone(H));
+ exp.init(Arrays.clone(H));
+ }
+
+ public void testCombine() throws Exception
+ {
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] A = randomBytes(1000);
+ byte[] C = randomBytes(1000);
+
+ byte[] ghashA_ = GHASH(A, EMPTY);
+ byte[] ghash_C = GHASH(EMPTY, C);
+ byte[] ghashAC = GHASH(A, C);
+
+ byte[] ghashCombine = combine_GHASH(ghashA_, (long)A.length * 8, ghash_C, (long)C.length * 8);
+
+ assertTrue(Arrays.areEqual(ghashAC, ghashCombine));
+ }
+ }
+
+ public void testConcatAuth() throws Exception
+ {
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] P = randomBlocks(100);
+ byte[] A = randomBytes(1000);
+ byte[] PA = concatArrays(P, A);
+
+ byte[] ghashP_ = GHASH(P, EMPTY);
+ byte[] ghashA_ = GHASH(A, EMPTY);
+ byte[] ghashPA_ = GHASH(PA, EMPTY);
+ byte[] ghashConcat = concatAuth_GHASH(ghashP_, (long)P.length * 8, ghashA_, (long)A.length * 8);
+
+ assertTrue(Arrays.areEqual(ghashPA_, ghashConcat));
+ }
+ }
+
+ public void testConcatCrypt() throws Exception
+ {
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] P = randomBlocks(100);
+ byte[] A = randomBytes(1000);
+ byte[] PA = concatArrays(P, A);
+
+ byte[] ghash_P = GHASH(EMPTY, P);
+ byte[] ghash_A = GHASH(EMPTY, A);
+ byte[] ghash_PA = GHASH(EMPTY, PA);
+ byte[] ghashConcat = concatCrypt_GHASH(ghash_P, (long)P.length * 8, ghash_A, (long)A.length * 8);
+
+ assertTrue(Arrays.areEqual(ghash_PA, ghashConcat));
+ }
+ }
+
+ public void testExp()
+ {
+ {
+ byte[] buf1 = new byte[16];
+ buf1[0] = (byte)0x80;
+
+ byte[] buf2 = new byte[16];
+
+ for (int pow = 0; pow != 100; ++pow)
+ {
+ exp.exponentiateX(pow, buf2);
+
+ assertTrue(Arrays.areEqual(buf1, buf2));
+
+ mul.multiplyH(buf1);
+ }
+ }
+
+ long[] testPow = new long[]{ 10, 1, 8, 17, 24, 13, 2, 13, 2, 3 };
+ byte[][] testData = new byte[][]{
+ Hex.decode("9185848a877bd87ba071e281f476e8e7"),
+ Hex.decode("697ce3052137d80745d524474fb6b290"),
+ Hex.decode("2696fc47198bb23b11296e4f88720a17"),
+ Hex.decode("01f2f0ead011a4ae0cf3572f1b76dd8e"),
+ Hex.decode("a53060694a044e4b7fa1e661c5a7bb6b"),
+ Hex.decode("39c0392e8b6b0e04a7565c85394c2c4c"),
+ Hex.decode("519c362d502e07f2d8b7597a359a5214"),
+ Hex.decode("5a527a393675705e19b2117f67695af4"),
+ Hex.decode("27fc0901d1d332a53ba4d4386c2109d2"),
+ Hex.decode("93ca9b57174aabedf8220e83366d7df6"),
+ };
+
+ for (int i = 0; i != 10; ++i)
+ {
+ long pow = testPow[i];
+ byte[] data = Arrays.clone(testData[i]);
+
+ byte[] expected = Arrays.clone(data);
+ for (int j = 0; j < pow; ++j)
+ {
+ mul.multiplyH(expected);
+ }
+
+ byte[] H_a = new byte[16];
+ exp.exponentiateX(pow, H_a);
+ byte[] actual = multiply(data, H_a);
+
+ assertTrue(Arrays.areEqual(expected, actual));
+ }
+ }
+
+ public void testMultiply()
+ {
+ byte[] expected = Arrays.clone(H);
+ mul.multiplyH(expected);
+
+ assertTrue(Arrays.areEqual(expected, multiply(H, H)));
+
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] a = new byte[16];
+ random.nextBytes(a);
+
+ byte[] b = new byte[16];
+ random.nextBytes(b);
+
+ expected = Arrays.clone(a);
+ mul.multiplyH(expected);
+ assertTrue(Arrays.areEqual(expected, multiply(a, H)));
+ assertTrue(Arrays.areEqual(expected, multiply(H, a)));
+
+ expected = Arrays.clone(b);
+ mul.multiplyH(expected);
+ assertTrue(Arrays.areEqual(expected, multiply(b, H)));
+ assertTrue(Arrays.areEqual(expected, multiply(H, b)));
+
+ assertTrue(Arrays.areEqual(multiply(a, b), multiply(b, a)));
+ }
+ }
+
+ private byte[] randomBlocks(int upper)
+ {
+ byte[] bs = new byte[16 * random.nextInt(upper)];
+ random.nextBytes(bs);
+ return bs;
+ }
+
+ private byte[] randomBytes(int upper)
+ {
+ byte[] bs = new byte[random.nextInt(upper)];
+ random.nextBytes(bs);
+ return bs;
+ }
+
+ private byte[] concatArrays(byte[] a, byte[] b) throws IOException
+ {
+ byte[] ab = new byte[a.length + b.length];
+ System.arraycopy(a, 0, ab, 0, a.length);
+ System.arraycopy(b, 0, ab, a.length, b.length);
+ return ab;
+ }
+
+ private byte[] combine_GHASH(byte[] ghashA_, long bitlenA, byte[] ghash_C, long bitlenC)
+ {
+ // Note: bitlenA must be aligned to the block size
+
+ long c = (bitlenC + 127) >>> 7;
+
+ byte[] H_c = new byte[16];
+ exp.exponentiateX(c, H_c);
+
+ byte[] tmp1 = lengthBlock(bitlenA, 0);
+ mul.multiplyH(tmp1);
+
+ byte[] ghashAC = Arrays.clone(ghashA_);
+ xor(ghashAC, tmp1);
+ ghashAC = multiply(ghashAC, H_c);
+ // No need to touch the len(C) part (second 8 bytes)
+ xor(ghashAC, tmp1);
+ xor(ghashAC, ghash_C);
+
+ return ghashAC;
+ }
+
+ private byte[] concatAuth_GHASH(byte[] ghashP, long bitlenP, byte[] ghashA, long bitlenA)
+ {
+ // Note: bitlenP must be aligned to the block size
+
+ long a = (bitlenA + 127) >>> 7;
+
+ byte[] tmp1 = lengthBlock(bitlenP, 0);
+ mul.multiplyH(tmp1);
+
+ byte[] tmp2 = lengthBlock(bitlenA ^ (bitlenP + bitlenA), 0);
+ mul.multiplyH(tmp2);
+
+ byte[] H_a = new byte[16];
+ exp.exponentiateX(a, H_a);
+
+ byte[] ghashC = Arrays.clone(ghashP);
+ xor(ghashC, tmp1);
+ ghashC = multiply(ghashC, H_a);
+ xor(ghashC, tmp2);
+ xor(ghashC, ghashA);
+ return ghashC;
+ }
+
+ private byte[] concatCrypt_GHASH(byte[] ghashP, long bitlenP, byte[] ghashA, long bitlenA)
+ {
+ // Note: bitlenP must be aligned to the block size
+
+ long a = (bitlenA + 127) >>> 7;
+
+ byte[] tmp1 = lengthBlock(0, bitlenP);
+ mul.multiplyH(tmp1);
+
+ byte[] tmp2 = lengthBlock(0, bitlenA ^ (bitlenP + bitlenA));
+ mul.multiplyH(tmp2);
+
+ byte[] H_a = new byte[16];
+ exp.exponentiateX(a, H_a);
+
+ byte[] ghashC = Arrays.clone(ghashP);
+ xor(ghashC, tmp1);
+ ghashC = multiply(ghashC, H_a);
+ xor(ghashC, tmp2);
+ xor(ghashC, ghashA);
+ return ghashC;
+ }
+
+ private byte[] GHASH(byte[] A, byte[] C)
+ {
+ byte[] X = new byte[16];
+
+ {
+ for (int pos = 0; pos < A.length; pos += 16)
+ {
+ byte[] tmp = new byte[16];
+ int num = Math.min(A.length - pos, 16);
+ System.arraycopy(A, pos, tmp, 0, num);
+ xor(X, tmp);
+ mul.multiplyH(X);
+ }
+ }
+
+ {
+ for (int pos = 0; pos < C.length; pos += 16)
+ {
+ byte[] tmp = new byte[16];
+ int num = Math.min(C.length - pos, 16);
+ System.arraycopy(C, pos, tmp, 0, num);
+ xor(X, tmp);
+ mul.multiplyH(X);
+ }
+ }
+
+ {
+ xor(X, lengthBlock((long)A.length * 8, (long)C.length * 8));
+ mul.multiplyH(X);
+ }
+
+ return X;
+ }
+
+ private static byte[] lengthBlock(long bitlenA, long bitlenC)
+ {
+ byte[] tmp = new byte[16];
+ Pack.longToBigEndian(bitlenA, tmp, 0);
+ Pack.longToBigEndian(bitlenC, tmp, 8);
+ return tmp;
+ }
+
+ private static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ private static byte[] multiply(byte[] a, byte[] b)
+ {
+ byte[] c = new byte[16];
+ byte[] tmp = Arrays.clone(b);
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = a[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(c, tmp);
+ }
+
+ boolean lsb = (tmp[15] & 1) != 0;
+ shiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+// GCMUtil.xor(v, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
+
+ return c;
+ }
+
+ private static void shiftRight(byte[] block)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ block[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GCMTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GCMTest.java
new file mode 100644
index 000000000..b87cb69df
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GCMTest.java
@@ -0,0 +1,675 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.modes.GCMBlockCipher;
+import org.spongycastle.crypto.modes.gcm.BasicGCMMultiplier;
+import org.spongycastle.crypto.modes.gcm.GCMMultiplier;
+import org.spongycastle.crypto.modes.gcm.Tables64kGCMMultiplier;
+import org.spongycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from "The Galois/Counter Mode of Operation (GCM)", McGrew/Viega, Appendix B
+ */
+public class GCMTest
+ extends SimpleTest
+{
+ private static final String[][] TEST_VECTORS = new String[][] {
+ {
+ "Test Case 1",
+ "00000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "58e2fccefa7e3061367f1d57a4e7455a",
+ },
+ {
+ "Test Case 2",
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "0388dace60b6a392f328c2b971b2fe78",
+ "ab6e47d42cec13bdf53a67b21257bddf",
+ },
+ {
+ "Test Case 3",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49c"
+ + "e3aa212f2c02a4e035c17e2329aca12e"
+ + "21d514b25466931c7d8f6a5aac84aa05"
+ + "1ba30b396a0aac973d58e091473f5985",
+ "4d5c2af327cd64a62cf35abd2ba6fab4",
+ },
+ {
+ "Test Case 4",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49c"
+ + "e3aa212f2c02a4e035c17e2329aca12e"
+ + "21d514b25466931c7d8f6a5aac84aa05"
+ + "1ba30b396a0aac973d58e091",
+ "5bc94fbc3221a5db94fae95ae7121a47",
+ },
+ {
+ "Test Case 5",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbad",
+ "61353b4c2806934a777ff51fa22a4755"
+ + "699b2a714fcdc6f83766e5f97b6c7423"
+ + "73806900e49f24b22b097544d4896b42"
+ + "4989b5e1ebac0f07c23f4598",
+ "3612d2e79e3b0785561be14aaca2fccb",
+ },
+ {
+ "Test Case 6",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "9313225df88406e555909c5aff5269aa"
+ + "6a7a9538534f7da1e4c303d2a318a728"
+ + "c3c0c95156809539fcf0e2429a6b5254"
+ + "16aedbf5a0de6a57a637b39b",
+ "8ce24998625615b603a033aca13fb894"
+ + "be9112a5c3a211a8ba262a3cca7e2ca7"
+ + "01e4a9a4fba43c90ccdcb281d48c7c6f"
+ + "d62875d2aca417034c34aee5",
+ "619cc5aefffe0bfa462af43c1699d050",
+ },
+ {
+ "Test Case 7",
+ "00000000000000000000000000000000"
+ + "0000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "cd33b28ac773f74ba00ed1f312572435",
+ },
+ {
+ "Test Case 8",
+ "00000000000000000000000000000000"
+ + "0000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "98e7247c07f0fe411c267e4384b0f600",
+ "2ff58d80033927ab8ef4d4587514f0fb",
+ },
+ {
+ "Test Case 9",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757"
+ + "859e1ceaa6efd984628593b40ca1e19c"
+ + "7d773d00c144c525ac619d18c84a3f47"
+ + "18e2448b2fe324d9ccda2710acade256",
+ "9924a7c8587336bfb118024db8674a14",
+ },
+ {
+ "Test Case 10",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757"
+ + "859e1ceaa6efd984628593b40ca1e19c"
+ + "7d773d00c144c525ac619d18c84a3f47"
+ + "18e2448b2fe324d9ccda2710",
+ "2519498e80f1478f37ba55bd6d27618c",
+ },
+ {
+ "Test Case 11",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbad",
+ "0f10f599ae14a154ed24b36e25324db8"
+ + "c566632ef2bbb34f8347280fc4507057"
+ + "fddc29df9a471f75c66541d4d4dad1c9"
+ + "e93a19a58e8b473fa0f062f7",
+ "65dcc57fcf623a24094fcca40d3533f8",
+ },
+ {
+ "Test Case 12",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "9313225df88406e555909c5aff5269aa"
+ + "6a7a9538534f7da1e4c303d2a318a728"
+ + "c3c0c95156809539fcf0e2429a6b5254"
+ + "16aedbf5a0de6a57a637b39b",
+ "d27e88681ce3243c4830165a8fdcf9ff"
+ + "1de9a1d8e6b447ef6ef7b79828666e45"
+ + "81e79012af34ddd9e2f037589b292db3"
+ + "e67c036745fa22e7e9b7373b",
+ "dcf566ff291c25bbb8568fc3d376a6d9",
+ },
+ {
+ "Test Case 13",
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "530f8afbc74536b9a963b4f1c4cb738b",
+ },
+ {
+ "Test Case 14",
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "cea7403d4d606b6e074ec5d3baf39d18",
+ "d0d1c8a799996bf0265b98b5d48ab919",
+ },
+ {
+ "Test Case 15",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d"
+ + "643a8cdcbfe5c0c97598a2bd2555d1aa"
+ + "8cb08e48590dbb3da7b08b1056828838"
+ + "c5f61e6393ba7a0abcc9f662898015ad",
+ "b094dac5d93471bdec1a502270e3cc6c",
+ },
+ {
+ "Test Case 16",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d"
+ + "643a8cdcbfe5c0c97598a2bd2555d1aa"
+ + "8cb08e48590dbb3da7b08b1056828838"
+ + "c5f61e6393ba7a0abcc9f662",
+ "76fc6ece0f4e1768cddf8853bb2d551b",
+ },
+ {
+ "Test Case 17",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbad",
+ "c3762df1ca787d32ae47c13bf19844cb"
+ + "af1ae14d0b976afac52ff7d79bba9de0"
+ + "feb582d33934a4f0954cc2363bc73f78"
+ + "62ac430e64abe499f47c9b1f",
+ "3a337dbf46a792c45e454913fe2ea8f2",
+ },
+ {
+ "Test Case 18",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "9313225df88406e555909c5aff5269aa"
+ + "6a7a9538534f7da1e4c303d2a318a728"
+ + "c3c0c95156809539fcf0e2429a6b5254"
+ + "16aedbf5a0de6a57a637b39b",
+ "5a8def2f0c9e53f1f75d7853659e2a20"
+ + "eeb2b22aafde6419a058ab4f6f746bf4"
+ + "0fc0c3b780f244452da3ebf1c5d82cde"
+ + "a2418997200ef82e44ae7e3f",
+ "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+ },
+ };
+
+ public String getName()
+ {
+ return "GCM";
+ }
+
+ public void performTest() throws Exception
+ {
+ for (int i = 0; i < TEST_VECTORS.length; ++i)
+ {
+ runTestCase(TEST_VECTORS[i]);
+ }
+
+ randomTests();
+ outputSizeTests();
+ testExceptions();
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ GCMBlockCipher gcm = new GCMBlockCipher(new AESFastEngine());
+
+ try
+ {
+ gcm = new GCMBlockCipher(new DESEngine());
+
+ fail("incorrect block size not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ gcm.init(false, new KeyParameter(new byte[16]));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ AEADTestUtil.testReset(this, new GCMBlockCipher(new AESEngine()), new GCMBlockCipher(new AESEngine()), new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[16]));
+ AEADTestUtil.testTampering(this, gcm, new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[16]));
+ }
+
+ private void runTestCase(String[] testVector)
+ throws InvalidCipherTextException
+ {
+ for (int macLength = 12; macLength <= 16; ++macLength)
+ {
+ runTestCase(testVector, macLength);
+ }
+ }
+
+ private void runTestCase(String[] testVector, int macLength)
+ throws InvalidCipherTextException
+ {
+ int pos = 0;
+ String testName = testVector[pos++];
+ byte[] K = Hex.decode(testVector[pos++]);
+ byte[] P = Hex.decode(testVector[pos++]);
+ byte[] A = Hex.decode(testVector[pos++]);
+ byte[] IV = Hex.decode(testVector[pos++]);
+ byte[] C = Hex.decode(testVector[pos++]);
+
+ // For short MAC, take leading bytes
+ byte[] t = Hex.decode(testVector[pos++]);
+ byte[] T = new byte[macLength];
+ System.arraycopy(t, 0, T, 0, T.length);
+
+ // Default multiplier
+ runTestCase(null, null, testName, K, IV, A, P, C, T);
+
+ runTestCase(new BasicGCMMultiplier(), new BasicGCMMultiplier(), testName, K, IV, A, P, C, T);
+ runTestCase(new Tables8kGCMMultiplier(), new Tables8kGCMMultiplier(), testName, K, IV, A, P, C, T);
+ runTestCase(new Tables64kGCMMultiplier(), new Tables64kGCMMultiplier(), testName, K, IV, A, P, C, T);
+ }
+
+ private void runTestCase(
+ GCMMultiplier encM,
+ GCMMultiplier decM,
+ String testName,
+ byte[] K,
+ byte[] IV,
+ byte[] A,
+ byte[] P,
+ byte[] C,
+ byte[] T)
+ throws InvalidCipherTextException
+ {
+ byte[] fa = new byte[A.length / 2];
+ byte[] la = new byte[A.length - (A.length / 2)];
+ System.arraycopy(A, 0, fa, 0, fa.length);
+ System.arraycopy(A, fa.length, la, 0, la.length);
+
+ runTestCase(encM, decM, testName + " all initial associated data", K, IV, A, null, P, C, T);
+ runTestCase(encM, decM, testName + " all subsequent associated data", K, IV, null, A, P, C, T);
+ runTestCase(encM, decM, testName + " split associated data", K, IV, fa, la, P, C, T);
+ }
+
+ private void runTestCase(
+ GCMMultiplier encM,
+ GCMMultiplier decM,
+ String testName,
+ byte[] K,
+ byte[] IV,
+ byte[] A,
+ byte[] SA,
+ byte[] P,
+ byte[] C,
+ byte[] T)
+ throws InvalidCipherTextException
+ {
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), T.length * 8, IV, A);
+ GCMBlockCipher encCipher = initCipher(encM, true, parameters);
+ GCMBlockCipher decCipher = initCipher(decM, false, parameters);
+ checkTestCase(encCipher, decCipher, testName, SA, P, C, T);
+ checkTestCase(encCipher, decCipher, testName + " (reused)", SA, P, C, T);
+
+ // Key reuse
+ AEADParameters keyReuseParams = new AEADParameters(null, parameters.getMacSize(), parameters.getNonce(), parameters.getAssociatedText());
+ encCipher.init(true, keyReuseParams);
+ decCipher.init(false, keyReuseParams);
+ checkTestCase(encCipher, decCipher, testName + " (key reuse)", SA, P, C, T);
+ checkTestCase(encCipher, decCipher, testName + " (key reuse)", SA, P, C, T);
+ }
+
+ private GCMBlockCipher initCipher(GCMMultiplier m, boolean forEncryption, AEADParameters parameters)
+ {
+ GCMBlockCipher c = new GCMBlockCipher(new AESFastEngine(), m);
+ c.init(forEncryption, parameters);
+ return c;
+ }
+
+ private void checkTestCase(
+ GCMBlockCipher encCipher,
+ GCMBlockCipher decCipher,
+ String testName,
+ byte[] SA,
+ byte[] P,
+ byte[] C,
+ byte[] T)
+ throws InvalidCipherTextException
+ {
+ byte[] enc = new byte[encCipher.getOutputSize(P.length)];
+ if (SA != null)
+ {
+ encCipher.processAADBytes(SA, 0, SA.length);
+ }
+ int len = encCipher.processBytes(P, 0, P.length, enc, 0);
+ len += encCipher.doFinal(enc, len);
+
+ if (enc.length != len)
+ {
+// System.out.println("" + enc.length + "/" + len);
+ fail("encryption reported incorrect length: " + testName);
+ }
+
+ byte[] mac = encCipher.getMac();
+
+ byte[] data = new byte[P.length];
+ System.arraycopy(enc, 0, data, 0, data.length);
+ byte[] tail = new byte[enc.length - P.length];
+ System.arraycopy(enc, P.length, tail, 0, tail.length);
+
+ if (!areEqual(C, data))
+ {
+ fail("incorrect encrypt in: " + testName);
+ }
+
+ if (!areEqual(T, mac))
+ {
+ fail("getMac() returned wrong mac in: " + testName);
+ }
+
+ if (!areEqual(T, tail))
+ {
+ fail("stream contained wrong mac in: " + testName);
+ }
+
+ byte[] dec = new byte[decCipher.getOutputSize(enc.length)];
+ if (SA != null)
+ {
+ decCipher.processAADBytes(SA, 0, SA.length);
+ }
+ len = decCipher.processBytes(enc, 0, enc.length, dec, 0);
+ len += decCipher.doFinal(dec, len);
+ mac = decCipher.getMac();
+
+ data = new byte[C.length];
+ System.arraycopy(dec, 0, data, 0, data.length);
+
+ if (!areEqual(P, data))
+ {
+ fail("incorrect decrypt in: " + testName);
+ }
+ }
+
+ private void randomTests()
+ throws InvalidCipherTextException
+ {
+ SecureRandom srng = new SecureRandom();
+ for (int i = 0; i < 10; ++i)
+ {
+ randomTest(srng, null);
+ randomTest(srng, new BasicGCMMultiplier());
+ randomTest(srng, new Tables8kGCMMultiplier());
+ randomTest(srng, new Tables64kGCMMultiplier());
+ }
+ }
+
+ private void randomTest(SecureRandom srng, GCMMultiplier m)
+ throws InvalidCipherTextException
+ {
+ int kLength = 16 + 8 * (Math.abs(srng.nextInt()) % 3);
+ byte[] K = new byte[kLength];
+ srng.nextBytes(K);
+
+ int pLength = srng.nextInt() >>> 16;
+ byte[] P = new byte[pLength];
+ srng.nextBytes(P);
+
+ int aLength = srng.nextInt() >>> 24;
+ byte[] A = new byte[aLength];
+ srng.nextBytes(A);
+
+ int saLength = srng.nextInt() >>> 24;
+ byte[] SA = new byte[saLength];
+ srng.nextBytes(SA);
+
+ int ivLength = 1 + (srng.nextInt() >>> 24);
+ byte[] IV = new byte[ivLength];
+ srng.nextBytes(IV);
+
+ GCMBlockCipher cipher = new GCMBlockCipher(new AESFastEngine(), m);
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, IV, A);
+ cipher.init(true, parameters);
+ byte[] C = new byte[cipher.getOutputSize(P.length)];
+ int predicted = cipher.getUpdateOutputSize(P.length);
+
+ int split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ int len = cipher.processBytes(P, 0, P.length, C, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ if (predicted != len)
+ {
+ fail("encryption reported incorrect update length in randomised test");
+ }
+
+ len += cipher.doFinal(C, len);
+
+ if (C.length != len)
+ {
+ fail("encryption reported incorrect length in randomised test");
+ }
+
+ byte[] encT = cipher.getMac();
+ byte[] tail = new byte[C.length - P.length];
+ System.arraycopy(C, P.length, tail, 0, tail.length);
+
+ if (!areEqual(encT, tail))
+ {
+ fail("stream contained wrong mac in randomised test");
+ }
+
+ cipher.init(false, parameters);
+ byte[] decP = new byte[cipher.getOutputSize(C.length)];
+ predicted = cipher.getUpdateOutputSize(C.length);
+
+ split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ len = cipher.processBytes(C, 0, C.length, decP, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ if (predicted != len)
+ {
+ fail("decryption reported incorrect update length in randomised test");
+ }
+
+ len += cipher.doFinal(decP, len);
+
+ if (!areEqual(P, decP))
+ {
+ fail("incorrect decrypt in randomised test");
+ }
+
+ byte[] decT = cipher.getMac();
+ if (!areEqual(encT, decT))
+ {
+ fail("decryption produced different mac from encryption");
+ }
+
+ //
+ // key reuse test
+ //
+ cipher.init(false, new AEADParameters(null, parameters.getMacSize(), parameters.getNonce(), parameters.getAssociatedText()));
+ decP = new byte[cipher.getOutputSize(C.length)];
+
+ split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ len = cipher.processBytes(C, 0, C.length, decP, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ len += cipher.doFinal(decP, len);
+
+ if (!areEqual(P, decP))
+ {
+ fail("incorrect decrypt in randomised test");
+ }
+
+ decT = cipher.getMac();
+ if (!areEqual(encT, decT))
+ {
+ fail("decryption produced different mac from encryption");
+ }
+ }
+
+ private void outputSizeTests()
+ {
+ byte[] K = new byte[16];
+ byte[] A = null;
+ byte[] IV = new byte[16];
+
+ GCMBlockCipher cipher = new GCMBlockCipher(new AESFastEngine(), new BasicGCMMultiplier());
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, IV, A);
+
+ cipher.init(true, parameters);
+
+ if (cipher.getUpdateOutputSize(0) != 0)
+ {
+ fail("incorrect getUpdateOutputSize for initial 0 bytes encryption");
+ }
+
+ if (cipher.getOutputSize(0) != 16)
+ {
+ fail("incorrect getOutputSize for initial 0 bytes encryption");
+ }
+
+ cipher.init(false, parameters);
+
+ if (cipher.getUpdateOutputSize(0) != 0)
+ {
+ fail("incorrect getUpdateOutputSize for initial 0 bytes decryption");
+ }
+
+ // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here
+ if (cipher.getOutputSize(0) != 0)
+ {
+ fail("fragile getOutputSize for initial 0 bytes decryption");
+ }
+
+ if (cipher.getOutputSize(16) != 0)
+ {
+ fail("incorrect getOutputSize for initial MAC-size bytes decryption");
+ }
+ }
+
+ private static int nextInt(SecureRandom rand, int n)
+ {
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31);
+ }
+
+ int bits, value;
+ do
+ {
+ bits = rand.nextInt() >>> 1;
+ value = bits % n;
+ }
+ while (bits - value + (n - 1) < 0);
+
+ return value;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new GCMTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GMacTest.java
new file mode 100644
index 000000000..c478fc863
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GMacTest.java
@@ -0,0 +1,171 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.macs.GMac;
+import org.spongycastle.crypto.modes.GCMBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors for AES-GMAC, extracted from NIST CAVP GCM test
+ * vectors.
+ *
+ */
+public class GMacTest extends SimpleTest
+{
+ private static class TestCase
+ {
+ private byte[] key;
+ private byte[] iv;
+ private byte[] ad;
+ private byte[] tag;
+ private String name;
+
+ private TestCase(final String name, final String key, final String iv, final String ad, final String tag)
+ {
+ this.name = name;
+ this.key = Hex.decode(key);
+ this.iv = Hex.decode(iv);
+ this.ad = Hex.decode(ad);
+ this.tag = Hex.decode(tag);
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getIv()
+ {
+ return iv;
+ }
+
+ public byte[] getAd()
+ {
+ return ad;
+ }
+
+ public byte[] getTag()
+ {
+ return tag;
+ }
+ }
+
+ private static TestCase[] TEST_VECTORS = new TestCase[] {
+ // Count = 0, from each of the PTlen = 0 test vector sequences
+ new TestCase("128/96/0/128", "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "",
+ "250327c674aaf477aef2675748cf6971"),
+ new TestCase("128/96/0/120", "272f16edb81a7abbea887357a58c1917", "794ec588176c703d3d2a7a07", "",
+ "b6e6f197168f5049aeda32dafbdaeb"),
+ new TestCase("128/96/0/112", "81b6844aab6a568c4556a2eb7eae752f", "ce600f59618315a6829bef4d", "",
+ "89b43e9dbc1b4f597dbbc7655bb5"),
+ new TestCase("128/96/0/104", "cde2f9a9b1a004165ef9dc981f18651b", "29512c29566c7322e1e33e8e", "",
+ "2e58ce7dabd107c82759c66a75"),
+ new TestCase("128/96/0/96", "b01e45cc3088aaba9fa43d81d481823f", "5a2c4a66468713456a4bd5e1", "",
+ "014280f944f53c681164b2ff"),
+
+ new TestCase("128/96/128/128", "77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3",
+ "7a43ec1d9c0a5a78a0b16533a6213cab", "209fcc8d3675ed938e9c7166709dd946"),
+ new TestCase("128/96/128/96", "bea48ae4980d27f357611014d4486625", "32bddb5c3aa998a08556454c",
+ "8a50b0b8c7654bced884f7f3afda2ead", "8e0f6d8bf05ffebe6f500eb1"),
+
+ new TestCase("128/96/384/128", "99e3e8793e686e571d8285c564f75e2b", "c2dd0ab868da6aa8ad9c0d23",
+ "b668e42d4e444ca8b23cfdd95a9fedd5178aa521144890b093733cf5cf22526c5917ee476541809ac6867a8c399309fc",
+ "3f4fba100eaf1f34b0baadaae9995d85"),
+ new TestCase("128/96/384/96", "c77acd1b0918e87053cb3e51651e7013", "39ff857a81745d10f718ac00",
+ "407992f82ea23b56875d9a3cb843ceb83fd27cb954f7c5534d58539fe96fb534502a1b38ea4fac134db0a42de4be1137",
+ "2a5dc173285375dc82835876"),
+
+ new TestCase(
+ "128/1024/0/128",
+ "d0f1f4defa1e8c08b4b26d576392027c",
+ "42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac",
+ "", "7ab49b57ddf5f62c427950111c5c4f0d"),
+ new TestCase(
+ "128/1024/384/96",
+ "3cce72d37933394a8cac8a82deada8f0",
+ "aa2f0d676d705d9733c434e481972d4888129cf7ea55c66511b9c0d25a92a174b1e28aa072f27d4de82302828955aadcb817c4907361869bd657b45ff4a6f323871987fcf9413b0702d46667380cd493ed24331a28b9ce5bbfa82d3a6e7679fcce81254ba64abcad14fd18b22c560a9d2c1cd1d3c42dac44c683edf92aced894",
+ "5686b458e9c176f4de8428d9ebd8e12f569d1c7595cf49a4b0654ab194409f86c0dd3fdb8eb18033bb4338c70f0b97d1",
+ "a3a9444b21f330c3df64c8b6"), };
+
+ public void performTest()
+ {
+ for (int i = 0; i < TEST_VECTORS.length; i++)
+ {
+ TestCase testCase = TEST_VECTORS[i];
+
+ Mac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), testCase.getTag().length * 8);
+ CipherParameters key = new KeyParameter(testCase.getKey());
+ mac.init(new ParametersWithIV(key, testCase.getIv()));
+
+ testSingleByte(mac, testCase);
+ testMultibyte(mac, testCase);
+ }
+
+ // Invalid mac size
+ testInvalidMacSize(97);
+ testInvalidMacSize(136);
+ testInvalidMacSize(88);
+ testInvalidMacSize(64);
+ }
+
+ private void testInvalidMacSize(int size)
+ {
+ try
+ {
+ GMac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), size);
+ mac.init(new ParametersWithIV(null, new byte[16]));
+ fail("Expected failure for illegal mac size " + size);
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ private void testMultibyte(Mac mac, TestCase testCase)
+ {
+ mac.update(testCase.getAd(), 0, testCase.getAd().length);
+ checkMac(mac, testCase);
+ }
+
+ private void testSingleByte(Mac mac, TestCase testCase)
+ {
+ final byte[] ad = testCase.getAd();
+ for (int i = 0; i < ad.length; i++)
+ {
+ mac.update(ad[i]);
+ }
+ checkMac(mac, testCase);
+ }
+
+ private void checkMac(Mac mac, TestCase testCase)
+ {
+ final byte[] generatedMac = new byte[mac.getMacSize()];
+ mac.doFinal(generatedMac, 0);
+ if (!areEqual(testCase.getTag(), generatedMac))
+ {
+ fail("Failed " + testCase.getName() + " - expected " + new String(Hex.encode(testCase.getTag())) + " got "
+ + new String(Hex.encode(generatedMac)));
+ }
+ }
+
+ public String getName()
+ {
+ return "GMac";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new GMacTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST28147MacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST28147MacTest.java
new file mode 100644
index 000000000..4ed507837
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST28147MacTest.java
@@ -0,0 +1,89 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.engines.GOST28147Engine;
+import org.spongycastle.crypto.macs.GOST28147Mac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithSBox;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * GOST 28147 MAC tester
+ */
+public class GOST28147MacTest
+ implements Test
+{
+ //
+ // these GOSTMac for testing.
+ //
+ static byte[] gkeyBytes1 = Hex.decode("6d145dc993f4019e104280df6fcd8cd8e01e101e4c113d7ec4f469ce6dcd9e49");
+ static byte[] gkeyBytes2 = Hex.decode("6d145dc993f4019e104280df6fcd8cd8e01e101e4c113d7ec4f469ce6dcd9e49");
+
+ static byte[] input3 = Hex.decode("7768617420646f2079612077616e7420666f72206e6f7468696e673f");
+ static byte[] input4 = Hex.decode("7768617420646f2079612077616e7420666f72206e6f7468696e673f");
+
+ static byte[] output7 = Hex.decode("93468a46");
+ static byte[] output8 = Hex.decode("93468a46");
+
+ public GOST28147MacTest()
+ {
+ }
+
+ public TestResult perform()
+ {
+ // test1
+ Mac mac = new GOST28147Mac();
+ KeyParameter key = new KeyParameter(gkeyBytes1);
+
+ mac.init(key);
+
+ mac.update(input3, 0, input3.length);
+
+ byte[] out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, output7))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed test 1 - expected " + new String(Hex.encode(output7)) + " got " + new String(Hex.encode(out)));
+ }
+
+ // test2
+ key = new KeyParameter(gkeyBytes2);
+
+ ParametersWithSBox gparam = new ParametersWithSBox(key, GOST28147Engine.getSBox("E-A"));
+
+ mac.init(gparam);
+
+ mac.update(input4, 0, input4.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, output8))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed test 2 - expected " + new String(Hex.encode(output8)) + " got " + new String(Hex.encode(out)));
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "GOST28147Mac";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ GOST28147MacTest test = new GOST28147MacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST28147Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST28147Test.java
new file mode 100644
index 000000000..a89f2e632
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST28147Test.java
@@ -0,0 +1,328 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.digests.GOST3411Digest;
+import org.spongycastle.crypto.engines.GOST28147Engine;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.GOFBBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithSBox;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class GOST28147Test
+ extends CipherTest
+{
+ static String input1 = "0000000000000000";
+ static String output1 = "1b0bbc32cebcab42";
+ static String input2 = "bc350e71aac5f5c2";
+ static String output2 = "d35ab653493b49f5";
+ static String input3 = "bc350e71aa11345709acde";
+ static String output3 = "8824c124c4fd14301fb1e8";
+ static String input4 = "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f";
+ static String output4 = "29b7083e0a6d955ca0ec5b04fdb4ea41949f1dd2efdf17baffc1780b031f3934";
+
+ static byte TestSBox[] = {
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0
+ };
+
+ static SimpleTest[] tests =
+ { new BlockCipherVectorTest(1, new GOST28147Engine(),
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")),
+ input1, output1),
+ new BlockCipherVectorTest(2, new CBCBlockCipher(new GOST28147Engine()),
+ new ParametersWithIV(new KeyParameter(Hex.decode("00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF")),
+ Hex.decode("1234567890abcdef")), input2, output2),
+ new BlockCipherVectorTest(3, new GOFBBlockCipher(new GOST28147Engine()),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0011223344556677889900112233445566778899001122334455667788990011")),
+ Hex.decode("1234567890abcdef")), //IV
+ input3, output3),
+ new BlockCipherVectorTest(4, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f659cae63489b479e5")),
+ Hex.decode("aafd12f659cae634")), input4, output4),
+
+ //tests with parameters, set S-box.
+ new BlockCipherVectorTest(5, new GOST28147Engine(),
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")),//key , default parameter S-box set to D-Test
+ input1, output1),
+ new BlockCipherVectorTest(6, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("D-Test")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "b587f7a0814c911d"), //encrypt message
+ new BlockCipherVectorTest(7, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-Test")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "e8287f53f991d52b"), //encrypt message
+ new BlockCipherVectorTest(8, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "c41009dba22ebe35"), //encrypt message
+ new BlockCipherVectorTest(9, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-B")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "80d8723fcd3aba28"), //encrypt message
+ new BlockCipherVectorTest(10, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-C")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "739f6f95068499b5"), //encrypt message
+ new BlockCipherVectorTest(11, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-D")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "4663f720f4340f57"), //encrypt message
+ new BlockCipherVectorTest(12, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("D-A")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "5bb0a31d218ed564"), //encrypt message
+ new BlockCipherVectorTest(13, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ TestSBox), //set own S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "c3af96ef788667c5"), //encrypt message
+ new BlockCipherVectorTest(14, new GOFBBlockCipher(new GOST28147Engine()),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("4ef72b778f0b0bebeef4f077551cb74a927b470ad7d7f2513454569a247e989d")), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "bc350e71aa11345709acde", //input message
+ "1bcc2282707c676fb656dc"), //encrypt message
+
+ };
+
+ static private final int GOST28147_KEY_LENGTH = 32;
+
+ private byte[] generateKey(byte[] startkey)
+ {
+ byte[] newKey = new byte[GOST28147_KEY_LENGTH];
+
+ GOST3411Digest digest = new GOST3411Digest();
+
+ digest.update(startkey, 0, startkey.length);
+ digest.doFinal(newKey, 0);
+
+ return newKey;
+ }
+
+ GOST28147Test()
+ {
+ super(tests, new GOST28147Engine(), new KeyParameter(new byte[32]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ //advanced tests with GOST28147KeyGenerator:
+ //encrypt on hesh message; ECB mode:
+ byte[] in = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+ byte[] output = Hex.decode("8ad3c8f56b27ff1fbd46409359bdc796bc350e71aac5f5c0");
+ byte[] out = new byte[in.length];
+
+ byte[] key = generateKey(Hex.decode("0123456789abcdef")); //!!! heshing start_key - get 256 bits !!!
+// System.out.println(new String(Hex.encode(key)));
+ CipherParameters param = new ParametersWithSBox(new KeyParameter(key), GOST28147Engine.getSBox("E-A"));
+ //CipherParameters param = new GOST28147Parameters(key,"D-Test");
+ BufferedBlockCipher cipher = new BufferedBlockCipher(new GOST28147Engine());
+
+ cipher.init(true, param);
+ int len1 = cipher.processBytes(in, 0, in.length, out, 0);
+ try
+ {
+ cipher.doFinal(out, len1);
+ }
+ catch (CryptoException e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+ if (out.length != output.length)
+ {
+ fail("failed - "
+ + "expected " + new String(Hex.encode(output)) + " got "
+ + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+
+ //encrypt on hesh message; CFB mode:
+ in = Hex.decode("bc350e71aac5f5c2");
+ output = Hex.decode("0ebbbafcf38f14a5");
+ out = new byte[in.length];
+
+ key = generateKey(Hex.decode("0123456789abcdef")); //!!! heshing start_key - get 256 bits !!!
+ param = new ParametersWithIV(new ParametersWithSBox(
+ new KeyParameter(key), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")); //IV
+
+ cipher = new BufferedBlockCipher(new CFBBlockCipher(new GOST28147Engine(), 64));
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+ try
+ {
+ cipher.doFinal(out, len1);
+ }
+ catch (CryptoException e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+
+ //encrypt on hesh message; CFB mode:
+ in = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f");
+ output = Hex.decode("64988982819f0a1655e226e19ecad79d10cc73bac95c5d7da034786c12294225");
+ out = new byte[in.length];
+
+ key = generateKey(Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f659cae63489b479e5")); //!!! heshing start_key - get 256 bits !!!
+ param = new ParametersWithIV(new ParametersWithSBox(
+ new KeyParameter(key), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("aafd12f659cae634")); //IV
+
+ cipher = new BufferedBlockCipher(new CFBBlockCipher(new GOST28147Engine(), 64));
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ //encrypt on hesh message; OFB mode:
+ in = Hex.decode("bc350e71aa11345709acde");
+ output = Hex.decode("1bcc2282707c676fb656dc");
+ out = new byte[in.length];
+
+ key = generateKey(Hex.decode("0123456789abcdef")); //!!! heshing start_key - get 256 bits !!!
+ param = new ParametersWithIV(new ParametersWithSBox(
+ new KeyParameter(key), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")); //IV
+
+ cipher = new BufferedBlockCipher(new GOFBBlockCipher(new GOST28147Engine()));
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected "
+ + new String(Hex.encode(output)) + " got "
+ + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ // key reuse test
+ param = new ParametersWithIV(null, // key and sbox reused
+ Hex.decode("1234567890abcdef")); //IV
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected "
+ + new String(Hex.encode(output)) + " got "
+ + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "GOST28147";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GOST28147Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST3410Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST3410Test.java
new file mode 100644
index 000000000..08ce0b019
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST3410Test.java
@@ -0,0 +1,1570 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.generators.GOST3410KeyPairGenerator;
+import org.spongycastle.crypto.generators.GOST3410ParametersGenerator;
+import org.spongycastle.crypto.params.GOST3410KeyGenerationParameters;
+import org.spongycastle.crypto.params.GOST3410Parameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.GOST3410Signer;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.FixedSecureRandom;
+import org.spongycastle.util.test.NumberParsing;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class GOST3410Test
+ implements Test
+{
+ byte[] hashmessage = Hex.decode("3042453136414534424341374533364339313734453431443642453241453435");
+
+ private byte[] zeroTwo(int length)
+ {
+ byte[] data = new byte[length];
+ data[data.length - 1] = 0x02;
+ return data;
+ }
+
+ private class GOST3410_TEST1_512
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST1-512";
+ }
+
+ FixedSecureRandom init_random = new FixedSecureRandom(new byte[][] { Hex.decode("00005EC900007341"), zeroTwo(64) });
+ FixedSecureRandom random = new FixedSecureRandom(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
+ FixedSecureRandom keyRandom = new FixedSecureRandom(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
+
+ BigInteger pValue = new BigInteger("EE8172AE8996608FB69359B89EB82A69854510E2977A4D63BC97322CE5DC3386EA0A12B343E9190F23177539845839786BB0C345D165976EF2195EC9B1C379E3", 16);
+ BigInteger qValue = new BigInteger("98915E7EC8265EDFCDA31E88F24809DDB064BDC7285DD50D7289F0AC6F49DD2D", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("3e5f895e276d81d2d52c0763270a458157b784c57abdbd807bc44fd43a32ac06",16);
+ BigInteger s = new BigInteger("3f0dd5d4400d47c08e4ce505ff7434b6dbf729592e37c74856dab85115a60955",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(512, 1, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (params.getValidationParameters() == null)
+ {
+ return new SimpleTestResult(false, getName() + "validation parameters wrong");
+ }
+ if (params.getValidationParameters().getC() != 29505
+ || params.getValidationParameters().getX0() != 24265)
+ {
+ return new SimpleTestResult(false, getName() + "validation parameters values wrong");
+ }
+ if (!init_random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'init_random'.");
+ }
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ if (!keyRandom.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'keyRandom'.");
+ }
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer gost3410 = new GOST3410Signer();
+
+ gost3410.init(true, param);
+
+ BigInteger[] sig = gost3410.generateSignature(hashmessage);
+
+ if (!random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'random'.");
+ }
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ gost3410.init(false, pair.getPublic());
+
+ if (gost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_TEST2_512
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST2-512";
+ }
+
+ FixedSecureRandom init_random = new FixedSecureRandom(new byte[][] { Hex.decode("000000003DFC46F1000000000000000D"), zeroTwo(64) });
+ FixedSecureRandom random = new FixedSecureRandom(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
+ FixedSecureRandom keyRandom = new FixedSecureRandom(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
+
+ BigInteger pValue = new BigInteger("8b08eb135af966aab39df294538580c7da26765d6d38d30cf1c06aae0d1228c3316a0e29198460fad2b19dc381c15c888c6dfd0fc2c565abb0bf1faff9518f85", 16);
+ BigInteger qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("7c07c8cf035c2a1cb2b7fae5807ac7cd623dfca7a1a68f6d858317822f1ea00d",16);
+ BigInteger s = new BigInteger("7e9e036a6ff87dbf9b004818252b1f6fc310bdd4d17cb8c37d9c36c7884de60c",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(512, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!init_random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'init_random'.");
+ }
+
+ if (params.getValidationParameters() == null)
+ {
+ return new SimpleTestResult(false, getName() + ": validation parameters wrong");
+ }
+
+ if (params.getValidationParameters().getCL() != 13
+ || params.getValidationParameters().getX0L() != 1039943409)
+ {
+ return new SimpleTestResult(false, getName() + ": validation parameters values wrong");
+ }
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ if (!keyRandom.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'keyRandom'.");
+ }
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'random'.");
+ }
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_TEST1_1024
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST1-1024";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstInt = true;
+
+ public int nextInt()
+ {
+ String x0 = "0xA565";
+ String c = "0x538B";
+
+ if (firstInt)
+ {
+ firstInt = false;
+ return NumberParsing.decodeIntFromHex(x0);
+ }
+ return NumberParsing.decodeIntFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("02");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("ab8f37938356529e871514c1f48c5cbce77b2f4fc9a2673ac2c1653da8984090c0ac73775159a26bef59909d4c9846631270e16653a6234668f2a52a01a39b921490e694c0f104b58d2e14970fccb478f98d01e975a1028b9536d912de5236d2dd2fc396b77153594d4178780e5f16f718471e2111c8ce64a7d7e196fa57142d", 16);
+ BigInteger qValue = new BigInteger("bcc02ca0ce4f0753ec16105ee5d530aa00d39f3171842ab2c334a26b5f576e0f", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("a8790aabbd5a998ff524bad048ac69cd1faff2dab048265c8d60d1471c44a9ee",16);
+ BigInteger s = new BigInteger("30df5ba32ac77170b9632559bef7d37620017756dff3fea1088b4267db0944b8",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 1, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_TEST2_1024
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST2-1024";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x3DFC46F1";
+ String c = "0xD";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("02");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("e2c4191c4b5f222f9ac2732562f6d9b4f18e7fb67a290ea1e03d750f0b9806755fc730d975bf3faa606d05c218b35a6c3706919aab92e0c58b1de4531c8fa8e7af43c2bff016251e21b2870897f6a27ac4450bca235a5b748ad386e4a0e4dfcb09152435abcfe48bd0b126a8122c7382f285a9864615c66decddf6afd355dfb7", 16);
+ BigInteger qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("81d69a192e9c7ac21fc07da41bd07e230ba6a94eb9f3c1fd104c7bd976733ca5",16);
+ BigInteger s = new BigInteger("315c879c8414f35feb4deb15e7cc0278c48e6ca1596325d6959338d860b0c47a",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_AParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-AParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x520874F5";
+ String c = "0xEE39ADB3";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("02");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("b4e25efb018e3c8b87505e2a67553c5edc56c2914b7e4f89d23f03f03377e70a2903489dd60e78418d3d851edb5317c4871e40b04228c3b7902963c4b7d85d52b9aa88f2afdbeb28da8869d6df846a1d98924e925561bd69300b9ddd05d247b5922d967cbb02671881c57d10e5ef72d3e6dad4223dc82aa1f7d0294651a480df", 16);
+ BigInteger qValue = new BigInteger("972432a437178b30bd96195b773789ab2fff15594b176dd175b63256ee5af2cf", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("64a8856628e5669d85f62cd763dd4a99bc56d33dc0e1859122855d141e9e4774",16);
+ BigInteger s = new BigInteger("319ebac97092b288d469a4b988248794f60c865bc97858d9a3135c6d1a1bf2dd",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_BParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-BParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x5B977CDB";
+ String c = "0x6E9692DD";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("bc3cbbdb7e6f848286e19ad9a27a8e297e5b71c53dd974cdf60f937356df69cbc97a300ccc71685c553046147f11568c4fddf363d9d886438345a62c3b75963d6546adfabf31b31290d12cae65ecb8309ef66782");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("c6971fc57524b30c9018c5e621de15499736854f56a6f8aee65a7a404632b3540f09020f67f04dc2e6783b141dceffd21a703035b7d0187c6e12cb4229922bafdb2225b73e6b23a0de36e20047065aea000c1a374283d0ad8dc1981e3995f0bb8c72526041fcb98ae6163e1e71a669d8364e9c4c3188f673c5f8ee6fadb41abf", 16);
+ BigInteger qValue = new BigInteger("b09d634c10899cd7d4c3a7657403e05810b07c61a688bab2c37f475e308b0607", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("860d82c60e9502cd00c0e9e1f6563feafec304801974d745c5e02079946f729e",16);
+ BigInteger s = new BigInteger("7ef49264ef022801aaa03033cd97915235fbab4c823ed936b0f360c22114688a",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_CParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-CParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x43848744";
+ String c = "0xB50A826D";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("7F575E8194BC5BDF");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("9d88e6d7fe3313bd2e745c7cdd2ab9ee4af3c8899e847de74a33783ea68bc30588ba1f738c6aaf8ab350531f1854c3837cc3c860ffd7e2e106c3f63b3d8a4c034ce73942a6c3d585b599cf695ed7a3c4a93b2b947b7157bb1a1c043ab41ec8566c6145e938a611906de0d32e562494569d7e999a0dda5c879bdd91fe124df1e9", 16);
+ BigInteger qValue = new BigInteger("fadd197abd19a1b4653eecf7eca4d6a22b1f7f893b641f901641fbb555354faf", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("4deb95a0b35e7ed7edebe9bef5a0f93739e16b7ff27fe794d989d0c13159cfbc",16);
+ BigInteger s = new BigInteger("e1d0d30345c24cfeb33efde3deee5fbbda78ddc822b719d860cd0ba1fb6bd43b",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_DParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-DParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x13DA8B9D";
+ String c = "0xA0E9DE4B";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("41ab97857f42614355d32db0b1069f109a4da283676c7c53a68185b4");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("80f102d32b0fd167d069c27a307adad2c466091904dbaa55d5b8cc7026f2f7a1919b890cb652c40e054e1e9306735b43d7b279eddf9102001cd9e1a831fe8a163eed89ab07cf2abe8242ac9dedddbf98d62cddd1ea4f5f15d3a42a6677bdd293b24260c0f27c0f1d15948614d567b66fa902baa11a69ae3bceadbb83e399c9b5", 16);
+ BigInteger qValue = new BigInteger("f0f544c418aac234f683f033511b65c21651a6078bda2d69bb9f732867502149", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("712592d285b792e33b8a9a11e8e6c4f512ddf0042972bbfd1abb0a93e8fc6f54",16);
+ BigInteger s = new BigInteger("2cf26758321258b130d5612111339f09ceb8668241f3482e38baa56529963f07",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_AExParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-AExParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0xD05E9F14";
+ String c = "0x46304C5F";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("35ab875399cda33c146ca629660e5a5e5c07714ca326db032dd6751995cdb90a612b9228932d8302704ec24a5def7739c5813d83");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("ca3b3f2eee9fd46317d49595a9e7518e6c63d8f4eb4d22d10d28af0b8839f079f8289e603b03530784b9bb5a1e76859e4850c670c7b71c0df84ca3e0d6c177fe9f78a9d8433230a883cd82a2b2b5c7a3306980278570cdb79bf01074a69c9623348824b0c53791d53c6a78cab69e1cfb28368611a397f50f541e16db348dbe5f", 16);
+ BigInteger qValue = new BigInteger("cae4d85f80c147704b0ca48e85fb00a9057aa4acc44668e17f1996d7152690d9", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("90892707282f433398488f19d31ac48523a8e2ded68944e0da91c6895ee7045e",16);
+ BigInteger s = new BigInteger("3be4620ee88f1ee8f9dd63c7d145b7e554839feeca125049118262ea4651e9de",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_BExParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-BExParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x7A007804";
+ String c = "0xD31A4FF7";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("7ec123d161477762838c2bea9dbdf33074af6d41d108a066a1e7a07ab3048de2");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("9286dbda91eccfc3060aa5598318e2a639f5ba90a4ca656157b2673fb191cd0589ee05f4cef1bd13508408271458c30851ce7a4ef534742bfb11f4743c8f787b11193ba304c0e6bca25701bf88af1cb9b8fd4711d89f88e32b37d95316541bf1e5dbb4989b3df13659b88c0f97a3c1087b9f2d5317d557dcd4afc6d0a754e279", 16);
+ BigInteger qValue = new BigInteger("c966e9b3b8b7cdd82ff0f83af87036c38f42238ec50a876cd390e43d67b6013f", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("8f79a582513df84dc247bcb624340cc0e5a34c4324a20ce7fe3ab8ff38a9db71",16);
+ BigInteger s = new BigInteger("7508d22fd6cbb45efd438cb875e43f137247088d0f54b29a7c91f68a65b5fa85",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_CExParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-CExParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x162AB910";
+ String c = "0x93F828D3";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("ca82cce78a738bc46f103d53b9bf809745ec845e4f6da462606c51f60ecf302e31204b81");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("b194036ace14139d36d64295ae6c50fc4b7d65d8b340711366ca93f383653908ee637be428051d86612670ad7b402c09b820fa77d9da29c8111a8496da6c261a53ed252e4d8a69a20376e6addb3bdcd331749a491a184b8fda6d84c31cf05f9119b5ed35246ea4562d85928ba1136a8d0e5a7e5c764ba8902029a1336c631a1d", 16);
+ BigInteger qValue = new BigInteger("96120477df0f3896628e6f4a88d83c93204c210ff262bccb7dae450355125259", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("169fdb2dc09f690b71332432bfec806042e258fa9a21dafe73c6abfbc71407d9",16);
+ BigInteger s = new BigInteger("9002551808ae40d19f6f31fb67e4563101243cf07cffd5f2f8ff4c537b0c9866",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ Test tests[] =
+ {
+ new GOST3410_TEST1_512(),
+ new GOST3410_TEST2_512(),
+// new GOST3410_TEST1_1024(),
+// new GOST3410_TEST2_1024(),
+// new GOST3410_AParam(),
+// new GOST3410_BParam(),
+// new GOST3410_CParam(),
+// new GOST3410_DParam(),
+// new GOST3410_AExParam(),
+// new GOST3410_BExParam(),
+// new GOST3410_CExParam()
+ };
+
+ public String getName()
+ {
+ return "GOST3410";
+ }
+
+ public TestResult perform()
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+ }
+
+ return new SimpleTestResult(true, "GOST3410: Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ GOST3410Test test = new GOST3410Test();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST3411DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST3411DigestTest.java
new file mode 100644
index 000000000..b3121e827
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/GOST3411DigestTest.java
@@ -0,0 +1,74 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.GOST3411Digest;
+import org.spongycastle.crypto.generators.PKCS5S1ParametersGenerator;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+
+public class GOST3411DigestTest
+ extends DigestTest
+{
+ private static final String[] messages =
+ {
+ "",
+ "This is message, length=32 bytes",
+ "Suppose the original message has length = 50 bytes",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ };
+
+// If S-box = D-A (see: digest/GOST3411Digest.java; function: E(byte[] in, byte[] key); string: CipherParameters param = new GOST28147Parameters(key,"D-A");)
+ private static final String[] digests =
+ {
+ "981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0",
+ "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb",
+ "c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011",
+ "73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61"
+ };
+
+// If S-box = D-Test (see: digest/GOST3411Digest.java; function:E(byte[] in, byte[] key); string: CipherParameters param = new GOST28147Parameters(key,"D-Test");)
+// private static final String[] digests =
+// {
+// "ce85b99cc46752fffee35cab9a7b0278abb4c2d2055cff685af4912c49490f8d",
+// "b1c466d37519b82e8319819ff32595e047a28cb6f83eff1c6916a815a637fffa",
+// "471aba57a60a770d3a76130635c1fbea4ef14de51f78b4ae57dd893b62f55208",
+// "95c1af627c356496d80274330b2cff6a10c67b5f597087202f94d06d2338cf8e"
+// };
+
+ // 1 million 'a'
+ static private String million_a_digest = "8693287aa62f9478f7cb312ec0866b6c4e4a0f11160441e8f4ffcd2715dd554f";
+
+ GOST3411DigestTest()
+ {
+ super(new GOST3411Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+
+ HMac gMac = new HMac(new GOST3411Digest());
+
+ gMac.init(new KeyParameter(PKCS5S1ParametersGenerator.PKCS5PasswordToUTF8Bytes("1".toCharArray())));
+
+ byte[] data = "fred".getBytes();
+
+ gMac.update(data, 0, data.length);
+ byte[] mac = new byte[gMac.getMacSize()];
+
+ gMac.doFinal(mac, 0);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new GOST3411Digest((GOST3411Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GOST3411DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Grain128Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Grain128Test.java
new file mode 100644
index 000000000..7ac877c15
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Grain128Test.java
@@ -0,0 +1,117 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.Grain128Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Grain-128 Test
+ */
+public class Grain128Test
+ extends SimpleTest
+{
+
+ String keyStream1 = "f09b7bf7d7f6b5c2de2ffc73ac21397f";
+ String keyStream2 = "afb5babfa8de896b4b9c6acaf7c4fbfd";
+
+ public String getName()
+ {
+ return "Grain-128";
+ }
+
+ public void performTest()
+ {
+ Grain128Test1(new ParametersWithIV(new KeyParameter(Hex
+ .decode("00000000000000000000000000000000")), Hex
+ .decode("000000000000000000000000")));
+ Grain128Test2(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef123456789abcdef0")), Hex
+ .decode("0123456789abcdef12345678")));
+ Grain128Test3(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef123456789abcdef0")), Hex
+ .decode("0123456789abcdef12345678")));
+ }
+
+ private void Grain128Test1(CipherParameters params)
+ {
+ StreamCipher grain = new Grain128Engine();
+ byte[] in = new byte[16];
+ byte[] out = new byte[16];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+ }
+
+ private void Grain128Test2(CipherParameters params)
+ {
+ StreamCipher grain = new Grain128Engine();
+ byte[] in = new byte[16];
+ byte[] out = new byte[16];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+ }
+
+ private void Grain128Test3(CipherParameters params)
+ {
+ StreamCipher grain = new Grain128Engine();
+ byte[] in = "Encrypt me!".getBytes();
+ byte[] cipher = new byte[in.length];
+ byte[] clear = new byte[in.length];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, cipher, 0);
+ grain.reset();
+ grain.processBytes(cipher, 0, cipher.length, clear, 0);
+
+ if (!areEqual(in, clear))
+ {
+ mismatch("Test 3", new String(Hex.encode(in)), clear);
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new Grain128Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Grainv1Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Grainv1Test.java
new file mode 100644
index 000000000..a9847d0e6
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Grainv1Test.java
@@ -0,0 +1,140 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.Grainv1Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Grain v1 Test
+ */
+public class Grainv1Test
+ extends SimpleTest
+{
+
+ String keyStream1 = "dee931cf1662a72f77d0";
+ String keyStream2 = "7f362bd3f7abae203664";
+ String keyStream4 = "017D13ECB20AE0C9ACF784CB06525F72"
+ + "CE6D52BEBB948F124668C35064559024"
+ + "49EEA505C19F3EE4D052C3D19DA9C4D1"
+ + "B92DBC7F07AFEA6A3D845DE60D8471FD";
+
+ public String getName()
+ {
+ return "Grain v1";
+ }
+
+ public void performTest()
+ {
+ Grainv1Test1(new ParametersWithIV(new KeyParameter(Hex
+ .decode("00000000000000000000")), Hex
+ .decode("0000000000000000")));
+ Grainv1Test2(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef1234")), Hex
+ .decode("0123456789abcdef")));
+ Grainv1Test3(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef1234")), Hex
+ .decode("0123456789abcdef")));
+ Grainv1Test4(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0F62B5085BAE0154A7FA")), Hex
+ .decode("288FF65DC42B92F9")));
+ }
+
+ private void Grainv1Test1(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = new byte[10];
+ byte[] out = new byte[10];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+ }
+
+ private void Grainv1Test2(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = new byte[10];
+ byte[] out = new byte[10];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+ }
+
+ private void Grainv1Test3(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = "Encrypt me!".getBytes();
+ byte[] cipher = new byte[in.length];
+ byte[] clear = new byte[in.length];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, cipher, 0);
+ grain.reset();
+ grain.processBytes(cipher, 0, cipher.length, clear, 0);
+
+ if (!areEqual(in, clear))
+ {
+ mismatch("Test 3", new String(Hex.encode(in)), clear);
+ }
+ }
+
+ private void Grainv1Test4(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = new byte[keyStream4.length() / 2];
+ byte[] out = new byte[in.length];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream4)))
+ {
+ mismatch("Keystream 4", keyStream4, out);
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new Grainv1Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HCFamilyTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HCFamilyTest.java
new file mode 100644
index 000000000..1032c4431
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HCFamilyTest.java
@@ -0,0 +1,192 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.HC128Engine;
+import org.spongycastle.crypto.engines.HC256Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * HC-128 and HC-256 Tests. Based on the test vectors in the official reference
+ * papers, respectively:
+ *
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ *
+ * See HCFamilyVecTest for a more exhaustive test based on the ecrypt vectors.
+ */
+public class HCFamilyTest
+ extends SimpleTest
+{
+ private static final byte[] MSG = new byte[64];
+
+ private static String[][] HC128_VerifiedTest =
+ {
+ {
+ "Set 2, vector# 0",
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "82001573A003FD3B7FD72FFB0EAF63AA" +
+ "C62F12DEB629DCA72785A66268EC758B" +
+ "1EDB36900560898178E0AD009ABF1F49" +
+ "1330DC1C246E3D6CB264F6900271D59C"
+ },
+ {
+ "Set 6, vector# 0",
+ "0053A6F94C9FF24598EB3E91E4378ADD",
+ "0D74DB42A91077DE45AC137AE148AF16",
+ "2E1ED12A8551C05AF41FF39D8F9DF933" +
+ "122B5235D48FC2A6F20037E69BDBBCE8" +
+ "05782EFC16C455A4B3FF06142317535E" +
+ "F876104C32445138CB26EBC2F88A684C"
+ },
+ {
+ "Set 6, vector# 1",
+ "0558ABFE51A4F74A9DF04396E93C8FE2",
+ "167DE44BB21980E74EB51C83EA51B81F",
+ "4F864BF3C96D0363B1903F0739189138" +
+ "F6ED2BC0AF583FEEA0CEA66BA7E06E63" +
+ "FB28BF8B3CA0031D24ABB511C57DD17B" +
+ "FC2861C32400072CB680DF2E58A5CECC"
+ },
+ {
+ "Set 6, vector# 2",
+ "0A5DB00356A9FC4FA2F5489BEE4194E7",
+ "1F86ED54BB2289F057BE258CF35AC128",
+ "82168AB0023B79AAF1E6B4D823855E14" +
+ "A7084378036A951B1CFEF35173875ED8" +
+ "6CB66AB8410491A08582BE40080C3102" +
+ "193BA567F9E95D096C3CC60927DD7901"
+ },
+ {
+ "Set 6, vector# 3",
+ "0F62B5085BAE0154A7FA4DA0F34699EC",
+ "288FF65DC42B92F960C72E95FC63CA31",
+ "1CD8AEDDFE52E217E835D0B7E84E2922" +
+ "D04B1ADBCA53C4522B1AA604C42856A9" +
+ "0AF83E2614BCE65C0AECABDD8975B557" +
+ "00D6A26D52FFF0888DA38F1DE20B77B7"
+ }
+ };
+
+ private static String[][] HC256_VerifiedTest =
+ {
+ {
+ "Set 2, vector# 0",
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "5B078985D8F6F30D42C5C02FA6B67951" +
+ "53F06534801F89F24E74248B720B4818" +
+ "CD9227ECEBCF4DBF8DBF6977E4AE14FA" +
+ "E8504C7BC8A9F3EA6C0106F5327E6981"
+ },
+ {
+ "Set 2, vector# 9",
+ "09090909090909090909090909090909",
+ "00000000000000000000000000000000",
+ "F5C2926651AEED9AF1A9C2F04C03D081" +
+ "2145B56AEA46EB283A25A4C9E3D8BEB4" +
+ "821B418F06F2B9DCDF1A85AB8C02CD14" +
+ "62E1BBCAEC9AB0E99AA6AFF918BA627C"
+ },
+ {
+ "Set 2, vector#135",
+ "87878787878787878787878787878787",
+ "00000000000000000000000000000000",
+ "CEC0C3852E3B98233EBCB975C10B1191" +
+ "3C69F2275EB97A1402EDF16C6FBE19BE" +
+ "79D65360445BCB63676E6553B609A065" +
+ "0155C3B22DD1975AC0F3F65063A2E16E"
+ },
+ {
+ "Set 6, vector# 0",
+ "0053A6F94C9FF24598EB3E91E4378ADD" +
+ "3083D6297CCF2275C81B6EC11467BA0D",
+ "0D74DB42A91077DE45AC137AE148AF16" +
+ "7DE44BB21980E74EB51C83EA51B81F86",
+ "23D9E70A45EB0127884D66D9F6F23C01" +
+ "D1F88AFD629270127247256C1FFF91E9" +
+ "1A797BD98ADD23AE15BEE6EEA3CEFDBF" +
+ "A3ED6D22D9C4F459DB10C40CDF4F4DFF"
+ },
+ {
+ "Set 6, vector# 1",
+ "0558ABFE51A4F74A9DF04396E93C8FE2" +
+ "3588DB2E81D4277ACD2073C6196CBF12",
+ "167DE44BB21980E74EB51C83EA51B81F" +
+ "86ED54BB2289F057BE258CF35AC1288F",
+ "C44B5262F2EAD9C018213127686DB742" +
+ "A72D3F2D61D18F0F4E7DE5B4F7ADABE0" +
+ "7E0C82033B139F02BAACB4E2F2D0BE30" +
+ "110C3A8A2B621523756692877C905DD0"
+ },
+ {
+ "Set 6, vector# 2",
+ "0A5DB00356A9FC4FA2F5489BEE4194E7" +
+ "3A8DE03386D92C7FD22578CB1E71C417",
+ "1F86ED54BB2289F057BE258CF35AC128" +
+ "8FF65DC42B92F960C72E95FC63CA3198",
+ "9D13AA06122F4F03AE60D507701F1ED0" +
+ "63D7530FF35EE76CAEDCBFB01D8A239E" +
+ "FA4A44B272DE9B4092E2AD56E87C3A60" +
+ "89F5A074D1F6E5B8FC6FABEE0C936F06"
+ },
+ {
+ "Set 6, vector# 3",
+ "0F62B5085BAE0154A7FA4DA0F34699EC" +
+ "3F92E5388BDE3184D72A7DD02376C91C",
+ "288FF65DC42B92F960C72E95FC63CA31" +
+ "98FF66CD349B0269D0379E056CD33AA1",
+ "C8632038DA61679C4685288B37D3E232" +
+ "7BC2D28C266B041FE0CA0D3CFEED8FD5" +
+ "753259BAB757168F85EA96ADABD823CA" +
+ "4684E918423E091565713FEDDE2CCFE0"
+ }
+ };
+
+ public String getName()
+ {
+ return "HC-128 and HC-256";
+ }
+
+ public void performTest()
+ {
+ StreamCipher hc = new HC256Engine();
+
+ for (int i = 0; i != HC256_VerifiedTest.length; i++)
+ {
+ String[] test = HC256_VerifiedTest[i];
+ HCTest(hc, "HC-256 - " + test[0], Hex.decode(test[1]), Hex.decode(test[2]), Hex.decode(test[3]));
+ }
+
+ hc = new HC128Engine();
+
+ for (int i = 0; i != HC128_VerifiedTest.length; i++)
+ {
+ String[] test = HC128_VerifiedTest[i];
+ HCTest(hc, "HC-128 - " + test[0], Hex.decode(test[1]), Hex.decode(test[2]), Hex.decode(test[3]));
+ }
+ }
+
+ private void HCTest(StreamCipher hc, String test, byte[] key, byte[] IV, byte[] expected)
+ {
+ KeyParameter kp = new KeyParameter(key);
+ ParametersWithIV ivp = new ParametersWithIV(kp, IV);
+
+ hc.init(true, ivp);
+ for (int i = 0; i < 64; i++)
+ {
+ if (hc.returnByte(MSG[i]) != expected[i])
+ {
+ fail(test + " failure at byte " + i);
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HCFamilyTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HCFamilyVecTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HCFamilyVecTest.java
new file mode 100644
index 000000000..a3c954f8a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HCFamilyVecTest.java
@@ -0,0 +1,199 @@
+package org.spongycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.HC128Engine;
+import org.spongycastle.crypto.engines.HC256Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * HC-128 and HC-256 Tests. Based on the test vectors in the official reference
+ * papers, respectively:
+ *
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ */
+public class HCFamilyVecTest
+ extends SimpleTest
+{
+ private static class PeekableLineReader extends BufferedReader
+ {
+ public PeekableLineReader(Reader r) throws IOException
+ {
+ super(r);
+
+ peek = super.readLine();
+ }
+
+ public String peekLine()
+ {
+ return peek;
+ }
+
+ public String readLine() throws IOException
+ {
+ String tmp = peek;
+ peek = super.readLine();
+ return tmp;
+ }
+
+ private String peek;
+ }
+
+ public String getName()
+ {
+ return "HC-128 and HC-256 (ecrypt)";
+ }
+
+ public void performTest() throws Exception
+ {
+ runTests(new HC128Engine(), "ecrypt_HC-128.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_128K_128IV.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_256K_128IV.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_128K_256IV.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_256K_256IV.txt");
+ }
+
+ private void runTests(StreamCipher hc, String fileName) throws IOException
+ {
+ Reader resource = new InputStreamReader(getClass().getResourceAsStream(fileName));
+ PeekableLineReader r = new PeekableLineReader(resource);
+ runAllVectors(hc, fileName, r);
+ }
+
+ private void runAllVectors(StreamCipher hc, String fileName, PeekableLineReader r)
+ throws IOException
+ {
+ for (;;)
+ {
+ String line = r.readLine();
+ if (line == null)
+ {
+ break;
+ }
+
+ line = line.trim();
+
+ if (line.startsWith("Set "))
+ {
+ runVector(hc, fileName, r, dellChar(line, ':'));
+ }
+ }
+ }
+
+ private String dellChar(String s, char c)
+ {
+ StringBuffer b = new StringBuffer();
+
+ for (int i = 0; i != s.length(); i++)
+ {
+ if (s.charAt(i) != c)
+ {
+ b.append(s.charAt(i));
+ }
+ }
+
+ return b.toString();
+ }
+
+ private void runVector(StreamCipher hc, String fileName, PeekableLineReader r, String vectorName)
+ throws IOException
+ {
+// System.out.println(fileName + " => " + vectorName);
+ String hexKey = readBlock(r);
+ String hexIV = readBlock(r);
+
+ CipherParameters cp = new KeyParameter(Hex.decode(hexKey));
+ cp = new ParametersWithIV(cp, Hex.decode(hexIV));
+ hc.init(true, cp);
+
+ byte[] input = new byte[64];
+ byte[] output = new byte[64];
+ byte[] digest = new byte[64];
+ int pos = 0;
+
+ for (;;)
+ {
+ String line1 = r.peekLine().trim();
+ int equalsPos = line1.indexOf('=');
+ String lead = line1.substring(0, equalsPos - 1);
+
+ String hexData = readBlock(r);
+ byte[] data = Hex.decode(hexData);
+
+ if (lead.equals("xor-digest"))
+ {
+ if (!Arrays.areEqual(data, digest))
+ {
+ fail("Failed in " + fileName + " for test vector: " + vectorName + " at " + lead);
+// System.out.println(fileName + " => " + vectorName + " failed at " + lead); return;
+ }
+ break;
+ }
+
+ int posA = lead.indexOf('[');
+ int posB = lead.indexOf("..");
+ int posC = lead.indexOf(']');
+ int start = Integer.parseInt(lead.substring(posA + 1, posB));
+ int end = Integer.parseInt(lead.substring(posB + 2, posC));
+
+ if (start % 64 != 0 || (end - start != 63))
+ {
+ throw new IllegalStateException(vectorName + ": " + lead + " not on 64 byte boundaries");
+ }
+
+ while (pos < end)
+ {
+ hc.processBytes(input, 0, input.length, output, 0);
+ xor(digest, output);
+ pos += 64;
+ }
+
+ if (!Arrays.areEqual(data, output))
+ {
+ fail("Failed in " + fileName + " for test vector: " + vectorName + " at " + lead);
+// System.out.println(fileName + " => " + vectorName + " failed at " + lead); return;
+ }
+ }
+ }
+
+ private static String readBlock(PeekableLineReader r) throws IOException
+ {
+ String first = r.readLine().trim();
+ String result = first.substring(first.lastIndexOf(' ') + 1);
+
+ for (;;)
+ {
+ String peek = r.peekLine().trim();
+ if (peek.length() < 1 || peek.indexOf('=') >= 0)
+ {
+ break;
+ }
+ result += r.readLine().trim();
+ }
+
+ return result;
+ }
+
+ private static void xor(byte[] digest, byte[] block)
+ {
+ for (int i = 0; i < digest.length; ++i)
+ {
+ digest[i] ^= block[i];
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HCFamilyVecTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HKDFGeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HKDFGeneratorTest.java
new file mode 100644
index 000000000..00564eb65
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HKDFGeneratorTest.java
@@ -0,0 +1,304 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.generators.HKDFBytesGenerator;
+import org.spongycastle.crypto.params.HKDFParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * HKDF tests - vectors from RFC 5869, + 2 more, 101 and 102
+ */
+public class HKDFGeneratorTest
+ extends SimpleTest
+{
+
+ public HKDFGeneratorTest()
+ {
+ }
+
+ private void compareOKM(int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!areEqual(calculatedOKM, testOKM))
+ {
+ fail("HKDF failed generator test " + test);
+ }
+ }
+
+ public void performTest()
+ {
+ {
+ // === A.1. Test Case 1 - Basic test case with SHA-256 ===
+
+ Digest hash = new SHA256Digest();
+ byte[] ikm = Hex
+ .decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = Hex.decode("000102030405060708090a0b0c");
+ byte[] info = Hex.decode("f0f1f2f3f4f5f6f7f8f9");
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(1, okm, Hex.decode(
+ "3cb25f25faacd57a90434f64d0362f2a" +
+ "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" +
+ "34007208d5b887185865"));
+ }
+
+ // === A.2. Test Case 2 - Test with SHA-256 and longer inputs/outputs
+ // ===
+ {
+ Digest hash = new SHA256Digest();
+ byte[] ikm = Hex.decode("000102030405060708090a0b0c0d0e0f"
+ + "101112131415161718191a1b1c1d1e1f"
+ + "202122232425262728292a2b2c2d2e2f"
+ + "303132333435363738393a3b3c3d3e3f"
+ + "404142434445464748494a4b4c4d4e4f");
+ byte[] salt = Hex.decode("606162636465666768696a6b6c6d6e6f"
+ + "707172737475767778797a7b7c7d7e7f"
+ + "808182838485868788898a8b8c8d8e8f"
+ + "909192939495969798999a9b9c9d9e9f"
+ + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
+ byte[] info = Hex.decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+ int l = 82;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(2, okm, Hex.decode(
+ "b11e398dc80327a1c8e7f78c596a4934" +
+ "4f012eda2d4efad8a050cc4c19afa97c" +
+ "59045a99cac7827271cb41c65e590e09" +
+ "da3275600c2f09b8367793a9aca3db71" +
+ "cc30c58179ec3e87c14c01d5c1f3434f" +
+ "1d87"));
+ }
+
+ {
+ // === A.3. Test Case 3 - Test with SHA-256 and zero-length
+ // salt/info ===
+
+ // setting salt to an empty byte array means that the salt is set to
+ // HashLen zero valued bytes
+ // setting info to null generates an empty byte array as info
+ // structure
+
+ Digest hash = new SHA256Digest();
+ byte[] ikm = Hex
+ .decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = new byte[0];
+ byte[] info = null;
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(3, okm, Hex.decode(
+ "8da4e775a563c18f715f802a063c5a31" +
+ "b8a11f5c5ee1879ec3454e5f3c738d2d" +
+ "9d201395faa4b61a96c8"));
+ }
+
+ {
+ // === A.4. Test Case 4 - Basic test case with SHA-1 ===
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = Hex.decode("000102030405060708090a0b0c");
+ byte[] info = Hex.decode("f0f1f2f3f4f5f6f7f8f9");
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(4, okm, Hex.decode(
+ "085a01ea1b10f36933068b56efa5ad81" +
+ "a4f14b822f5b091568a9cdd4f155fda2" +
+ "c22e422478d305f3f896"));
+ }
+
+ // === A.5. Test Case 5 - Test with SHA-1 and longer inputs/outputs ===
+ {
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex.decode("000102030405060708090a0b0c0d0e0f"
+ + "101112131415161718191a1b1c1d1e1f"
+ + "202122232425262728292a2b2c2d2e2f"
+ + "303132333435363738393a3b3c3d3e3f"
+ + "404142434445464748494a4b4c4d4e4f");
+ byte[] salt = Hex.decode("606162636465666768696a6b6c6d6e6f"
+ + "707172737475767778797a7b7c7d7e7f"
+ + "808182838485868788898a8b8c8d8e8f"
+ + "909192939495969798999a9b9c9d9e9f"
+ + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
+ byte[] info = Hex.decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+ int l = 82;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(5, okm, Hex.decode(
+ "0bd770a74d1160f7c9f12cd5912a06eb" +
+ "ff6adcae899d92191fe4305673ba2ffe" +
+ "8fa3f1a4e5ad79f3f334b3b202b2173c" +
+ "486ea37ce3d397ed034c7f9dfeb15c5e" +
+ "927336d0441f4c4300e2cff0d0900b52" +
+ "d3b4"));
+ }
+
+ {
+ // === A.6. Test Case 6 - Test with SHA-1 and zero-length salt/info
+ // ===
+
+ // setting salt to null should generate a new salt of HashLen zero
+ // valued bytes
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = null;
+ byte[] info = new byte[0];
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(6, okm, Hex.decode(
+ "0ac1af7002b3d761d1e55298da9d0506" +
+ "b9ae52057220a306e07b6b87e8df21d0" +
+ "ea00033de03984d34918"));
+ }
+
+ {
+ // === A.7. Test Case 7 - Test with SHA-1, salt not provided,
+ // zero-length info ===
+ // (salt defaults to HashLen zero octets)
+
+ // this test is identical to test 6 in all ways bar the IKM value
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c");
+ byte[] salt = null;
+ byte[] info = new byte[0];
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(7, okm, Hex.decode(
+ "2c91117204d745f3500d636a62f64f0a" +
+ "b3bae548aa53d423b0d1f27ebba6f5e5" +
+ "673a081d70cce7acfc48"));
+ }
+
+ {
+ // === A.101. Additional Test Case - Test with SHA-1, skipping extract
+ // zero-length info ===
+ // (salt defaults to HashLen zero octets)
+
+ // this test is identical to test 7 in all ways bar the IKM value
+ // which is set to the PRK value
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("2adccada18779e7c2077ad2eb19d3f3e731385dd");
+ byte[] info = new byte[0];
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = HKDFParameters.skipExtractParameters(ikm, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(101, okm, Hex.decode(
+ "2c91117204d745f3500d636a62f64f0a" +
+ "b3bae548aa53d423b0d1f27ebba6f5e5" +
+ "673a081d70cce7acfc48"));
+ }
+
+ // === A.102. Additional Test Case - Test with SHA-1, maximum output ===
+ // (salt defaults to HashLen zero octets)
+
+ // this test is identical to test 7 in all ways bar the IKM value
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("2adccada18779e7c2077ad2eb19d3f3e731385dd");
+ byte[] info = new byte[0];
+ int l = 255 * hash.getDigestSize();
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = HKDFParameters.skipExtractParameters(ikm, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ int zeros = 0;
+ for (int i = 0; i < hash.getDigestSize(); i++)
+ {
+ if (okm[i] == 0)
+ {
+ zeros++;
+ }
+ }
+
+ if (zeros == hash.getDigestSize())
+ {
+ fail("HKDF failed generator test " + 102);
+ }
+ }
+
+ public String getName()
+ {
+ return "HKDF";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new HKDFGeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HashCommitmentTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HashCommitmentTest.java
new file mode 100644
index 000000000..d4c1c0768
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/HashCommitmentTest.java
@@ -0,0 +1,152 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.Commitment;
+import org.spongycastle.crypto.Committer;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.commitments.GeneralHashCommitter;
+import org.spongycastle.crypto.commitments.HashCommitter;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class HashCommitmentTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "HashCommitmentTest";
+ }
+
+ public void performBasicTest()
+ throws Exception
+ {
+ byte[] data = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+
+ Committer committer = new HashCommitter(new SHA256Digest(), new SecureRandom());
+
+ Commitment c = committer.commit(data);
+
+ committer = new HashCommitter(new SHA256Digest(), new SecureRandom());
+
+ if (!committer.isRevealed(c, data))
+ {
+ fail("commitment failed to validate");
+ }
+
+ committer = new HashCommitter(new SHA1Digest(), new SecureRandom());
+
+ if (committer.isRevealed(c, data))
+ {
+ fail("commitment validated!!");
+ }
+
+ try
+ {
+ committer.isRevealed(c, new byte[data.length + 1]);
+ }
+ catch (Exception e)
+ {
+ if (!e.getMessage().equals("Message and witness secret lengths do not match."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+
+ // SHA1 has a block size of 512 bits, try a message that's too big
+
+ try
+ {
+ c = committer.commit(new byte[33]);
+ }
+ catch (DataLengthException e)
+ {
+ if (!e.getMessage().equals("Message to be committed to too large for digest."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+ }
+
+ public void performGeneralTest()
+ throws Exception
+ {
+ byte[] data = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+
+ Committer committer = new GeneralHashCommitter(new SHA256Digest(), new SecureRandom());
+
+ Commitment c = committer.commit(data);
+
+ committer = new GeneralHashCommitter(new SHA256Digest(), new SecureRandom());
+
+ if (!committer.isRevealed(c, data))
+ {
+ fail("general commitment failed to validate");
+ }
+
+ committer = new GeneralHashCommitter(new SHA1Digest(), new SecureRandom());
+
+ if (committer.isRevealed(c, data))
+ {
+ fail("general commitment validated!!");
+ }
+
+ c = committer.commit(data);
+
+ // try and fool it.
+ byte[] s = c.getSecret();
+ byte[] newS = Arrays.copyOfRange(s, 0, s.length - 1);
+ byte[] newData = new byte[data.length + 1];
+
+ newData[0] = s[s.length - 1];
+ System.arraycopy(data, 0, newData, 1, data.length);
+
+ c = new Commitment(newS, c.getCommitment());
+
+ if (committer.isRevealed(c, newData))
+ {
+ fail("general commitment validated!!");
+ }
+
+ try
+ {
+ committer.isRevealed(c, new byte[data.length + 1]);
+ }
+ catch (Exception e)
+ {
+ if (!e.getMessage().equals("Message and witness secret lengths do not match."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+
+ // SHA1 has a block size of 512 bits, try a message that's too big
+
+ try
+ {
+ c = committer.commit(new byte[33]);
+ }
+ catch (DataLengthException e)
+ {
+ if (!e.getMessage().equals("Message to be committed to too large for digest."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ performBasicTest();
+ performGeneralTest();
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HashCommitmentTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/IDEATest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/IDEATest.java
new file mode 100644
index 000000000..beb0e4895
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/IDEATest.java
@@ -0,0 +1,38 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.IDEAEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ */
+public class IDEATest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new IDEAEngine(),
+ new KeyParameter(Hex.decode("00112233445566778899AABBCCDDEEFF")),
+ "000102030405060708090a0b0c0d0e0f", "ed732271a7b39f475b4b2b6719f194bf"),
+ new BlockCipherVectorTest(0, new IDEAEngine(),
+ new KeyParameter(Hex.decode("00112233445566778899AABBCCDDEEFF")),
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "b8bc6ed5c899265d2bcfad1fc6d4287d")
+ };
+
+ IDEATest()
+ {
+ super(tests, new IDEAEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "IDEA";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new IDEATest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISAACTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISAACTest.java
new file mode 100644
index 000000000..e2f9f1242
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISAACTest.java
@@ -0,0 +1,180 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.ISAACEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ISAAC Test - see http://www.burtleburtle.net/bob/rand/isaacafa.html
+ */
+public class ISAACTest
+ extends SimpleTest
+{
+ byte[] out = Hex.decode(
+ "f650e4c8e448e96d98db2fb4f5fad54f433f1afbedec154ad837048746ca4f9a" +
+ "5de3743e88381097f1d444eb823cedb66a83e1e04a5f6355c744243325890e2e" +
+ "7452e31957161df638a824f3002ed71329f5544951c08d83d78cb99ea0cc74f3" +
+ "8f651659cbc8b7c2f5f71c6912ad6419e5792e1b860536b809b3ce98d45d6d81" +
+ "f3b2612917e38f8529cf72ce349947b0c998f9ffb5e13dae32ae2a2bf7cf814c" +
+ "8ebfa303cf22e0640b923200eca4d58aef53cec4d0f7b37d9c411a2affdf8a80" +
+ "b40e27bcb4d2f97644b89b08f37c71d51a70e7e90bdb9c3060dc5207b3c3f24b" +
+ "d7386806229749b54e232cd091dabc65a70e11018b87437e5781414fcdbc62e2" +
+ "8107c9ff69d2e4ae3b18e752b143b6886f4e077295138769943c3c74afc17a97" +
+ "0fd439636a529b0bd8c58a6aa8bcc22d2db35dfea7a2f4026cb167db538e1f4e" +
+ "7275e2771d3b8e97ecc5dc9115e3a5b90369661430ab93ecac9fe69d7bc76811" +
+ "60eda8da28833522d5295ebc5adb60e7f7e1cdd097166d14b67ec13a210f3925" +
+ "64af0fef0d0286843aea3decb058bafbb8b0ccfcf2b5cc05e3a662d9814bc24c" +
+ "2364a1aa37c0ed052b36505c451e7ec85d2a542fe43d0fbb91c8d92560d4d5f8" +
+ "12a0594b9e8a51dacd49ebdb1b0dcdc1cd57c7f7e63444517ded386f2f36fa86" +
+ "a6d1210133bc405db388d96cdb6dbe96fe29661c13edc0cbcb0eee4a70cc94ae" +
+ "de11ed340606cf9f3a6ce38923d74f4ea37f63ff917bdec2d73f72d40e7e0e67" +
+ "3d77d9a213add9228891b3db01a9bd7056a001e3d51f093dcc033ce35ad0d3b0" +
+ "34105a8c6a123f57bd2e50247364944be89b1a3b21835c4d9f39e2d9d405ded8" +
+ "294d37e5bccaaeed35a124b56708a2bcb00960ba2a98121a4d8fae820bb3263f" +
+ "12595a196a1075890809e49421c171ec884d682514c8009bb0b84e7b03fb88f4" +
+ "28e7cb789388b13bdd2dc1d5848f520a07c28cd168a3935872c9137d127dd430" +
+ "c613f1578c2f0d55f7d3f39f309bfb788406b13746c0a6f53718d59708607f04" +
+ "76904b6d04db4e13cd7411a7b510ce0ebfc7f7ccb83f957afdfef62dc35e4580" +
+ "3ff1e5244112d96c02c9b944d5990dfbe7e265810d9c7e7e826dfa8966f1e0ab" +
+ "30bcc764eadebeaced35e5ee0c571a7de4f3a26af7f58f7badf6bc235d023e65" +
+ "1ed3ff4eec46b0b6d2a93b51e75b41c97e315aeb61119a5a53245b7933f6d7b1" +
+ "cae8deba50fc8194afa92a6dc87c80064188bfcd8bace62e78ffa5685597ec0f" +
+ "b4415f7d08294766ad56764309c36f903dde9f394a0a283c18080c8e080c79ec" +
+ "79ae4c10cb9e15637cdd662f62d31911a4ca0cf15cf824cd3b708f991e16614c" +
+ "b6b9d7665de87abb7229ea81d5b2d75056e6cd21fe1e42d596da2655c2b9aa36" +
+ "b8f6fd4a6a158d1001913fd3af7d1fb80b5e435f90c107576554abda7a68710f" +
+ "82ac484fd7e1c7be95c85eaa94a302f44d3cfbda786b29081010b27582d53d12" +
+ "21e2a51c3d1e9150b059261dd0638e1a31860f0581f2864dff4cfc350451516d" +
+ "bd086f26bc5654c165dfa427a82427f5582e3014b8d2486dc79a17499a1d7745" +
+ "8766bb541e04a7f73d3dff8ad5ec6bf4dbef7d9f36ec0ea31feb2e4f15cfcc5c" +
+ "d8c423fbd0ef3cc9eb244925ba5590c8a5f48ac433c5321c613b67b2479c3a22" +
+ "e21339cc10d210aa931dd7e2ef05ee06b82f2703a385cb2c5d67133c877eb7b4" +
+ "1e3437f75afb43ae53c078f394d904811d96458908063a85e13222281956b1e5" +
+ "31860f132e7b022f21182ca396f703ac46819e2e0d28fe523724d4dca0eabe6b" +
+ "c66699fdc6112fdd19c1e69c04d3658a4b55dd9931907d62f854b5224d678f26" +
+ "22ae0582eafed133e4a51d2184bd6dd6c1a513753f28ee63fb737b1a70a1660e" +
+ "8a8dfaa31be79937f7476978513c1764531ac6bf12c06908001cdb951a4b6a53" +
+ "d067fce512b2cfb69ddb477f740e006639ddf25acc8bfa2df1b20eaf64f2632c" +
+ "9783cdee63bfd4d80084cfe575f4e9e219b48fd06c48ddd87a36af9371865c4c" +
+ "9ce0199d867027d72cb7b77f84ef01da72f5972f040f7074df9afa29c921f94e" +
+ "75c08a3618c1ef9ad649a428c5b719378a30738ad97cd348858129a6239e3b0a" +
+ "bbb8abc480fac4c2ecfcf20bd9d711f9e2a4ef71b5fe87c0be8b06b2aafef5a7" +
+ "9c15db3b0aeb81654389a84a253b1d7a19047c797cdc78a2d20adf0356f55a71" +
+ "3e730fa8fd8650d8959e234eb7546681dad1b22a142a6e858ef4bce668235b9d" +
+ "85a13f8574096ae7a949bea229322d0dd568385882846526403dae086dd1943a" +
+ "e1279bff9e7e4f041c3a4524484525e481d4cc5fe24124c0037464c0bf1bd691" +
+ "26ceb003275ead3ac5bde90826414ff3a30519add7b43abe2ce5d3d588412761" +
+ "97ca2070e5fbb9c7276df0b4308f751f37a97df6c9cd808cfe4cb3803d469303" +
+ "aee19096c0d5d42a4e823ad3f5f9cc3b4286619c9ca45e1c66c97340891aec49" +
+ "45bae606c798f04752649d6cce86fdfc80c6e402d6ec2f2b27c822821fe26ce0" +
+ "92f57ea7de462f4d07497cae5a48755c721502dd6cbe7935836d80039ead7f70" +
+ "9ab3a42f4c8652d632e39273e8fa38601da4f25a0cd6ef8102503f7d8854a0a1" +
+ "9a30c4e88815715305efe29457c4c9252887d96fc1a71e3ce9f841632d0985de" +
+ "d21e796c6fb5ce5602614abfc3c7be2cb54fed6fa617a083c3142d8f6079e4ce" +
+ "ceffc1471d0cb81bdc153e5fe36ef5bbd531161a165b10157aa114ed3f7579b3" +
+ "f7f395f1bc6172c7a86f875e0e6c51b3cdfec2af73c0e762824c2009c5a87748" +
+ "94d401258aba3ffbd32be0608c17eff021e2547e07cffad905340e15f3310c92" +
+ "9d8d190886ba527ff943f672ef73fbf046d95ca5c54cd95b9d855e894bb5af29");
+
+ byte[] outFFFFFFFF = Hex.decode(
+ "de3b3f3c19e0629c1fc8b7836695d523e7804edd86ff7ce9b106f52caebae9d9" +
+ "72f845d49ce17d7da44e49bae954aac0d0b1284b98a88eec1524fb6bc91a16b5" +
+ "1192ac5334131446ac2442de9ff3d5867b9b9148881ee30a6e87dd88e5d1f7cd" +
+ "98db31ff36f70d9850cfefaef42abb00ecc39ed308bf4b8030cdc2b6b7e42f0e" +
+ "908030dd282f96edacc888b3a986e109c129998f89baa1b5da8970b07a6ab012" +
+ "f10264f23c315c9c8e0c164955c68517b6a4f982b2626db70787f869ac6d551b" +
+ "e34931627c7058e965c502e18d2cd370e6db3b70d947d61aa9717cf8394f48c6" +
+ "3c796f3a154950846badb28b70d982f29bc670254e3e5e0f8e36b0a5f6da0a04" +
+ "6b235ed6a42988c012bde74d879fa8eb5d59f5f40ed5e76601c9847b3edb2690");
+
+ byte[] outFFFF0000 = Hex.decode(
+ "26c54b1f8c4e3fc582e9e8180f7aba5380463dcf58b03cbeda0ecc8ba90ccff8" +
+ "5bd50896313d7efed44015faeac6964b241a7fb8a2e37127a7cbea0fd7c020f2" +
+ "406371b87ef5185089504751e5e44352eff63e00e5c28f5dff0616a9a3a00f1f" +
+ "4a1350e3a17be9abddfc2c94571450a0dc4c3c0c7c7f98e80c95f607d50c676a" +
+ "9a3006f9d279a79a4d66b2ab0c52930c9ee84bc09895e70fa041b1a3a2966f11" +
+ "6a47fd09705124b1f5c7ae055e54536e66584b1608f3612d81b72f109a385831" +
+ "121945b207b90ac72437a248f27a121c2801f4153a8699fb047e193f7ba69e1b" +
+ "b117869675d4c963e6070c2ca3d332ce830cb5e3d9ed2eee7faf0acc20fbe154" +
+ "188ae789e95bd5c1f459dbd150aab6eb833170257084bc5d44e9df09f5624f9d" +
+ "afecd0c9340ac8587f8625d343f7efd1cc8abcf7a6f90eabd4e8e2d906278d6e" +
+ "431fcade165c8c467887fbf5c26d341557b064b98c60dd40ab262dc046d69647" +
+ "56f3ddc1a07ae5f87be878b9334fcde40add68d2ca1dc05fb1670f998c7c4607" +
+ "9a6e48bdb330ad8d30b61b5cc8dc156f5733905931949783f89ac396b65aa4b8" +
+ "51f746b53ed8ea66130e1d75e8eab136e60450e3e600226bc8e17d03744ce94c" +
+ "0eec9234fea5f18eef65d81f2f10cfbc0b112b8cde17c32eb33ed81d7356eac3" +
+ "eb1cb9cefa6604c2d707949b6e5a83e60705bf6aae76dcc7d35d68ff149c1ac5" +
+ "424bb4a39e2f496f886637fce3db4ba4ad12c1a32d25e1606f6635ff636486f6" +
+ "714997b45477f38813c02afce4bebf196b813332f0decd567c745f441e736364");
+
+ byte[] out0000FFFF = Hex.decode(
+ "bc31712f2a2f467a5abc737c57ce0f8d49d2f775eb850fc8f856daf19310fee2"+
+ "5bab40e78403c9ef4ccd971418992faf4e85ca643fa6b482f30c4659066158a6"+
+ "5bc3e620ba7ea5c34dd0eac5aabb2cf078d915fd1f8c437ed00423076c10f701"+
+ "eefa7fc7c461aca5db8a87be29d925c4212d4adcfa71ff5b06af15c048aa0dfd"+
+ "f0e645bc09fea200c430a88eb38c466ff358b836f1159656a078f6fc752f6db1"+
+ "6680bb30fc771a6a785bbb2298e947d7b3500e557775962248bedf4e82c16e66"+
+ "f39283ccb95e5399061056a11c4a280f00f7487888199487905273c7aa13012b"+
+ "4849eca626cbf071c782e084f9fded57de92313e5f61a6e81117fb1115eff275"+
+ "66fd5c755bb3b01bba69aeb8f1b1b1cc9709734be31b35bc707d372ba6fe70d1"+
+ "e2c3b0e5e74a7058faff6b11d3a168f19fecc9fcb36b3e6a5f828c01c22ac0c2"+
+ "5da2a3a9eec7e0ebbbf51472e430ed4cf1c7ab57ef9aea511e40250846d260b6"+
+ "17a3fdeba16cf4afaf700144d3296b58b22a3c79ed96f3e2fc8d9e3c660ae153"+
+ "8e0c285ccdc48b59117e80413bd0ad24c6a8d4f133fe1496f14351bb89904fa5"+
+ "e10c4b8d50e0604578389c336a9ab3d292beb90ce640fc028e697cf54e021e2f"+
+ "c0ca3fe0471fde5e5462f221739a74f5a13ae0621fe2a82e752bc294f63de48d"+
+ "e85430af71307a30441b861ab5380e6a6dbe1251c9baa567da14e38e5a0ccddf"+
+ "0127205c38fc3b77065e98101d219246103438d223ec7f8f533d4bb3a3d3407a"+
+ "944910f11e8e5492e86de7a0471250eca32f0838b3db02fffe71898712af3261");
+
+ public String getName()
+ {
+ return "ISAAC";
+ }
+
+ public void performTest()
+ {
+ ISAACEngine engine = new ISAACEngine();
+
+ doTest(engine, Hex.decode("00000000"), out);
+ doTest(engine, Hex.decode("ffffffff"), outFFFFFFFF);
+
+ byte[] k = new byte[256 * 4];
+ for (int i = 0; i != k.length; i++)
+ {
+ k[i] = (byte)((i % 4 == 0 || i % 4 == 1) ? 0xff : 0x00);
+ }
+ doTest(engine, k, outFFFF0000);
+ k = new byte[256 * 4];
+ for (int i = 0; i != k.length; i++)
+ {
+ k[i] = (byte)((i % 4 == 2 || i % 4 == 3) ? 0xff : 0x00);
+ }
+ doTest(engine, k, out0000FFFF);
+ }
+
+ private void doTest(ISAACEngine engine, byte[] key, byte[] output)
+ {
+ byte[] in = new byte[output.length];
+ byte[] enc = new byte[output.length];
+ engine.init(true, new KeyParameter(key));
+ engine.processBytes(in, 0, in.length, enc, 0);
+ if (!areEqual(enc, output))
+ {
+ fail("ciphertext mismatch");
+ }
+ engine.init(false, new KeyParameter(key));
+ engine.processBytes(enc, 0, enc.length, enc, 0);
+ if (!areEqual(enc, in))
+ {
+ fail("plaintext mismatch");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ISAACTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISO9796Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISO9796Test.java
new file mode 100644
index 000000000..109696bc1
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISO9796Test.java
@@ -0,0 +1,972 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.RIPEMD128Digest;
+import org.spongycastle.crypto.digests.RIPEMD160Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.encodings.ISO9796d1Encoding;
+import org.spongycastle.crypto.engines.RSABlindedEngine;
+import org.spongycastle.crypto.engines.RSAEngine;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithSalt;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.signers.ISO9796d2PSSSigner;
+import org.spongycastle.crypto.signers.ISO9796d2Signer;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * test vectors from ISO 9796-1 and ISO 9796-2 edition 1.
+ */
+public class ISO9796Test
+ extends SimpleTest
+{
+ static BigInteger mod1 = new BigInteger("0100000000000000000000000000000000bba2d15dbb303c8a21c5ebbcbae52b7125087920dd7cdf358ea119fd66fb064012ec8ce692f0a0b8e8321b041acd40b7", 16);
+
+ static BigInteger pub1 = new BigInteger("03", 16);
+
+ static BigInteger pri1 = new BigInteger("2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac9f0783a49dd5f6c5af651f4c9d0dc9281c96a3f16a85f9572d7cc3f2d0f25a9dbf1149e4cdc32273faadd3fda5dcda7", 16);
+
+ static BigInteger mod2 = new BigInteger("ffffff7fa27087c35ebead78412d2bdffe0301edd494df13458974ea89b364708f7d0f5a00a50779ddf9f7d4cb80b8891324da251a860c4ec9ef288104b3858d", 16);
+
+ static BigInteger pub2 = new BigInteger("03", 16);
+
+ static BigInteger pri2 = new BigInteger("2aaaaa9545bd6bf5e51fc7940adcdca5550080524e18cfd88b96e8d1c19de6121b13fac0eb0495d47928e047724d91d1740f6968457ce53ec8e24c9362ce84b5", 16);
+
+ static byte msg1[] = Hex.decode("0cbbaa99887766554433221100");
+
+ //
+ // you'll need to see the ISO 9796 to make sense of this
+ //
+ static byte sig1[] = mod1.subtract(new BigInteger("309f873d8ded8379490f6097eaafdabc137d3ebfd8f25ab5f138d56a719cdc526bdd022ea65dabab920a81013a85d092e04d3e421caab717c90d89ea45a8d23a", 16)).toByteArray();
+
+ static byte msg2[] = Hex.decode("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
+
+ static byte sig2[] = new BigInteger("319bb9becb49f3ed1bca26d0fcf09b0b0a508e4d0bd43b350f959b72cd25b3af47d608fdcd248eada74fbe19990dbeb9bf0da4b4e1200243a14e5cab3f7e610c", 16).toByteArray();
+
+ static byte msg3[] = Hex.decode("0112233445566778899aabbccd");
+
+ static byte sig3[] = mod2.subtract(new BigInteger("58e59ffb4b1fb1bcdbf8d1fe9afa3730c78a318a1134f5791b7313d480ff07ac319b068edf8f212945cb09cf33df30ace54f4a063fcca0b732f4b662dc4e2454", 16)).toByteArray();
+
+ //
+ // ISO 9796-2
+ //
+ static BigInteger mod3 = new BigInteger("ffffffff78f6c55506c59785e871211ee120b0b5dd644aa796d82413a47b24573f1be5745b5cd9950f6b389b52350d4e01e90009669a8720bf265a2865994190a661dea3c7828e2e7ca1b19651adc2d5", 16);
+
+ static BigInteger pub3 = new BigInteger("03", 16);
+
+ static BigInteger pri3 = new BigInteger("2aaaaaaa942920e38120ee965168302fd0301d73a4e60c7143ceb0adf0bf30b9352f50e8b9e4ceedd65343b2179005b2f099915e4b0c37e41314bb0821ad8330d23cba7f589e0f129b04c46b67dfce9d", 16);
+
+ static BigInteger mod4 = new BigInteger("FFFFFFFF45f1903ebb83d4d363f70dc647b839f2a84e119b8830b2dec424a1ce0c9fd667966b81407e89278283f27ca8857d40979407fc6da4cc8a20ecb4b8913b5813332409bc1f391a94c9c328dfe46695daf922259174544e2bfbe45cc5cd", 16);
+ static BigInteger pub4 = new BigInteger("02", 16);
+ static BigInteger pri4 = new BigInteger("1fffffffe8be3207d7707a9a6c7ee1b8c8f7073e5509c2337106165bd8849439c193faccf2cd70280fd124f0507e4f94cb66447680c6b87b6599d1b61c8f3600854a618262e9c1cb1438e485e47437be036d94b906087a61ee74ab0d9a1accd8", 16);
+
+ static byte msg4[] = Hex.decode("6162636462636465636465666465666765666768666768696768696a68696a6b696a6b6c6a6b6c6d6b6c6d6e6c6d6e6f6d6e6f706e6f7071");
+ static byte sig4[] = Hex.decode("374695b7ee8b273925b4656cc2e008d41463996534aa5aa5afe72a52ffd84e118085f8558f36631471d043ad342de268b94b080bee18a068c10965f581e7f32899ad378835477064abed8ef3bd530fce");
+
+ static byte msg5[] = Hex.decode("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
+ static byte sig5[] = Hex.decode("5cf9a01854dbacaec83aae8efc563d74538192e95466babacd361d7c86000fe42dcb4581e48e4feb862d04698da9203b1803b262105104d510b365ee9c660857ba1c001aa57abfd1c8de92e47c275cae");
+
+ //
+ // scheme 2 data
+ //
+ static BigInteger mod6 = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ static BigInteger pub6 = new BigInteger("11", 16);
+ static BigInteger pri6 = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
+
+ static byte sig6[] = new BigInteger("0073FEAF13EB12914A43FE635022BB4AB8188A8F3ABD8D8A9E4AD6C355EE920359C7F237AE36B1212FE947F676C68FE362247D27D1F298CA9302EB21F4A64C26CE44471EF8C0DFE1A54606F0BA8E63E87CDACA993BFA62973B567473B4D38FAE73AB228600934A9CC1D3263E632E21FD52D2B95C5F7023DA63DE9509C01F6C7BBC", 16).modPow(pri6, mod6).toByteArray();
+
+ static byte msg7[] = Hex.decode("6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F70716F70717270717273");
+ static byte sig7[] = new BigInteger("296B06224010E1EC230D4560A5F88F03550AAFCE31C805CE81E811E5E53E5F71AE64FC2A2A486B193E87972D90C54B807A862F21A21919A43ECF067240A8C8C641DE8DCDF1942CF790D136728FFC0D98FB906E7939C1EC0E64C0E067F0A7443D6170E411DF91F797D1FFD74009C4638462E69D5923E7433AEC028B9A90E633CC", 16).modPow(pri6, mod6).toByteArray();
+
+ static byte msg8[] = Hex.decode("FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA98");
+ static byte sig8[] = new BigInteger("01402B29ABA104079677CE7FC3D5A84DB24494D6F9508B4596484F5B3CC7E8AFCC4DDE7081F21CAE9D4F94D6D2CCCB43FCEDA0988FFD4EF2EAE72CFDEB4A2638F0A34A0C49664CD9DB723315759D758836C8BA26AC4348B66958AC94AE0B5A75195B57ABFB9971E21337A4B517F2E820B81F26BCE7C66F48A2DB12A8F3D731CC", 16).modPow(pri6, mod6).toByteArray();
+
+ static byte msg9[] = Hex.decode("6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F70716F707172707172737172737472737475737475767475767775767778767778797778797A78797A61797A61627A6162636162636462636465");
+ static byte sig9[] = new BigInteger("6F2BB97571FE2EF205B66000E9DD06656655C1977F374E8666D636556A5FEEEEAF645555B25F45567C4EE5341F96FED86508C90A9E3F11B26E8D496139ED3E55ECE42860A6FB3A0817DAFBF13019D93E1D382DA07264FE99D9797D2F0B7779357CA7E74EE440D8855B7DDF15F000AC58EE3FFF144845E771907C0C83324A6FBC", 16).modPow(pri6, mod6).toByteArray();
+
+ public String getName()
+ {
+ return "ISO9796";
+ }
+
+ private boolean isSameAs(
+ byte[] a,
+ int off,
+ byte[] b)
+ {
+ if ((a.length - off) != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != b.length; i++)
+ {
+ if (a[i + off] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean startsWith(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length < b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != b.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void doTest1()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod1, pub1);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod1, pri1);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-1 - public encrypt, private decrypt
+ //
+ ISO9796d1Encoding eng = new ISO9796d1Encoding(rsa);
+
+ eng.init(true, privParameters);
+
+ eng.setPadBits(4);
+
+ data = eng.processBlock(msg1, 0, msg1.length);
+
+ eng.init(false, pubParameters);
+
+ if (!areEqual(sig1, data))
+ {
+ fail("failed ISO9796-1 generation Test 1");
+ }
+
+ data = eng.processBlock(data, 0, data.length);
+
+ if (!areEqual(msg1, data))
+ {
+ fail("failed ISO9796-1 retrieve Test 1");
+ }
+ }
+
+ private void doTest2()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod1, pub1);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod1, pri1);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-1 - public encrypt, private decrypt
+ //
+ ISO9796d1Encoding eng = new ISO9796d1Encoding(rsa);
+
+ eng.init(true, privParameters);
+
+ data = eng.processBlock(msg2, 0, msg2.length);
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(data, 1, sig2))
+ {
+ fail("failed ISO9796-1 generation Test 2");
+ }
+
+ data = eng.processBlock(data, 0, data.length);
+
+
+ if (!areEqual(msg2, data))
+ {
+ fail("failed ISO9796-1 retrieve Test 2");
+ }
+ }
+
+ public void doTest3()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod2, pub2);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod2, pri2);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-1 - public encrypt, private decrypt
+ //
+ ISO9796d1Encoding eng = new ISO9796d1Encoding(rsa);
+
+ eng.init(true, privParameters);
+
+ eng.setPadBits(4);
+
+ data = eng.processBlock(msg3, 0, msg3.length);
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig3, 1, data))
+ {
+ fail("failed ISO9796-1 generation Test 3");
+ }
+
+ data = eng.processBlock(data, 0, data.length);
+
+ if (!isSameAs(msg3, 0, data))
+ {
+ fail("failed ISO9796-1 retrieve Test 3");
+ }
+ }
+
+ public void doTest4()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod3, pub3);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod3, pri3);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - Signing
+ //
+ ISO9796d2Signer eng = new ISO9796d2Signer(rsa, new RIPEMD128Digest());
+
+ eng.init(true, privParameters);
+
+ eng.update(msg4[0]);
+ eng.update(msg4, 1, msg4.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig4, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 4");
+ }
+
+ eng.update(msg4[0]);
+ eng.update(msg4, 1, msg4.length - 1);
+
+ if (!eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 verify Test 4");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ eng = new ISO9796d2Signer(rsa, new RIPEMD128Digest());
+
+ eng.init(false, pubParameters);
+
+ if (!eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 verify and recover Test 4");
+ }
+
+ if (!isSameAs(eng.getRecoveredMessage(), 0, msg4))
+ {
+ fail("failed ISO9796-2 recovered message Test 4");
+ }
+
+ // try update with recovered
+ eng.updateWithRecoveredMessage(sig4);
+
+ if (!isSameAs(eng.getRecoveredMessage(), 0, msg4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered recovered message Test 4");
+ }
+
+ if (!eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered verify and recover Test 4");
+ }
+
+ if (!isSameAs(eng.getRecoveredMessage(), 0, msg4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered recovered verify message Test 4");
+ }
+
+ // should fail
+ eng.updateWithRecoveredMessage(sig4);
+
+ eng.update(msg4, 0, msg4.length);
+
+ if (eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered verify and recover Test 4");
+ }
+ }
+ else
+ {
+ fail("full message flag false - Test 4");
+ }
+ }
+
+ public void doTest5()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod3, pub3);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod3, pri3);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - Signing
+ //
+ ISO9796d2Signer eng = new ISO9796d2Signer(rsa, new RIPEMD160Digest(), true);
+
+ eng.init(true, privParameters);
+
+ eng.update(msg5[0]);
+ eng.update(msg5, 1, msg5.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig5, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 5");
+ }
+
+ eng.update(msg5[0]);
+ eng.update(msg5, 1, msg5.length - 1);
+
+ if (!eng.verifySignature(sig5))
+ {
+ fail("failed ISO9796-2 verify Test 5");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ fail("fullMessage true - Test 5");
+ }
+
+ if (!startsWith(msg5, eng.getRecoveredMessage()))
+ {
+ fail("failed ISO9796-2 partial recovered message Test 5");
+ }
+
+ int length = eng.getRecoveredMessage().length;
+
+ if (length >= msg5.length)
+ {
+ fail("Test 5 recovered message too long");
+ }
+
+ eng = new ISO9796d2Signer(rsa, new RIPEMD160Digest(), true);
+
+ eng.init(false, pubParameters);
+
+ eng.updateWithRecoveredMessage(sig5);
+
+ if (!startsWith(msg5, eng.getRecoveredMessage()))
+ {
+ fail("failed ISO9796-2 updateWithRecovered partial recovered message Test 5");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ fail("fullMessage updateWithRecovered true - Test 5");
+ }
+
+ for (int i = length; i != msg5.length; i++)
+ {
+ eng.update(msg5[i]);
+ }
+
+ if (!eng.verifySignature(sig5))
+ {
+ fail("failed ISO9796-2 verify Test 5");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ fail("fullMessage updateWithRecovered true - Test 5");
+ }
+
+ // should fail
+ eng.updateWithRecoveredMessage(sig5);
+
+ eng.update(msg5, 0, msg5.length);
+
+ if (eng.verifySignature(sig5))
+ {
+ fail("failed ISO9796-2 updateWithRecovered verify fail Test 5");
+ }
+ }
+
+ //
+ // against a zero length string
+ //
+
+ public void doTest6()
+ throws Exception
+ {
+ byte[] salt = Hex.decode("61DF870C4890FE85D6E3DD87C3DCE3723F91DB49");
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ ParametersWithSalt sigParameters = new ParametersWithSalt(privParameters, salt);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new RIPEMD160Digest(), 20, true);
+
+ eng.init(true, sigParameters);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig6, 1, data))
+ {
+ fail("failed ISO9796-2 generation Test 6");
+ }
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 6");
+ }
+ }
+
+ public void doTest7()
+ throws Exception
+ {
+ byte[] salt = new byte[0];
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ ParametersWithSalt sigParameters = new ParametersWithSalt(privParameters, salt);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new SHA1Digest(), 0, false);
+
+ eng.init(true, sigParameters);
+
+ eng.update(msg7[0]);
+ eng.update(msg7, 1, msg7.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig7, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 7");
+ }
+
+ eng.update(msg7[0]);
+ eng.update(msg7, 1, msg7.length - 1);
+
+ if (!eng.verifySignature(sig7))
+ {
+ fail("failed ISO9796-2 verify Test 7");
+ }
+
+ if (!isSameAs(msg7, 0, eng.getRecoveredMessage()))
+ {
+ fail("failed ISO9796-2 recovery Test 7");
+ }
+ }
+
+ public void doTest8()
+ throws Exception
+ {
+ byte[] salt = Hex.decode("78E293203CBA1B7F92F05F4D171FF8CA3E738FF8");
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ ParametersWithSalt sigParameters = new ParametersWithSalt(privParameters, salt);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new RIPEMD160Digest(), 20, false);
+
+ eng.init(true, sigParameters);
+
+ eng.update(msg8[0]);
+ eng.update(msg8, 1, msg8.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig8, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 8");
+ }
+
+ eng.update(msg8[0]);
+ eng.update(msg8, 1, msg8.length - 1);
+
+ if (!eng.verifySignature(sig8))
+ {
+ fail("failed ISO9796-2 verify Test 8");
+ }
+ }
+
+ public void doTest9()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new RIPEMD160Digest(), 0, true);
+
+ eng.init(true, privParameters);
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig9, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 9");
+ }
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ if (!eng.verifySignature(sig9))
+ {
+ fail("failed ISO9796-2 verify Test 9");
+ }
+ }
+
+ public void doTest10()
+ throws Exception
+ {
+ BigInteger mod = new BigInteger("B3ABE6D91A4020920F8B3847764ECB34C4EB64151A96FDE7B614DC986C810FF2FD73575BDF8532C06004C8B4C8B64F700A50AEC68C0701ED10E8D211A4EA554D", 16);
+ BigInteger pubExp = new BigInteger("65537", 10);
+ BigInteger priExp = new BigInteger("AEE76AE4716F77C5782838F328327012C097BD67E5E892E75C1356E372CCF8EE1AA2D2CBDFB4DA19F703743F7C0BA42B2D69202BA7338C294D1F8B6A5771FF41", 16);
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod, priExp);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ Digest dig = new SHA1Digest();
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, dig, dig.getDigestSize());
+
+ //
+ // as the padding is random this test needs to repeat a few times to
+ // make sure
+ //
+ for (int i = 0; i != 500; i++)
+ {
+ eng.init(true, privParameters);
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 10");
+ }
+ }
+ }
+
+ public void doTest11()
+ throws Exception
+ {
+ BigInteger mod = new BigInteger("B3ABE6D91A4020920F8B3847764ECB34C4EB64151A96FDE7B614DC986C810FF2FD73575BDF8532C06004C8B4C8B64F700A50AEC68C0701ED10E8D211A4EA554D", 16);
+ BigInteger pubExp = new BigInteger("65537", 10);
+ BigInteger priExp = new BigInteger("AEE76AE4716F77C5782838F328327012C097BD67E5E892E75C1356E372CCF8EE1AA2D2CBDFB4DA19F703743F7C0BA42B2D69202BA7338C294D1F8B6A5771FF41", 16);
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod, priExp);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+ byte[] m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ byte[] m2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
+ byte[] m3 = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ Digest dig = new SHA1Digest();
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, dig, dig.getDigestSize());
+
+ //
+ // check message bounds
+ //
+ eng.init(true, privParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ eng.update(m2, 0, m2.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m2 verify Test 11");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m3, 0, m3.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m3 verify Test 11");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 11");
+ }
+ }
+
+ public void doTest12()
+ throws Exception
+ {
+ BigInteger mod = new BigInteger("B3ABE6D91A4020920F8B3847764ECB34C4EB64151A96FDE7B614DC986C810FF2FD73575BDF8532C06004C8B4C8B64F700A50AEC68C0701ED10E8D211A4EA554D", 16);
+ BigInteger pubExp = new BigInteger("65537", 10);
+ BigInteger priExp = new BigInteger("AEE76AE4716F77C5782838F328327012C097BD67E5E892E75C1356E372CCF8EE1AA2D2CBDFB4DA19F703743F7C0BA42B2D69202BA7338C294D1F8B6A5771FF41", 16);
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod, priExp);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+ byte[] m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ byte[] m2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
+ byte[] m3 = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ //
+ // ISO 9796-2 - Signing
+ //
+ Digest dig = new SHA1Digest();
+ ISO9796d2Signer eng = new ISO9796d2Signer(rsa, dig);
+
+ //
+ // check message bounds
+ //
+ eng.init(true, privParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ eng.update(m2, 0, m2.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m2 verify Test 12");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m3, 0, m3.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m3 verify Test 12");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 12");
+ }
+ }
+
+ private void doTest13()
+ throws Exception
+ {
+ BigInteger modulus = new BigInteger(1, Hex.decode("CDCBDABBF93BE8E8294E32B055256BBD0397735189BF75816341BB0D488D05D627991221DF7D59835C76A4BB4808ADEEB779E7794504E956ADC2A661B46904CDC71337DD29DDDD454124EF79CFDD7BC2C21952573CEFBA485CC38C6BD2428809B5A31A898A6B5648CAA4ED678D9743B589134B7187478996300EDBA16271A861"));
+ BigInteger pubExp = new BigInteger(1, Hex.decode("010001"));
+ BigInteger privExp = new BigInteger(1, Hex.decode("4BA6432AD42C74AA5AFCB6DF60FD57846CBC909489994ABD9C59FE439CC6D23D6DE2F3EA65B8335E796FD7904CA37C248367997257AFBD82B26F1A30525C447A236C65E6ADE43ECAAF7283584B2570FA07B340D9C9380D88EAACFFAEEFE7F472DBC9735C3FF3A3211E8A6BBFD94456B6A33C17A2C4EC18CE6335150548ED126D"));
+
+ RSAKeyParameters pubParams = new RSAKeyParameters(false, modulus, pubExp);
+ RSAKeyParameters privParams = new RSAKeyParameters(true, modulus, privExp);
+
+ AsymmetricBlockCipher rsaEngine = new RSABlindedEngine();
+ Digest digest = new SHA256Digest();
+
+ // set challenge to all zero's for verification
+ byte[] challenge = new byte[8];
+
+ // DOES NOT USE FINAL BOOLEAN TO INDICATE RECOVERY
+ ISO9796d2Signer signer = new ISO9796d2Signer(rsaEngine, digest, false);
+
+ // sign
+ signer.init(true, privParams);
+ signer.update(challenge, 0, challenge.length);
+
+ byte[] sig = signer.generateSignature();
+
+ // verify
+ signer.init(false, pubParams);
+ signer.update(challenge, 0, challenge.length);
+
+ if (!signer.verifySignature(sig))
+ {
+ fail("basic verification failed");
+ }
+
+ // === LETS ACTUALLY DO SOME RECOVERY, USING INPUT FROM INTERNAL AUTHENTICATE ===
+
+ signer.reset();
+
+ final String args0 = "482E20D1EDDED34359C38F5E7C01203F9D6B2641CDCA5C404D49ADAEDE034C7481D781D043722587761C90468DE69C6585A1E8B9C322F90E1B580EEDAB3F6007D0C366CF92B4DB8B41C8314929DCE2BE889C0129123484D2FD3D12763D2EBFD12AC8E51D7061AFCA1A53DEDEC7B9A617472A78C952CCC72467AE008E5F132994";
+
+ digest = new SHA1Digest();
+
+ signer = new ISO9796d2Signer(rsaEngine, digest, true);
+
+
+ signer.init(false, pubParams);
+ final byte[] signature = Hex.decode(args0);
+ signer.updateWithRecoveredMessage(signature);
+ signer.update(challenge, 0, challenge.length);
+
+ if (!signer.verifySignature(signature))
+ {
+ fail("recovered + challenge signature failed");
+ }
+
+ // === FINALLY, USING SHA-256 ===
+
+ signer.reset();
+
+ digest = new SHA256Digest();
+
+ // NOTE setting implit to false does not actually do anything for verification !!!
+ signer = new ISO9796d2Signer(rsaEngine, digest, false);
+
+
+ signer.init(true, privParams);
+ // generate NONCE of correct length using some inner knowledge
+ int nonceLength = modulus.bitLength() / 8 - 1 - digest.getDigestSize() - 2;
+ final byte[] nonce = new byte[nonceLength];
+ SecureRandom rnd = new SecureRandom();
+
+ rnd.nextBytes(nonce);
+
+ signer.update(nonce, 0, nonce.length);
+ signer.update(challenge, 0, challenge.length);
+ byte[] sig3 = signer.generateSignature();
+
+ signer.init(false, pubParams);
+ signer.updateWithRecoveredMessage(sig3);
+ signer.update(challenge, 0, challenge.length);
+ if (signer.verifySignature(sig3))
+ {
+ if (signer.hasFullMessage())
+ {
+ fail("signer indicates full message");
+ }
+ byte[] recoverableMessage = signer.getRecoveredMessage();
+
+ // sanity check, normally the nonce is ignored in eMRTD specs (PKI Technical Report)
+ if (!Arrays.areEqual(nonce, recoverableMessage))
+ {
+ fail("Nonce compare with recoverable part of message failed");
+ }
+ }
+ else
+ {
+ fail("recoverable + nonce failed.");
+ }
+ }
+
+ private static final byte[] longMessage = Base64.decode(
+ "VVNIKzErU0U2ODAxNTMyOTcxOSsyKzErNisyKzErMTo6OTk5OTk5OTk5OTk5"
+ + "OTo6OSsyOjo3Nzc3Nzc3Nzc3Nzc3Ojo5Kys1OjIwMTMwNDA1OjExMzUyMCdV"
+ + "U0ErMTo6OjE2OjEnVVNDKzRmYjk3YzFhNDI5ZGIyZDYnVVNBKzY6MTY6MTox"
+ + "MDoxKzE0OjIwNDgrMTI6/vn3S0h96eNhfmPN6OZUxXhd815h0tP871Hl+V1r"
+ + "fHHUXvrPXmjHV0vdb8fYY1zxwvnQUcFBWXT43PFi7Xbow0/9e9l6/mhs1UJq"
+ + "VPvp+ELbeXfn4Nj02ttk0e3H5Hfa69NYRuHv1WBO6lfizNnM9m9XYmh9TOrg"
+ + "f9rDRtd+ZNbf4lz9fPTt9OXyxOJWRPr/0FLzxUVsddplfHxM3ndETFD7ffjI"
+ + "/mhRYuL8WXZ733LeWFRCeOzKzmDz/HvT3GZx/XJMbFpqyOZjedzh6vZr1vrD"
+ + "615TQfN7wtJJ29bN2Hvzb2f1xGHaXl7af0/w9dpR2dr7/HzuZEJKYc7JSkv4"
+ + "/k37yERIbcrfbVTeVtR+dcVoeeRT41fmzMfzf8RnWOX4YMNifl0rMTM68EFA"
+ + "QSdCR00rMzgwKzk5OTk5OTk5J0RUTSsxMzc6MjAxMzA0MDU6MTAyJ0ZUWCtB"
+ + "QUkrKytJTlZPSUNFIFRFU1QnUkZGK09OOjEyMzQ1NidSRkYrRFE6MjIyMjIy"
+ + "MjIyJ0RUTSsxNzE6MjAxMzA0MDE6MTAyJ05BRCtTVSs5OTk5OTk5OTk5OTk5"
+ + "Ojo5KytURVNUIFNVUFBMSUVSOjpUcmFzZSByZWdpc3RlciBYWFhYWFhYK1Rl"
+ + "c3QgYWRkcmVzcyBzdXBwbGllcitDaXR5KysxMjM0NStERSdSRkYrVkE6QTEy"
+ + "MzQ1Njc4J05BRCtTQ08rOTk5OTk5OTk5OTk5OTo6OSsrVEVTVCBTVVBQTElF"
+ + "Ujo6VHJhc2UgcmVnaXN0ZXIgWFhYWFhYWCtUZXN0IGFkZHJlc3Mgc3VwcGxp"
+ + "ZXIrQ2l0eSsrMTIzNDUrREUnUkZGK1ZBOkExMjM0NTY3OCdOQUQrQlkrODg4"
+ + "ODg4ODg4ODg4ODo6OSdOQUQrSVYrNzc3Nzc3Nzc3Nzc3Nzo6OSsrVEVTVCBC"
+ + "VVlFUitUZXN0IGFkZHJlc3MgYnV5ZXIrQ2l0eTIrKzU0MzIxK0RFJ1JGRitW"
+ + "QTpKODc2NTQzMjEnTkFEK0JDTys3Nzc3Nzc3Nzc3Nzc3Ojo5KytURVNUIEJV"
+ + "WUVSK1Rlc3QgYWRkcmVzcyBidXllcitDaXR5MisrNTQzMjErREUnUkZGK1ZB"
+ + "Oko4NzY1NDMyMSdOQUQrRFArODg4ODg4ODg4ODg4ODo6OSdOQUQrUFIrNzc3"
+ + "Nzc3Nzc3Nzc3Nzo6OSdDVVgrMjpFVVI6NCdQQVQrMzUnRFRNKzEzOjIwMTMw"
+ + "NjI0OjEwMidMSU4rMSsrMTExMTExMTExMTExMTpFTidQSUErMStBQUFBQUFB"
+ + "OlNBJ0lNRCtGK00rOjo6UFJPRFVDVCBURVNUIDEnUVRZKzQ3OjEwLjAwMCdN"
+ + "T0ErNjY6Ny4wMCdQUkkrQUFCOjEuMDAnUFJJK0FBQTowLjcwJ1JGRitPTjox"
+ + "MjM0NTYnUkZGK0RROjIyMjIyMjIyMidUQVgrNytWQVQrKys6OjoyMS4wMDAn"
+ + "QUxDK0ErKysxK1REJ1BDRCsxOjMwLjAwMCdNT0ErMjA0OjMuMDAnTElOKzIr"
+ + "KzIyMjIyMjIyMjIyMjI6RU4nUElBKzErQkJCQkJCQjpTQSdJTUQrRitNKzo6"
+ + "OlBST0RVQ1QgVEVTVCAyJ1FUWSs0NzoyMC4wMDAnTU9BKzY2OjgwLjAwJ1BS"
+ + "SStBQUI6NS4wMCdQUkkrQUFBOjQuMDAnUkZGK09OOjEyMzQ1NidSRkYrRFE6"
+ + "MjIyMjIyMjIyJ1RBWCs3K1ZBVCsrKzo6OjIxLjAwMCdBTEMrQSsrKzErVEQn"
+ + "UENEKzE6MjAuMDAwJ01PQSsyMDQ6MjAuMDAnVU5TK1MnQ05UKzI6MidNT0Er"
+ + "Nzk6ODcuMDAnTU9BKzEzOToxMDUuMjcnTU9BKzEyNTo4Ny4wMCdNT0ErMjYw"
+ + "OjAuMDAnTU9BKzI1OTowLjAwJ01PQSsxNzY6MTguMjcnVEFYKzcrVkFUKysr"
+ + "Ojo6MjEuMDAwJ01PQSsxNzY6MTguMjcnTU9BKzEyNTo4Ny4wMCc=");
+
+ private static final byte[] shortPartialSig = Base64.decode(
+ "sb8yyKk6HM1cJhICScMx7QRQunRyrZ1fbI42+T+TBGNjOknvzKuvG7aftGX7"
+ + "O/RXuYgk6LTxpXv7+O5noUhMBsR2PKaHveuylU1WSPmDxDCui3kp4frqVH0w"
+ + "8Vjpl5CsKqBsmKkbGCKE+smM0xFXhYxV8QUTB2XsWNCQiFiHPgwbpfWzZUNY"
+ + "QPWd0A99P64EuUIYz1tkkDnLFmwQ19/PJu1a8orIQInmkVYWSsBsZ/7Ks6lx"
+ + "nDHpAvgiRe+OXmJ/yuQy1O3FJYdyoqvjYRPBu3qYeBK9+9L3lExLilImH5aD"
+ + "nJznaXcO8QFOxVPbrF2s4GdPIMDonEyAHdrnzoghlg==");
+
+ private void doShortPartialTest()
+ throws Exception
+ {
+ byte[] recovered = Hex.decode("5553482b312b534536383031353332393731392b322b312b362b322b312b313a3a393939393939393939393939393a3a392b323a3a373737373737373737373737373a3a392b2b353a32303133303430353a313133");
+ BigInteger exp = new BigInteger("10001", 16);
+ BigInteger mod = new BigInteger("b9b70b083da9e37e23cde8e654855db31e21d2d3fc11a5f91d2b3c311efa8f5e28c757dd6fc798631cb1b9d051c14119749cb122ad76e8c3fd7bd93abe282c026a14fba9f8023977a7a0d8b49a24d1ad87e4379a931846a1ef9520ea57e28c998cf65722683d0caaa0da8306973e2496a25cbd3cb4adb4b284e25604fabf12f385456c75da7c3c4cde37440cfb7db8c8fe6851e2bc59767b9f7218540238ac8acef3bc7bd3dc6671320c2c1a2ac8a6799ce1eaf62b9683ab1e1341b37b9249dbd6cd987b2f27b5c4619a1eda7f0fb0b59a519afbbc3cee640261cec90a4bb8fefbc844082dca9f549e56943e758579a453a357e6ccb37fc46718a5b8c3227e5d", 16);
+
+ AsymmetricKeyParameter pubKey = new RSAKeyParameters(false, mod, exp);
+
+ ISO9796d2PSSSigner pssSign = new ISO9796d2PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ pssSign.init(false, pubKey);
+
+ pssSign.updateWithRecoveredMessage(shortPartialSig);
+
+ pssSign.update(longMessage, pssSign.getRecoveredMessage().length, longMessage.length - pssSign.getRecoveredMessage().length);
+
+ if (!pssSign.verifySignature(shortPartialSig))
+ {
+ fail("short partial PSS sig verification failed.");
+ }
+
+ byte[] mm = pssSign.getRecoveredMessage();
+
+ if (!Arrays.areEqual(recovered, mm))
+ {
+ fail("short partial PSS recovery failed");
+ }
+ }
+
+ private void doFullMessageTest()
+ throws Exception
+ {
+ BigInteger modulus = new BigInteger(1, Hex.decode("CDCBDABBF93BE8E8294E32B055256BBD0397735189BF75816341BB0D488D05D627991221DF7D59835C76A4BB4808ADEEB779E7794504E956ADC2A661B46904CDC71337DD29DDDD454124EF79CFDD7BC2C21952573CEFBA485CC38C6BD2428809B5A31A898A6B5648CAA4ED678D9743B589134B7187478996300EDBA16271A861"));
+ BigInteger pubExp = new BigInteger(1, Hex.decode("010001"));
+ BigInteger privExp = new BigInteger(1, Hex.decode("4BA6432AD42C74AA5AFCB6DF60FD57846CBC909489994ABD9C59FE439CC6D23D6DE2F3EA65B8335E796FD7904CA37C248367997257AFBD82B26F1A30525C447A236C65E6ADE43ECAAF7283584B2570FA07B340D9C9380D88EAACFFAEEFE7F472DBC9735C3FF3A3211E8A6BBFD94456B6A33C17A2C4EC18CE6335150548ED126D"));
+
+ RSAKeyParameters pubParams = new RSAKeyParameters(false, modulus, pubExp);
+ RSAKeyParameters privParams = new RSAKeyParameters(true, modulus, privExp);
+
+ AsymmetricBlockCipher rsaEngine = new RSABlindedEngine();
+
+ // set challenge to all zero's for verification
+ byte[] challenge = new byte[8];
+
+ ISO9796d2PSSSigner pssSign = new ISO9796d2PSSSigner(new RSAEngine(), new SHA256Digest(), 20, true);
+
+ pssSign.init(true, privParams);
+
+ pssSign.update(challenge, 0, challenge.length);
+
+ byte[] sig = pssSign.generateSignature();
+
+ pssSign.init(false, pubParams);
+
+ pssSign.updateWithRecoveredMessage(sig);
+
+ if (!pssSign.verifySignature(sig))
+ {
+ fail("challenge PSS sig verification failed.");
+ }
+
+ byte[] mm = pssSign.getRecoveredMessage();
+
+ if (!Arrays.areEqual(challenge, mm))
+ {
+ fail("challenge partial PSS recovery failed");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ doTest1();
+ doTest2();
+ doTest3();
+ doTest4();
+ doTest5();
+ doTest6();
+ doTest7();
+ doTest8();
+ doTest9();
+ doTest10();
+ doTest11();
+ doTest12();
+ doTest13();
+ doShortPartialTest();
+ doFullMessageTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ISO9796Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISO9797Alg3MacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISO9797Alg3MacTest.java
new file mode 100644
index 000000000..1a2aee97d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ISO9797Alg3MacTest.java
@@ -0,0 +1,126 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.macs.ISO9797Alg3Mac;
+import org.spongycastle.crypto.paddings.ISO7816d4Padding;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ISO9797Alg3MacTest
+ extends SimpleTest
+{
+ static byte[] keyBytes = Hex.decode("7CA110454A1A6E570131D9619DC1376E");
+
+ static byte[] input1 = "Hello World !!!!".getBytes();
+
+ static byte[] output1 = Hex.decode("F09B856213BAB83B");
+
+ public ISO9797Alg3MacTest()
+ {
+ }
+
+ public void performTest()
+ {
+ KeyParameter key = new KeyParameter(keyBytes);
+ BlockCipher cipher = new DESEngine();
+ Mac mac = new ISO9797Alg3Mac(cipher);
+
+ //
+ // standard DAC - zero IV
+ //
+ mac.init(key);
+
+ mac.update(input1, 0, input1.length);
+
+ byte[] out = new byte[8];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output1))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output1)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // reset
+ //
+ mac.reset();
+
+ mac.init(key);
+
+ for (int i = 0; i != input1.length / 2; i++)
+ {
+ mac.update(input1[i]);
+ }
+
+ mac.update(input1, input1.length / 2, input1.length - (input1.length / 2));
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output1))
+ {
+ fail("Reset failed - expected " + new String(Hex.encode(output1)) + " got " + new String(Hex.encode(out)));
+ }
+
+ testMacWithIv();
+ }
+
+ private void testMacWithIv()
+ {
+ byte[] inputData = new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
+ byte[] key = new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
+ byte[] zeroIv = new byte[8];
+ byte[] nonZeroIv = new byte[]{0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4};
+
+ KeyParameter simpleParameter = new KeyParameter(key);
+ ParametersWithIV zeroIvParameter = new ParametersWithIV(new KeyParameter(key), zeroIv);
+
+ ISO9797Alg3Mac mac1 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
+
+ // we calculate a reference MAC with a null IV
+ mac1.init(simpleParameter);
+ mac1.update(inputData, 0, inputData.length);
+ byte[] output1 = new byte[mac1.getMacSize()];
+ mac1.doFinal(output1, 0);
+
+ // we then check that passing a vector of 0s is the same as not using any IV
+ ISO9797Alg3Mac mac2 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
+ mac2.init(zeroIvParameter);
+ mac2.update(inputData, 0, inputData.length);
+ byte[] output2 = new byte[mac2.getMacSize()];
+ mac2.doFinal(output2, 0);
+ if (!Arrays.areEqual(output1, output2))
+ {
+ fail("zero IV test failed");
+ }
+
+ // and then check that a non zero IV parameter produces a different results.
+ ParametersWithIV nonZeroIvParameter = new ParametersWithIV(new KeyParameter(key), nonZeroIv);
+ mac2 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
+ mac2.init(nonZeroIvParameter);
+ mac2.update(inputData, 0, inputData.length);
+ output2 = new byte[mac2.getMacSize()];
+ mac2.doFinal(output2, 0);
+ if (Arrays.areEqual(output1, output2))
+ {
+ fail("non-zero IV test failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "ISO9797Alg3Mac";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ISO9797Alg3MacTest());
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDF1GeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDF1GeneratorTest.java
new file mode 100644
index 000000000..0671ef8b0
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDF1GeneratorTest.java
@@ -0,0 +1,93 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.digests.ShortenedDigest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.generators.KDF1BytesGenerator;
+import org.spongycastle.crypto.params.ISO18033KDFParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * KDF1 tests - vectors from ISO 18033.
+ */
+public class KDF1GeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("d6e168c5f256a2dcff7ef12facd390f393c7a88d");
+ private byte[] mask1 = Hex.decode(
+ "0742ba966813af75536bb6149cc44fc256fd6406df79665bc31dc5"
+ + "a62f70535e52c53015b9d37d412ff3c1193439599e1b628774c50d9c"
+ + "cb78d82c425e4521ee47b8c36a4bcffe8b8112a89312fc04420a39de"
+ + "99223890e74ce10378bc515a212b97b8a6447ba6a8870278");
+
+ private byte[] seed2 = Hex.decode(
+ "032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d7643741"
+ + "52e0ac009e509e7ba30cd2f1778e113b64e135cf4e2292c75efe5288edfda4");
+ private byte[] mask2 = Hex.decode(
+ "5f8de105b5e96b2e490ddecbd147dd1def7e3b8e0e6a26eb7b956ccb8b3bdc1ca9"
+ + "75bc57c3989e8fbad31a224655d800c46954840ff32052cdf0d640562bdfadfa263c"
+ + "fccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842dbff8e13efee5b7e"
+ + "7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837eea4e0a2f04");
+
+ private byte[] seed3 = seed2;
+ private byte[] mask3= Hex.decode(
+ "09e2decf2a6e1666c2f6071ff4298305e2643fd510a2403db42a8743cb989de86e"
+ + "668d168cbe604611ac179f819a3d18412e9eb45668f2923c087c12fee0c5a0d2a8aa"
+ + "70185401fbbd99379ec76c663e875a60b4aacb1319fa11c3365a8b79a44669f26fb5"
+ + "55c80391847b05eca1cb5cf8c2d531448d33fbaca19f6410ee1fcb");
+
+
+ public KDF1GeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new KDF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed1, mask1);
+ checkMask(2, new KDF1BytesGenerator(new SHA1Digest()), seed2, mask2);
+ checkMask(3, new KDF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed3, mask3);
+
+ try
+ {
+ new KDF1BytesGenerator(new SHA1Digest()).generateBytes(new byte[10], 0, 20);
+
+ fail("short input array not caught");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ byte[] seed,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(new ISO18033KDFParameters(seed));
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("KDF1 failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "KDF1";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KDF1GeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDF2GeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDF2GeneratorTest.java
new file mode 100644
index 000000000..676f22b17
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDF2GeneratorTest.java
@@ -0,0 +1,105 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.ShortenedDigest;
+import org.spongycastle.crypto.generators.KDF2BytesGenerator;
+import org.spongycastle.crypto.params.KDFParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * KDF2 tests - vectors from ISO 18033.
+ */
+public class KDF2GeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("d6e168c5f256a2dcff7ef12facd390f393c7a88d");
+ private byte[] mask1 = Hex.decode(
+ "df79665bc31dc5a62f70535e52c53015b9d37d412ff3c119343959"
+ + "9e1b628774c50d9ccb78d82c425e4521ee47b8c36a4bcffe8b8112a8"
+ + "9312fc04420a39de99223890e74ce10378bc515a212b97b8a6447ba6"
+ + "a8870278f0262727ca041fa1aa9f7b5d1cf7f308232fe861");
+
+ private byte[] seed2 = Hex.decode(
+ "032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d7643741"
+ + "52e0ac009e509e7ba30cd2f1778e113b64e135cf4e2292c75efe5288edfda4");
+ private byte[] mask2 = Hex.decode(
+ "10a2403db42a8743cb989de86e668d168cbe604611ac179f819a3d18412e9eb456"
+ + "68f2923c087c12fee0c5a0d2a8aa70185401fbbd99379ec76c663e875a60b4aacb13"
+ + "19fa11c3365a8b79a44669f26fb555c80391847b05eca1cb5cf8c2d531448d33fbac"
+ + "a19f6410ee1fcb260892670e0814c348664f6a7248aaf998a3acc6");
+ private byte[] adjustedMask2 = Hex.decode(
+ "10a2403db42a8743cb989de86e668d168cbe6046e23ff26f741e87949a3bba1311ac1"
+ + "79f819a3d18412e9eb45668f2923c087c1299005f8d5fd42ca257bc93e8fee0c5a0d2"
+ + "a8aa70185401fbbd99379ec76c663e9a29d0b70f3fe261a59cdc24875a60b4aacb131"
+ + "9fa11c3365a8b79a44669f26fba933d012db213d7e3b16349");
+
+ private byte[] sha1Mask = Hex.decode(
+ "0e6a26eb7b956ccb8b3bdc1ca975bc57c3989e8fbad31a224655d800c46954840ff32"
+ + "052cdf0d640562bdfadfa263cfccf3c52b29f2af4a1869959bc77f854cf15bd7a2519"
+ + "2985a842dbff8e13efee5b7e7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837ee"
+ + "a4e0a2f04b53ca8f50fb31225c1be2d0126c8c7a4753b0807");
+
+ private byte[] seed3 = Hex.decode("CA7C0F8C3FFA87A96E1B74AC8E6AF594347BB40A");
+ private byte[] mask3 = Hex.decode("744AB703F5BC082E59185F6D049D2D367DB245C2");
+
+ private byte[] seed4 = Hex.decode("0499B502FC8B5BAFB0F4047E731D1F9FD8CD0D8881");
+ private byte[] mask4 = Hex.decode("03C62280C894E103C680B13CD4B4AE740A5EF0C72547292F82DC6B1777F47D63BA9D1EA732DBF386");
+
+ public KDF2GeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new KDF2BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed1, mask1);
+ checkMask(2, new KDF2BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed2, mask2);
+ checkMask(3, new KDF2BytesGenerator(new SHA256Digest()), seed2, adjustedMask2);
+ checkMask(4, new KDF2BytesGenerator(new SHA1Digest()), seed2, sha1Mask);
+ checkMask(5, new KDF2BytesGenerator(new SHA1Digest()), seed3, mask3);
+ checkMask(6, new KDF2BytesGenerator(new SHA1Digest()), seed4, mask4);
+
+ try
+ {
+ new KDF2BytesGenerator(new SHA1Digest()).generateBytes(new byte[10], 0, 20);
+
+ fail("short input array not caught");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ byte[] seed,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(new KDFParameters(seed, new byte[0]));
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("KDF2 failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "KDF2";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KDF2GeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFCounterGeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFCounterGeneratorTest.java
new file mode 100644
index 000000000..3b58db2a2
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFCounterGeneratorTest.java
@@ -0,0 +1,51 @@
+package org.spongycastle.crypto.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.spongycastle.crypto.test.cavp.CAVPReader;
+import org.spongycastle.crypto.test.cavp.KDFCounterTests;
+import org.spongycastle.util.test.SimpleTest;
+
+public class KDFCounterGeneratorTest
+ extends SimpleTest
+{
+
+ private static void testCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFCTR_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ public String getName()
+ {
+ return this.getClass().getSimpleName();
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testCounter();
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new KDFCounterGeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java
new file mode 100644
index 000000000..d44d1cab6
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java
@@ -0,0 +1,72 @@
+package org.spongycastle.crypto.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.spongycastle.crypto.test.cavp.CAVPReader;
+import org.spongycastle.crypto.test.cavp.KDFDoublePipelineCounterTests;
+import org.spongycastle.crypto.test.cavp.KDFDoublePipelineIterationNoCounterTests;
+import org.spongycastle.util.test.SimpleTest;
+
+public class KDFDoublePipelineIteratorGeneratorTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return this.getClass().getSimpleName();
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testDoublePipelineIterationCounter();
+ testDoublePipelineIterationNoCounter();
+ }
+
+ private static void testDoublePipelineIterationCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFDoublePipelineCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFDblPipelineCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFDoublePipelineIterationCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ private static void testDoublePipelineIterationNoCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFDoublePipelineIterationNoCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFDblPipelineNoCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFDblPipelineIterationNoCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new KDFDoublePipelineIteratorGeneratorTest());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFFeedbackGeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFFeedbackGeneratorTest.java
new file mode 100644
index 000000000..fa850985a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/KDFFeedbackGeneratorTest.java
@@ -0,0 +1,71 @@
+package org.spongycastle.crypto.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.spongycastle.crypto.test.cavp.CAVPReader;
+import org.spongycastle.crypto.test.cavp.KDFFeedbackCounterTests;
+import org.spongycastle.crypto.test.cavp.KDFFeedbackNoCounterTests;
+import org.spongycastle.util.test.SimpleTest;
+
+public class KDFFeedbackGeneratorTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return this.getClass().getSimpleName();
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testFeedbackCounter();
+ testFeedbackNoCounter();
+ }
+
+ private static void testFeedbackCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFFeedbackCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFFeedbackCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFFeedbackCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark ", e);
+ }
+ }
+
+ private static void testFeedbackNoCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFFeedbackNoCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFFeedbackNoCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFFeedbackNoCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new KDFDoublePipelineIteratorGeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD2DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD2DigestTest.java
new file mode 100644
index 000000000..e393f7994
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD2DigestTest.java
@@ -0,0 +1,52 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.MD2Digest;
+
+/**
+ * standard vector test for MD2
+ * from RFC1319 by B.Kaliski of RSA Laboratories April 1992
+ *
+ */
+public class MD2DigestTest
+ extends DigestTest
+{
+ static final String messages[] =
+ {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ static final String digests[] =
+ {
+ "8350e5a3e24c153df2275c9f80692773",
+ "32ec01ec4a6dac72c0ab96fb34c0b5d1",
+ "da853b0d3f88d99b30283a69e6ded6bb",
+ "ab4f496bfb2a530b219ff33031fe06b0",
+ "4e8ddff3650292ab5a4108c3aa47940b",
+ "da33def2a42df13975352846c30338cd",
+ "d5976f79d83d3a0dc9806c3c66f3efd8"
+ };
+
+ MD2DigestTest()
+ {
+ super(new MD2Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(
+ Digest digest)
+ {
+ return new MD2Digest((MD2Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD2DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD4DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD4DigestTest.java
new file mode 100644
index 000000000..9175e2fe4
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD4DigestTest.java
@@ -0,0 +1,43 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.MD4Digest;
+
+/**
+ * standard vector test for MD4 from RFC 1320.
+ */
+public class MD4DigestTest
+ extends DigestTest
+{
+ static private String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ static private String[] digests =
+ {
+ "31d6cfe0d16ae931b73c59d7e0c089c0",
+ "bde52cb31de33e46245e05fbdbd6fb24",
+ "a448017aaf21d8525fc10ae87aa6729d",
+ "e33b4ddc9c38f2199c3e7b164fcc0536"
+ };
+
+ MD4DigestTest()
+ {
+ super(new MD4Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new MD4Digest((MD4Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD4DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD5DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD5DigestTest.java
new file mode 100644
index 000000000..f39944d15
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD5DigestTest.java
@@ -0,0 +1,43 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.MD5Digest;
+
+/**
+ * standard vector test for MD5 from "Handbook of Applied Cryptography", page 345.
+ */
+public class MD5DigestTest
+ extends DigestTest
+{
+ static final String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghijklmnopqrstuvwxyz"
+ };
+
+ static final String[] digests =
+ {
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "c3fcd3d76192e4007dfb496cca67e13b"
+ };
+
+ MD5DigestTest()
+ {
+ super(new MD5Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new MD5Digest((MD5Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD5DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD5HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD5HMacTest.java
new file mode 100644
index 000000000..76d8e2149
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MD5HMacTest.java
@@ -0,0 +1,98 @@
+
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.MD5Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * MD5 HMac Test, test vectors from RFC 2202
+ */
+public class MD5HMacTest
+ extends SimpleTest
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "9294727a3638bb1c13f48ef8158bfc9d",
+ "750c783e6ab0b503eaa86e310a5db738",
+ "56be34521d144c88dbb8c733f0e8b3f6",
+ "697eaf0aca3a3aea3a75164746ffaa79",
+ "56461ef2342edc00f9bab995690efd4c",
+ "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd",
+ "6f630fad67cda0ee1fb1f562db3aa53e"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "MD5HMac";
+ }
+
+ public void performTest()
+ {
+ HMac hmac = new HMac(new MD5Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ fail("Vector " + i + " failed");
+ }
+ }
+
+ // test reset
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ fail("Reset with vector " + vector + " failed");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD5HMacTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MGF1GeneratorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MGF1GeneratorTest.java
new file mode 100644
index 000000000..0c7a2fec9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MGF1GeneratorTest.java
@@ -0,0 +1,88 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.ShortenedDigest;
+import org.spongycastle.crypto.generators.MGF1BytesGenerator;
+import org.spongycastle.crypto.params.MGFParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * MGF1 tests - vectors from ISO 18033 for KDF1 (equivalent).
+ */
+public class MGF1GeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("d6e168c5f256a2dcff7ef12facd390f393c7a88d");
+ private byte[] mask1 = Hex.decode(
+ "0742ba966813af75536bb6149cc44fc256fd6406df79665bc31dc5"
+ + "a62f70535e52c53015b9d37d412ff3c1193439599e1b628774c50d9c"
+ + "cb78d82c425e4521ee47b8c36a4bcffe8b8112a89312fc04420a39de"
+ + "99223890e74ce10378bc515a212b97b8a6447ba6a8870278");
+
+ private byte[] seed2 = Hex.decode(
+ "032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d7643741"
+ + "52e0ac009e509e7ba30cd2f1778e113b64e135cf4e2292c75efe5288edfda4");
+ private byte[] mask2 = Hex.decode(
+ "5f8de105b5e96b2e490ddecbd147dd1def7e3b8e0e6a26eb7b956ccb8b3bdc1ca9"
+ + "75bc57c3989e8fbad31a224655d800c46954840ff32052cdf0d640562bdfadfa263c"
+ + "fccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842dbff8e13efee5b7e"
+ + "7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837eea4e0a2f04");
+
+ private byte[] seed3 = seed2;
+ private byte[] mask3= Hex.decode(
+ "09e2decf2a6e1666c2f6071ff4298305e2643fd510a2403db42a8743cb989de86e"
+ + "668d168cbe604611ac179f819a3d18412e9eb45668f2923c087c12fee0c5a0d2a8aa"
+ + "70185401fbbd99379ec76c663e875a60b4aacb1319fa11c3365a8b79a44669f26fb5"
+ + "55c80391847b05eca1cb5cf8c2d531448d33fbaca19f6410ee1fcb");
+
+ public void performTest()
+ {
+ checkMask(1, new MGF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed1, mask1);
+ checkMask(2, new MGF1BytesGenerator(new SHA1Digest()), seed2, mask2);
+ checkMask(3, new MGF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed3, mask3);
+
+ try
+ {
+ new MGF1BytesGenerator(new SHA1Digest()).generateBytes(new byte[10], 0, 20);
+
+ fail("short input array not caught");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ byte[] seed,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(new MGFParameters(seed));
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("MGF1 failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "MGF1";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MGF1GeneratorTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MacTest.java
new file mode 100644
index 000000000..1ae2bca1b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/MacTest.java
@@ -0,0 +1,181 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.macs.CBCBlockCipherMac;
+import org.spongycastle.crypto.macs.CFBBlockCipherMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.paddings.PKCS7Padding;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * MAC tester - vectors from
+ * FIP 81 and
+ * FIP 113.
+ */
+public class MacTest
+ extends SimpleTest
+{
+ static byte[] keyBytes = Hex.decode("0123456789abcdef");
+ static byte[] ivBytes = Hex.decode("1234567890abcdef");
+
+ static byte[] input1 = Hex.decode("37363534333231204e6f77206973207468652074696d6520666f7220");
+
+ static byte[] output1 = Hex.decode("f1d30f68");
+ static byte[] output2 = Hex.decode("58d2e77e");
+ static byte[] output3 = Hex.decode("cd647403");
+
+ //
+ // these aren't NIST vectors, just for regression testing.
+ //
+ static byte[] input2 = Hex.decode("3736353433323120");
+
+ static byte[] output4 = Hex.decode("3af549c9");
+ static byte[] output5 = Hex.decode("188fbdd5");
+ static byte[] output6 = Hex.decode("7045eecd");
+
+ public MacTest()
+ {
+ }
+
+ public void performTest()
+ {
+ KeyParameter key = new KeyParameter(keyBytes);
+ BlockCipher cipher = new DESEngine();
+ Mac mac = new CBCBlockCipherMac(cipher);
+
+ //
+ // standard DAC - zero IV
+ //
+ mac.init(key);
+
+ mac.update(input1, 0, input1.length);
+
+ byte[] out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output1))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output1)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // mac with IV.
+ //
+ ParametersWithIV param = new ParametersWithIV(key, ivBytes);
+
+ mac.init(param);
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output2))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output2)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // CFB mac with IV - 8 bit CFB mode
+ //
+ param = new ParametersWithIV(key, ivBytes);
+
+ mac = new CFBBlockCipherMac(cipher);
+
+ mac.init(param);
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output3))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output3)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // word aligned data - zero IV
+ //
+ mac.init(key);
+
+ mac.update(input2, 0, input2.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output4))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output4)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // word aligned data - zero IV - CBC padding
+ //
+ mac = new CBCBlockCipherMac(cipher, new PKCS7Padding());
+
+ mac.init(key);
+
+ mac.update(input2, 0, input2.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output5))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output5)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // non-word aligned data - zero IV - CBC padding
+ //
+ mac.reset();
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output6))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output6)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // non-word aligned data - zero IV - CBC padding
+ //
+ mac.init(key);
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output6))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output6)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ public String getName()
+ {
+ return "Mac";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MacTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ModeTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ModeTest.java
new file mode 100644
index 000000000..19fe1932c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ModeTest.java
@@ -0,0 +1,115 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.OFBBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * CFB/OFB Mode test of IV padding.
+ */
+public class ModeTest
+ implements Test
+{
+ public ModeTest()
+ {
+ }
+
+ private boolean isEqualTo(
+ byte[] a,
+ byte[] b)
+ {
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public TestResult perform()
+ {
+ KeyParameter key = new KeyParameter(Hex.decode("0011223344556677"));
+ byte[] input = Hex.decode("4e6f7720");
+ byte[] out1 = new byte[4];
+ byte[] out2 = new byte[4];
+
+
+ BlockCipher ofb = new OFBBlockCipher(new DESEngine(), 32);
+
+ ofb.init(true, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+
+ ofb.processBlock(input, 0, out1, 0);
+
+ ofb.init(false, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+ ofb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 1 - in != out");
+ }
+
+ ofb.init(true, new ParametersWithIV(key, Hex.decode("11223344")));
+
+ ofb.processBlock(input, 0, out1, 0);
+
+ ofb.init(false, new ParametersWithIV(key, Hex.decode("0000000011223344")));
+ ofb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 2 - in != out");
+ }
+
+ BlockCipher cfb = new CFBBlockCipher(new DESEngine(), 32);
+
+ cfb.init(true, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+
+ cfb.processBlock(input, 0, out1, 0);
+
+ cfb.init(false, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+ cfb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 3 - in != out");
+ }
+
+ cfb.init(true, new ParametersWithIV(key, Hex.decode("11223344")));
+
+ cfb.processBlock(input, 0, out1, 0);
+
+ cfb.init(false, new ParametersWithIV(key, Hex.decode("0000000011223344")));
+ cfb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 4 - in != out");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "ModeTest";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ ModeTest test = new ModeTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NaccacheSternTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NaccacheSternTest.java
new file mode 100644
index 000000000..1bde2f458
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NaccacheSternTest.java
@@ -0,0 +1,354 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.NaccacheSternEngine;
+import org.spongycastle.crypto.generators.NaccacheSternKeyPairGenerator;
+import org.spongycastle.crypto.params.NaccacheSternKeyGenerationParameters;
+import org.spongycastle.crypto.params.NaccacheSternKeyParameters;
+import org.spongycastle.crypto.params.NaccacheSternPrivateKeyParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test case for NaccacheStern cipher. For details on this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ *
+ * Performs the following tests:
+ *
+ *
+ */
+public class NaccacheSternTest
+ extends SimpleTest
+{
+ static final boolean debug = false;
+
+ static final NaccacheSternEngine cryptEng = new NaccacheSternEngine();
+
+ static final NaccacheSternEngine decryptEng = new NaccacheSternEngine();
+
+ static
+ {
+ cryptEng.setDebug(debug);
+ decryptEng.setDebug(debug);
+ }
+
+ // Values from NaccacheStern paper
+ static final BigInteger a = BigInteger.valueOf(101);
+
+ static final BigInteger u1 = BigInteger.valueOf(3);
+
+ static final BigInteger u2 = BigInteger.valueOf(5);
+
+ static final BigInteger u3 = BigInteger.valueOf(7);
+
+ static final BigInteger b = BigInteger.valueOf(191);
+
+ static final BigInteger v1 = BigInteger.valueOf(11);
+
+ static final BigInteger v2 = BigInteger.valueOf(13);
+
+ static final BigInteger v3 = BigInteger.valueOf(17);
+
+ static final BigInteger ONE = BigInteger.valueOf(1);
+
+ static final BigInteger TWO = BigInteger.valueOf(2);
+
+ static final BigInteger sigma = u1.multiply(u2).multiply(u3).multiply(v1)
+ .multiply(v2).multiply(v3);
+
+ static final BigInteger p = TWO.multiply(a).multiply(u1).multiply(u2)
+ .multiply(u3).add(ONE);
+
+ static final BigInteger q = TWO.multiply(b).multiply(v1).multiply(v2)
+ .multiply(v3).add(ONE);
+
+ static final BigInteger n = p.multiply(q);
+
+ static final BigInteger phi_n = p.subtract(ONE).multiply(q.subtract(ONE));
+
+ static final BigInteger g = BigInteger.valueOf(131);
+
+ static final Vector smallPrimes = new Vector();
+
+ // static final BigInteger paperTest = BigInteger.valueOf(202);
+
+ static final String input = "4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ static final BigInteger paperTest = BigInteger.valueOf(202);
+
+ //
+ // to check that we handling byte extension by big number correctly.
+ //
+ static final String edgeInput = "ff6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ public String getName()
+ {
+ return "NaccacheStern";
+ }
+
+ public void performTest()
+ {
+ // Test with given key from NaccacheSternPaper (totally insecure)
+
+ // First the Parameters from the NaccacheStern Paper
+ // (see http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf )
+
+ smallPrimes.addElement(u1);
+ smallPrimes.addElement(u2);
+ smallPrimes.addElement(u3);
+ smallPrimes.addElement(v1);
+ smallPrimes.addElement(v2);
+ smallPrimes.addElement(v3);
+
+ NaccacheSternKeyParameters pubParameters = new NaccacheSternKeyParameters(false, g, n, sigma.bitLength());
+
+ NaccacheSternPrivateKeyParameters privParameters = new NaccacheSternPrivateKeyParameters(g, n, sigma.bitLength(), smallPrimes, phi_n);
+
+ AsymmetricCipherKeyPair pair = new AsymmetricCipherKeyPair(pubParameters, privParameters);
+
+ // Initialize Engines with KeyPair
+
+ if (debug)
+ {
+ System.out.println("initializing encryption engine");
+ }
+ cryptEng.init(true, pair.getPublic());
+
+ if (debug)
+ {
+ System.out.println("initializing decryption engine");
+ }
+ decryptEng.init(false, pair.getPrivate());
+
+ byte[] data = paperTest.toByteArray();
+
+ if (!new BigInteger(data).equals(new BigInteger(enDeCrypt(data))))
+ {
+ fail("failed NaccacheStern paper test");
+ }
+
+ //
+ // key generation test
+ //
+
+ //
+ // 768 Bit test
+ //
+
+ if (debug)
+ {
+ System.out.println();
+ System.out.println("768 Bit TEST");
+ }
+
+ // specify key generation parameters
+ NaccacheSternKeyGenerationParameters genParam
+ = new NaccacheSternKeyGenerationParameters(new SecureRandom(), 768, 8, 30, debug);
+
+ // Initialize Key generator and generate key pair
+ NaccacheSternKeyPairGenerator pGen = new NaccacheSternKeyPairGenerator();
+ pGen.init(genParam);
+
+ pair = pGen.generateKeyPair();
+
+ if (((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() < 768)
+ {
+ System.out.println("FAILED: key size is <786 bit, exactly "
+ + ((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() + " bit");
+ fail("failed key generation (768) length test");
+ }
+
+ // Initialize Engines with KeyPair
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit encryption engine");
+ }
+ cryptEng.init(true, pair.getPublic());
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit decryption engine");
+ }
+ decryptEng.init(false, pair.getPrivate());
+
+ // Basic data input
+ data = Hex.decode(input);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") basic test");
+ }
+
+ // Data starting with FF byte (would be interpreted as negative
+ // BigInteger)
+
+ data = Hex.decode(edgeInput);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") edgeInput test");
+ }
+
+ //
+ // 1024 Bit Test
+ //
+/*
+ if (debug)
+ {
+ System.out.println();
+ System.out.println("1024 Bit TEST");
+ }
+
+ // specify key generation parameters
+ genParam = new NaccacheSternKeyGenerationParameters(new SecureRandom(), 1024, 8, 40);
+
+ pGen.init(genParam);
+ pair = pGen.generateKeyPair();
+
+ if (((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() < 1024)
+ {
+ if (debug)
+ {
+ System.out.println("FAILED: key size is <1024 bit, exactly "
+ + ((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() + " bit");
+ }
+ fail("failed key generation (1024) length test");
+ }
+
+ // Initialize Engines with KeyPair
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit encryption engine");
+ }
+ cryptEng.init(true, pair.getPublic());
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit decryption engine");
+ }
+ decryptEng.init(false, pair.getPrivate());
+
+ if (debug)
+ {
+ System.out.println("Data is " + new BigInteger(1, data));
+ }
+
+ // Basic data input
+ data = Hex.decode(input);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") basic test");
+ }
+
+ // Data starting with FF byte (would be interpreted as negative
+ // BigInteger)
+
+ data = Hex.decode(edgeInput);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") edgeInput test");
+ }
+*/
+ // END OF TEST CASE
+
+ try
+ {
+ new NaccacheSternEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("failed initialisation check");
+ }
+
+ if (debug)
+ {
+ System.out.println("All tests successful");
+ }
+ }
+
+ private byte[] enDeCrypt(byte[] input)
+ {
+
+ // create work array
+ byte[] data = new byte[input.length];
+ System.arraycopy(input, 0, data, 0, data.length);
+
+ // Perform encryption like in the paper from Naccache-Stern
+ if (debug)
+ {
+ System.out.println("encrypting data. Data representation\n"
+ // + "As String:.... " + new String(data) + "\n"
+ + "As BigInteger: " + new BigInteger(1, data));
+ System.out.println("data length is " + data.length);
+ }
+
+ try
+ {
+ data = cryptEng.processData(data);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (debug)
+ {
+ System.out.println("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+ fail("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+
+ if (debug)
+ {
+ System.out.println("enrypted data representation\n"
+ // + "As String:.... " + new String(data) + "\n"
+ + "As BigInteger: " + new BigInteger(1, data));
+ System.out.println("data length is " + data.length);
+ }
+
+ try
+ {
+ data = decryptEng.processData(data);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (debug)
+ {
+ System.out.println("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+ fail("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+
+ if (debug)
+ {
+ System.out.println("decrypted data representation\n"
+ // + "As String:.... " + new String(data) + "\n"
+ + "As BigInteger: " + new BigInteger(1, data));
+ System.out.println("data length is " + data.length);
+ }
+
+ return data;
+
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new NaccacheSternTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NoekeonTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NoekeonTest.java
new file mode 100644
index 000000000..f17ff0ffe
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NoekeonTest.java
@@ -0,0 +1,45 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.NoekeonEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Noekeon tester
+ */
+public class NoekeonTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new NoekeonEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "b1656851699e29fa24b70148503d2dfc"),
+ new BlockCipherVectorTest(1, new NoekeonEngine(),
+ new KeyParameter(Hex.decode("ffffffffffffffffffffffffffffffff")),
+ "ffffffffffffffffffffffffffffffff",
+ "2a78421b87c7d0924f26113f1d1349b2"),
+ new BlockCipherVectorTest(2, new NoekeonEngine(),
+ new KeyParameter(Hex.decode("b1656851699e29fa24b70148503d2dfc")),
+ "2a78421b87c7d0924f26113f1d1349b2",
+ "e2f687e07b75660ffc372233bc47532c")
+ };
+
+ NoekeonTest()
+ {
+ super(tests, new NoekeonEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "Noekeon";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NoekeonTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NonMemoableDigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NonMemoableDigestTest.java
new file mode 100644
index 000000000..8abb02293
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NonMemoableDigestTest.java
@@ -0,0 +1,112 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.NonMemoableDigest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * SHA1 HMac Test, test vectors from RFC 2202
+ */
+public class NonMemoableDigestTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "b617318655057264e28bc0b6fb378c8ef146be00",
+ "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+ "125d7342b9ac11cd91a39af48aa17b4f63f175d3",
+ "4c9007f4026250c6bc8414f9bf50c86c2d7235da",
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91",
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "NonMemoableDigest";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new NonMemoableDigest(new SHA1Digest()));
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ NonMemoableDigestTest test = new NonMemoableDigestTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NullTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NullTest.java
new file mode 100644
index 000000000..f384cfc4c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/NullTest.java
@@ -0,0 +1,77 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.engines.NullEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class NullTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new NullEngine(),
+ new KeyParameter(Hex.decode("00")), "00", "00")
+ };
+
+ NullTest()
+ {
+ super(tests, new NullEngine(), new KeyParameter(new byte[2]));
+ }
+
+ public String getName()
+ {
+ return "Null";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ BlockCipher engine = new NullEngine();
+
+ engine.init(true, null);
+
+ byte[] buf = new byte[1];
+
+ engine.processBlock(buf, 0, buf, 0);
+
+ if (buf[0] != 0)
+ {
+ fail("NullCipher changed data!");
+ }
+
+ byte[] shortBuf = new byte[0];
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, buf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(buf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NullTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/OAEPTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/OAEPTest.java
new file mode 100644
index 000000000..30c575deb
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/OAEPTest.java
@@ -0,0 +1,830 @@
+package org.spongycastle.crypto.test;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.RSAPrivateKey;
+import org.spongycastle.asn1.pkcs.RSAPublicKey;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.encodings.OAEPEncoding;
+import org.spongycastle.crypto.engines.RSAEngine;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class OAEPTest
+ extends SimpleTest
+{
+ static byte[] pubKeyEnc1 =
+ {
+ (byte)0x30, (byte)0x5a, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
+ (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
+ (byte)0x00, (byte)0x03, (byte)0x49, (byte)0x00, (byte)0x30, (byte)0x46, (byte)0x02, (byte)0x41,
+ (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce, (byte)0x88, (byte)0xac, (byte)0xfd,
+ (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f, (byte)0xc4, (byte)0x52, (byte)0x3f,
+ (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3, (byte)0x77, (byte)0x4a, (byte)0x25,
+ (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5, (byte)0xd9, (byte)0x9c, (byte)0xb5,
+ (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28, (byte)0x5e, (byte)0x53, (byte)0x01,
+ (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb, (byte)0x68, (byte)0x76, (byte)0x93,
+ (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62, (byte)0x4a, (byte)0x11, (byte)0xe0,
+ (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc, (byte)0xac, (byte)0xa0, (byte)0xa1,
+ (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11
+ };
+
+ static byte[] privKeyEnc1 =
+ {
+ (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x52, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+ (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+ (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
+ (byte)0x01, (byte)0x3c, (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x38, (byte)0x02, (byte)0x01,
+ (byte)0x00, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce,
+ (byte)0x88, (byte)0xac, (byte)0xfd, (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f,
+ (byte)0xc4, (byte)0x52, (byte)0x3f, (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3,
+ (byte)0x77, (byte)0x4a, (byte)0x25, (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5,
+ (byte)0xd9, (byte)0x9c, (byte)0xb5, (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28,
+ (byte)0x5e, (byte)0x53, (byte)0x01, (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb,
+ (byte)0x68, (byte)0x76, (byte)0x93, (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62,
+ (byte)0x4a, (byte)0x11, (byte)0xe0, (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc,
+ (byte)0xac, (byte)0xa0, (byte)0xa1, (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11, (byte)0x02,
+ (byte)0x40, (byte)0x0a, (byte)0x03, (byte)0x37, (byte)0x48, (byte)0x62, (byte)0x64, (byte)0x87,
+ (byte)0x69, (byte)0x5f, (byte)0x5f, (byte)0x30, (byte)0xbc, (byte)0x38, (byte)0xb9, (byte)0x8b,
+ (byte)0x44, (byte)0xc2, (byte)0xcd, (byte)0x2d, (byte)0xff, (byte)0x43, (byte)0x40, (byte)0x98,
+ (byte)0xcd, (byte)0x20, (byte)0xd8, (byte)0xa1, (byte)0x38, (byte)0xd0, (byte)0x90, (byte)0xbf,
+ (byte)0x64, (byte)0x79, (byte)0x7c, (byte)0x3f, (byte)0xa7, (byte)0xa2, (byte)0xcd, (byte)0xcb,
+ (byte)0x3c, (byte)0xd1, (byte)0xe0, (byte)0xbd, (byte)0xba, (byte)0x26, (byte)0x54, (byte)0xb4,
+ (byte)0xf9, (byte)0xdf, (byte)0x8e, (byte)0x8a, (byte)0xe5, (byte)0x9d, (byte)0x73, (byte)0x3d,
+ (byte)0x9f, (byte)0x33, (byte)0xb3, (byte)0x01, (byte)0x62, (byte)0x4a, (byte)0xfd, (byte)0x1d,
+ (byte)0x51, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xd8, (byte)0x40, (byte)0xb4, (byte)0x16,
+ (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4,
+ (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52,
+ (byte)0x4d, (byte)0x04, (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00,
+ (byte)0xaf, (byte)0x46, (byte)0x12, (byte)0x0d, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xc9,
+ (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4, (byte)0x53, (byte)0xf6, (byte)0x34,
+ (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1, (byte)0xd9, (byte)0x35, (byte)0x3f,
+ (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66, (byte)0xb1, (byte)0xd0, (byte)0x5a,
+ (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x89, (byte)0x02,
+ (byte)0x20, (byte)0x59, (byte)0x0b, (byte)0x95, (byte)0x72, (byte)0xa2, (byte)0xc2, (byte)0xa9,
+ (byte)0xc4, (byte)0x06, (byte)0x05, (byte)0x9d, (byte)0xc2, (byte)0xab, (byte)0x2f, (byte)0x1d,
+ (byte)0xaf, (byte)0xeb, (byte)0x7e, (byte)0x8b, (byte)0x4f, (byte)0x10, (byte)0xa7, (byte)0x54,
+ (byte)0x9e, (byte)0x8e, (byte)0xed, (byte)0xf5, (byte)0xb4, (byte)0xfc, (byte)0xe0, (byte)0x9e,
+ (byte)0x05, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0x8e, (byte)0x3c, (byte)0x05, (byte)0x21,
+ (byte)0xfe, (byte)0x15, (byte)0xe0, (byte)0xea, (byte)0x06, (byte)0xa3, (byte)0x6f, (byte)0xf0,
+ (byte)0xf1, (byte)0x0c, (byte)0x99, (byte)0x52, (byte)0xc3, (byte)0x5b, (byte)0x7a, (byte)0x75,
+ (byte)0x14, (byte)0xfd, (byte)0x32, (byte)0x38, (byte)0xb8, (byte)0x0a, (byte)0xad, (byte)0x52,
+ (byte)0x98, (byte)0x62, (byte)0x8d, (byte)0x51, (byte)0x02, (byte)0x20, (byte)0x36, (byte)0x3f,
+ (byte)0xf7, (byte)0x18, (byte)0x9d, (byte)0xa8, (byte)0xe9, (byte)0x0b, (byte)0x1d, (byte)0x34,
+ (byte)0x1f, (byte)0x71, (byte)0xd0, (byte)0x9b, (byte)0x76, (byte)0xa8, (byte)0xa9, (byte)0x43,
+ (byte)0xe1, (byte)0x1d, (byte)0x10, (byte)0xb2, (byte)0x4d, (byte)0x24, (byte)0x9f, (byte)0x2d,
+ (byte)0xea, (byte)0xfe, (byte)0xf8, (byte)0x0c, (byte)0x18, (byte)0x26
+ };
+
+ static byte[] output1 =
+ {
+ (byte)0x1b, (byte)0x8f, (byte)0x05, (byte)0xf9, (byte)0xca, (byte)0x1a, (byte)0x79, (byte)0x52,
+ (byte)0x6e, (byte)0x53, (byte)0xf3, (byte)0xcc, (byte)0x51, (byte)0x4f, (byte)0xdb, (byte)0x89,
+ (byte)0x2b, (byte)0xfb, (byte)0x91, (byte)0x93, (byte)0x23, (byte)0x1e, (byte)0x78, (byte)0xb9,
+ (byte)0x92, (byte)0xe6, (byte)0x8d, (byte)0x50, (byte)0xa4, (byte)0x80, (byte)0xcb, (byte)0x52,
+ (byte)0x33, (byte)0x89, (byte)0x5c, (byte)0x74, (byte)0x95, (byte)0x8d, (byte)0x5d, (byte)0x02,
+ (byte)0xab, (byte)0x8c, (byte)0x0f, (byte)0xd0, (byte)0x40, (byte)0xeb, (byte)0x58, (byte)0x44,
+ (byte)0xb0, (byte)0x05, (byte)0xc3, (byte)0x9e, (byte)0xd8, (byte)0x27, (byte)0x4a, (byte)0x9d,
+ (byte)0xbf, (byte)0xa8, (byte)0x06, (byte)0x71, (byte)0x40, (byte)0x94, (byte)0x39, (byte)0xd2
+ };
+
+ static byte[] pubKeyEnc2 =
+ {
+ (byte)0x30, (byte)0x4c, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
+ (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
+ (byte)0x00, (byte)0x03, (byte)0x3b, (byte)0x00, (byte)0x30, (byte)0x38, (byte)0x02, (byte)0x33,
+ (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d, (byte)0xfd,
+ (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78, (byte)0xb8,
+ (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c, (byte)0x98,
+ (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b, (byte)0x26,
+ (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b, (byte)0x69,
+ (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b, (byte)0xe8,
+ (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03
+ };
+
+ static byte[] privKeyEnc2 =
+ {
+ (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x13, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+ (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+ (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x81,
+ (byte)0xfe, (byte)0x30, (byte)0x81, (byte)0xfb, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02,
+ (byte)0x33, (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d,
+ (byte)0xfd, (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78,
+ (byte)0xb8, (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c,
+ (byte)0x98, (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b,
+ (byte)0x26, (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b,
+ (byte)0x69, (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b,
+ (byte)0xe8, (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03, (byte)0x02,
+ (byte)0x32, (byte)0x6c, (byte)0xaf, (byte)0xbc, (byte)0x60, (byte)0x94, (byte)0xb3, (byte)0xfe,
+ (byte)0x4c, (byte)0x72, (byte)0xb0, (byte)0xb3, (byte)0x32, (byte)0xc6, (byte)0xfb, (byte)0x25,
+ (byte)0xa2, (byte)0xb7, (byte)0x62, (byte)0x29, (byte)0x80, (byte)0x4e, (byte)0x68, (byte)0x65,
+ (byte)0xfc, (byte)0xa4, (byte)0x5a, (byte)0x74, (byte)0xdf, (byte)0x0f, (byte)0x8f, (byte)0xb8,
+ (byte)0x41, (byte)0x3b, (byte)0x52, (byte)0xc0, (byte)0xd0, (byte)0xe5, (byte)0x3d, (byte)0x9b,
+ (byte)0x59, (byte)0x0f, (byte)0xf1, (byte)0x9b, (byte)0xe7, (byte)0x9f, (byte)0x49, (byte)0xdd,
+ (byte)0x21, (byte)0xe5, (byte)0xeb, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0xcf, (byte)0x20,
+ (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4,
+ (byte)0x16, (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3,
+ (byte)0xb4, (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x91, (byte)0x02,
+ (byte)0x1a, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
+ (byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
+ (byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
+ (byte)0xb1, (byte)0xd0, (byte)0x5f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x8a, (byte)0x15,
+ (byte)0x78, (byte)0xac, (byte)0x5d, (byte)0x13, (byte)0xaf, (byte)0x10, (byte)0x2b, (byte)0x22,
+ (byte)0xb9, (byte)0x99, (byte)0xcd, (byte)0x74, (byte)0x61, (byte)0xf1, (byte)0x5e, (byte)0x6d,
+ (byte)0x22, (byte)0xcc, (byte)0x03, (byte)0x23, (byte)0xdf, (byte)0xdf, (byte)0x0b, (byte)0x02,
+ (byte)0x1a, (byte)0x00, (byte)0x86, (byte)0x55, (byte)0x21, (byte)0x4a, (byte)0xc5, (byte)0x4d,
+ (byte)0x8d, (byte)0x4e, (byte)0xcd, (byte)0x61, (byte)0x77, (byte)0xf1, (byte)0xc7, (byte)0x36,
+ (byte)0x90, (byte)0xce, (byte)0x2a, (byte)0x48, (byte)0x2c, (byte)0x8b, (byte)0x05, (byte)0x99,
+ (byte)0xcb, (byte)0xe0, (byte)0x3f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x83, (byte)0xef,
+ (byte)0xef, (byte)0xb8, (byte)0xa9, (byte)0xa4, (byte)0x0d, (byte)0x1d, (byte)0xb6, (byte)0xed,
+ (byte)0x98, (byte)0xad, (byte)0x84, (byte)0xed, (byte)0x13, (byte)0x35, (byte)0xdc, (byte)0xc1,
+ (byte)0x08, (byte)0xf3, (byte)0x22, (byte)0xd0, (byte)0x57, (byte)0xcf, (byte)0x8d
+ };
+
+ static byte[] output2 =
+ {
+ (byte)0x14, (byte)0xbd, (byte)0xdd, (byte)0x28, (byte)0xc9, (byte)0x83, (byte)0x35, (byte)0x19,
+ (byte)0x23, (byte)0x80, (byte)0xe8, (byte)0xe5, (byte)0x49, (byte)0xb1, (byte)0x58, (byte)0x2a,
+ (byte)0x8b, (byte)0x40, (byte)0xb4, (byte)0x48, (byte)0x6d, (byte)0x03, (byte)0xa6, (byte)0xa5,
+ (byte)0x31, (byte)0x1f, (byte)0x1f, (byte)0xd5, (byte)0xf0, (byte)0xa1, (byte)0x80, (byte)0xe4,
+ (byte)0x17, (byte)0x53, (byte)0x03, (byte)0x29, (byte)0xa9, (byte)0x34, (byte)0x90, (byte)0x74,
+ (byte)0xb1, (byte)0x52, (byte)0x13, (byte)0x54, (byte)0x29, (byte)0x08, (byte)0x24, (byte)0x52,
+ (byte)0x62, (byte)0x51
+ };
+
+ static byte[] pubKeyEnc3 =
+ {
+ (byte)0x30, (byte)0x81, (byte)0x9d, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a,
+ (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01,
+ (byte)0x05, (byte)0x00, (byte)0x03, (byte)0x81, (byte)0x8b, (byte)0x00, (byte)0x30, (byte)0x81,
+ (byte)0x87, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
+ (byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
+ (byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
+ (byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
+ (byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
+ (byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
+ (byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
+ (byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
+ (byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
+ (byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
+ (byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
+ (byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
+ (byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
+ (byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
+ (byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
+ (byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
+ (byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11
+ };
+
+ static byte[] privKeyEnc3 =
+ {
+ (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x75, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+ (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+ (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
+ (byte)0x02, (byte)0x5f, (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x5b, (byte)0x02, (byte)0x01,
+ (byte)0x00, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
+ (byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
+ (byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
+ (byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
+ (byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
+ (byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
+ (byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
+ (byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
+ (byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
+ (byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
+ (byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
+ (byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
+ (byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
+ (byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
+ (byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
+ (byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
+ (byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11,
+ (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xa5, (byte)0xda, (byte)0xfc, (byte)0x53,
+ (byte)0x41, (byte)0xfa, (byte)0xf2, (byte)0x89, (byte)0xc4, (byte)0xb9, (byte)0x88, (byte)0xdb,
+ (byte)0x30, (byte)0xc1, (byte)0xcd, (byte)0xf8, (byte)0x3f, (byte)0x31, (byte)0x25, (byte)0x1e,
+ (byte)0x06, (byte)0x68, (byte)0xb4, (byte)0x27, (byte)0x84, (byte)0x81, (byte)0x38, (byte)0x01,
+ (byte)0x57, (byte)0x96, (byte)0x41, (byte)0xb2, (byte)0x94, (byte)0x10, (byte)0xb3, (byte)0xc7,
+ (byte)0x99, (byte)0x8d, (byte)0x6b, (byte)0xc4, (byte)0x65, (byte)0x74, (byte)0x5e, (byte)0x5c,
+ (byte)0x39, (byte)0x26, (byte)0x69, (byte)0xd6, (byte)0x87, (byte)0x0d, (byte)0xa2, (byte)0xc0,
+ (byte)0x82, (byte)0xa9, (byte)0x39, (byte)0xe3, (byte)0x7f, (byte)0xdc, (byte)0xb8, (byte)0x2e,
+ (byte)0xc9, (byte)0x3e, (byte)0xda, (byte)0xc9, (byte)0x7f, (byte)0xf3, (byte)0xad, (byte)0x59,
+ (byte)0x50, (byte)0xac, (byte)0xcf, (byte)0xbc, (byte)0x11, (byte)0x1c, (byte)0x76, (byte)0xf1,
+ (byte)0xa9, (byte)0x52, (byte)0x94, (byte)0x44, (byte)0xe5, (byte)0x6a, (byte)0xaf, (byte)0x68,
+ (byte)0xc5, (byte)0x6c, (byte)0x09, (byte)0x2c, (byte)0xd3, (byte)0x8d, (byte)0xc3, (byte)0xbe,
+ (byte)0xf5, (byte)0xd2, (byte)0x0a, (byte)0x93, (byte)0x99, (byte)0x26, (byte)0xed, (byte)0x4f,
+ (byte)0x74, (byte)0xa1, (byte)0x3e, (byte)0xdd, (byte)0xfb, (byte)0xe1, (byte)0xa1, (byte)0xce,
+ (byte)0xcc, (byte)0x48, (byte)0x94, (byte)0xaf, (byte)0x94, (byte)0x28, (byte)0xc2, (byte)0xb7,
+ (byte)0xb8, (byte)0x88, (byte)0x3f, (byte)0xe4, (byte)0x46, (byte)0x3a, (byte)0x4b, (byte)0xc8,
+ (byte)0x5b, (byte)0x1c, (byte)0xb3, (byte)0xc1, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xee,
+ (byte)0xcf, (byte)0xae, (byte)0x81, (byte)0xb1, (byte)0xb9, (byte)0xb3, (byte)0xc9, (byte)0x08,
+ (byte)0x81, (byte)0x0b, (byte)0x10, (byte)0xa1, (byte)0xb5, (byte)0x60, (byte)0x01, (byte)0x99,
+ (byte)0xeb, (byte)0x9f, (byte)0x44, (byte)0xae, (byte)0xf4, (byte)0xfd, (byte)0xa4, (byte)0x93,
+ (byte)0xb8, (byte)0x1a, (byte)0x9e, (byte)0x3d, (byte)0x84, (byte)0xf6, (byte)0x32, (byte)0x12,
+ (byte)0x4e, (byte)0xf0, (byte)0x23, (byte)0x6e, (byte)0x5d, (byte)0x1e, (byte)0x3b, (byte)0x7e,
+ (byte)0x28, (byte)0xfa, (byte)0xe7, (byte)0xaa, (byte)0x04, (byte)0x0a, (byte)0x2d, (byte)0x5b,
+ (byte)0x25, (byte)0x21, (byte)0x76, (byte)0x45, (byte)0x9d, (byte)0x1f, (byte)0x39, (byte)0x75,
+ (byte)0x41, (byte)0xba, (byte)0x2a, (byte)0x58, (byte)0xfb, (byte)0x65, (byte)0x99, (byte)0x02,
+ (byte)0x41, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
+ (byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
+ (byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
+ (byte)0xb1, (byte)0xd0, (byte)0x5a, (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b,
+ (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4, (byte)0x16, (byte)0x66, (byte)0xb4,
+ (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4, (byte)0x32, (byte)0x04,
+ (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52, (byte)0x4d, (byte)0x04,
+ (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00, (byte)0xaf, (byte)0x46,
+ (byte)0x15, (byte)0x03, (byte)0x02, (byte)0x40, (byte)0x54, (byte)0x49, (byte)0x4c, (byte)0xa6,
+ (byte)0x3e, (byte)0xba, (byte)0x03, (byte)0x37, (byte)0xe4, (byte)0xe2, (byte)0x40, (byte)0x23,
+ (byte)0xfc, (byte)0xd6, (byte)0x9a, (byte)0x5a, (byte)0xeb, (byte)0x07, (byte)0xdd, (byte)0xdc,
+ (byte)0x01, (byte)0x83, (byte)0xa4, (byte)0xd0, (byte)0xac, (byte)0x9b, (byte)0x54, (byte)0xb0,
+ (byte)0x51, (byte)0xf2, (byte)0xb1, (byte)0x3e, (byte)0xd9, (byte)0x49, (byte)0x09, (byte)0x75,
+ (byte)0xea, (byte)0xb7, (byte)0x74, (byte)0x14, (byte)0xff, (byte)0x59, (byte)0xc1, (byte)0xf7,
+ (byte)0x69, (byte)0x2e, (byte)0x9a, (byte)0x2e, (byte)0x20, (byte)0x2b, (byte)0x38, (byte)0xfc,
+ (byte)0x91, (byte)0x0a, (byte)0x47, (byte)0x41, (byte)0x74, (byte)0xad, (byte)0xc9, (byte)0x3c,
+ (byte)0x1f, (byte)0x67, (byte)0xc9, (byte)0x81, (byte)0x02, (byte)0x40, (byte)0x47, (byte)0x1e,
+ (byte)0x02, (byte)0x90, (byte)0xff, (byte)0x0a, (byte)0xf0, (byte)0x75, (byte)0x03, (byte)0x51,
+ (byte)0xb7, (byte)0xf8, (byte)0x78, (byte)0x86, (byte)0x4c, (byte)0xa9, (byte)0x61, (byte)0xad,
+ (byte)0xbd, (byte)0x3a, (byte)0x8a, (byte)0x7e, (byte)0x99, (byte)0x1c, (byte)0x5c, (byte)0x05,
+ (byte)0x56, (byte)0xa9, (byte)0x4c, (byte)0x31, (byte)0x46, (byte)0xa7, (byte)0xf9, (byte)0x80,
+ (byte)0x3f, (byte)0x8f, (byte)0x6f, (byte)0x8a, (byte)0xe3, (byte)0x42, (byte)0xe9, (byte)0x31,
+ (byte)0xfd, (byte)0x8a, (byte)0xe4, (byte)0x7a, (byte)0x22, (byte)0x0d, (byte)0x1b, (byte)0x99,
+ (byte)0xa4, (byte)0x95, (byte)0x84, (byte)0x98, (byte)0x07, (byte)0xfe, (byte)0x39, (byte)0xf9,
+ (byte)0x24, (byte)0x5a, (byte)0x98, (byte)0x36, (byte)0xda, (byte)0x3d, (byte)0x02, (byte)0x41,
+ (byte)0x00, (byte)0xb0, (byte)0x6c, (byte)0x4f, (byte)0xda, (byte)0xbb, (byte)0x63, (byte)0x01,
+ (byte)0x19, (byte)0x8d, (byte)0x26, (byte)0x5b, (byte)0xdb, (byte)0xae, (byte)0x94, (byte)0x23,
+ (byte)0xb3, (byte)0x80, (byte)0xf2, (byte)0x71, (byte)0xf7, (byte)0x34, (byte)0x53, (byte)0x88,
+ (byte)0x50, (byte)0x93, (byte)0x07, (byte)0x7f, (byte)0xcd, (byte)0x39, (byte)0xe2, (byte)0x11,
+ (byte)0x9f, (byte)0xc9, (byte)0x86, (byte)0x32, (byte)0x15, (byte)0x4f, (byte)0x58, (byte)0x83,
+ (byte)0xb1, (byte)0x67, (byte)0xa9, (byte)0x67, (byte)0xbf, (byte)0x40, (byte)0x2b, (byte)0x4e,
+ (byte)0x9e, (byte)0x2e, (byte)0x0f, (byte)0x96, (byte)0x56, (byte)0xe6, (byte)0x98, (byte)0xea,
+ (byte)0x36, (byte)0x66, (byte)0xed, (byte)0xfb, (byte)0x25, (byte)0x79, (byte)0x80, (byte)0x39,
+ (byte)0xf7
+ };
+
+ static byte[] output3 = Hex.decode(
+ "b8246b56a6ed5881aeb585d9a25b2ad790c417e080681bf1ac2bc3deb69d8bce"
+ + "f0c4366fec400af052a72e9b0effb5b3f2f192dbeaca03c12740057113bf1f06"
+ + "69ac22e9f3a7852e3c15d913cab0b8863a95c99294ce8674214954610346f4d4"
+ + "74b26f7c48b42ee68e1f572a1fc4026ac456b4f59f7b621ea1b9d88f64202fb1");
+
+ byte[] seed = {
+ (byte)0xaa, (byte)0xfd, (byte)0x12, (byte)0xf6, (byte)0x59,
+ (byte)0xca, (byte)0xe6, (byte)0x34, (byte)0x89, (byte)0xb4,
+ (byte)0x79, (byte)0xe5, (byte)0x07, (byte)0x6d, (byte)0xde,
+ (byte)0xc2, (byte)0xf0, (byte)0x6c, (byte)0xb5, (byte)0x8f
+ };
+
+ private class VecRand extends SecureRandom
+ {
+ byte[] seed;
+
+ VecRand(byte[] seed)
+ {
+ this.seed = seed;
+ }
+
+ public void nextBytes(
+ byte[] bytes)
+ {
+ System.arraycopy(seed, 0, bytes, 0, bytes.length);
+ }
+ }
+
+ private void baseOaepTest(
+ int id,
+ byte[] pubKeyEnc,
+ byte[] privKeyEnc,
+ byte[] output)
+ throws Exception
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(pubKeyEnc);
+ ASN1InputStream dIn = new ASN1InputStream(bIn);
+
+ //
+ // extract the public key info.
+ //
+ RSAPublicKey pubStruct;
+
+ pubStruct = RSAPublicKey.getInstance(new SubjectPublicKeyInfo((ASN1Sequence)dIn.readObject()).parsePublicKey());
+
+
+ bIn = new ByteArrayInputStream(privKeyEnc);
+ dIn = new ASN1InputStream(bIn);
+
+ //
+ // extract the private key info.
+ //
+ RSAPrivateKey privStruct;
+
+ privStruct = RSAPrivateKey.getInstance(new PrivateKeyInfo((ASN1Sequence)dIn.readObject()).parsePrivateKey());
+
+ RSAKeyParameters pubParameters = new RSAKeyParameters(
+ false,
+ pubStruct.getModulus(),
+ pubStruct.getPublicExponent());
+
+ RSAKeyParameters privParameters = new RSAPrivateCrtKeyParameters(
+ privStruct.getModulus(),
+ privStruct.getPublicExponent(),
+ privStruct.getPrivateExponent(),
+ privStruct.getPrime1(),
+ privStruct.getPrime2(),
+ privStruct.getExponent1(),
+ privStruct.getExponent2(),
+ privStruct.getCoefficient());
+
+ byte[] input = new byte[]
+ { (byte)0x54, (byte)0x85, (byte)0x9b, (byte)0x34, (byte)0x2c, (byte)0x49, (byte)0xea, (byte)0x2a };
+
+ encDec("id(" + id + ")", pubParameters, privParameters, seed, input, output);
+
+ }
+
+ private void encDec(
+ String label,
+ RSAKeyParameters pubParameters,
+ RSAKeyParameters privParameters,
+ byte[] seed,
+ byte[] input,
+ byte[] output)
+ throws InvalidCipherTextException
+ {
+ AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine());
+
+ cipher.init(true, new ParametersWithRandom(pubParameters, new VecRand(seed)));
+
+ byte[] out;
+
+ out = cipher.processBlock(input, 0, input.length);
+
+ for (int i = 0; i != output.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail(label + " failed encryption");
+ }
+ }
+
+ cipher.init(false, privParameters);
+
+ out = cipher.processBlock(output, 0, output.length);
+
+ for (int i = 0; i != input.length; i++)
+ {
+ if (out[i] != input[i])
+ {
+ fail(label + " failed decoding");
+ }
+ }
+ }
+
+ /*
+ * RSA vector tests from PKCS#1 page
+ */
+ byte[] modulus_1024 = Hex.decode(
+ "a8b3b284af8eb50b387034a860f146c4"
+ + "919f318763cd6c5598c8ae4811a1e0ab"
+ + "c4c7e0b082d693a5e7fced675cf46685"
+ + "12772c0cbc64a742c6c630f533c8cc72"
+ + "f62ae833c40bf25842e984bb78bdbf97"
+ + "c0107d55bdb662f5c4e0fab9845cb514"
+ + "8ef7392dd3aaff93ae1e6b667bb3d424"
+ + "7616d4f5ba10d4cfd226de88d39f16fb");
+
+ byte[] pubExp_1024 = Hex.decode(
+ "010001");
+
+ byte[] privExp_1024 = Hex.decode(
+ "53339cfdb79fc8466a655c7316aca85c"
+ + "55fd8f6dd898fdaf119517ef4f52e8fd"
+ + "8e258df93fee180fa0e4ab29693cd83b"
+ + "152a553d4ac4d1812b8b9fa5af0e7f55"
+ + "fe7304df41570926f3311f15c4d65a73"
+ + "2c483116ee3d3d2d0af3549ad9bf7cbf"
+ + "b78ad884f84d5beb04724dc7369b31de"
+ + "f37d0cf539e9cfcdd3de653729ead5d1");
+
+ byte[] prime1_1024 = Hex.decode(
+ "d32737e7267ffe1341b2d5c0d150a81b"
+ + "586fb3132bed2f8d5262864a9cb9f30a"
+ + "f38be448598d413a172efb802c21acf1"
+ + "c11c520c2f26a471dcad212eac7ca39d");
+
+ byte[] prime2_1024 = Hex.decode(
+ "cc8853d1d54da630fac004f471f281c7"
+ + "b8982d8224a490edbeb33d3e3d5cc93c"
+ + "4765703d1dd791642f1f116a0dd852be"
+ + "2419b2af72bfe9a030e860b0288b5d77");
+
+ byte[] primeExp1_1024 = Hex.decode(
+ "0e12bf1718e9cef5599ba1c3882fe804"
+ + "6a90874eefce8f2ccc20e4f2741fb0a3"
+ + "3a3848aec9c9305fbecbd2d76819967d"
+ + "4671acc6431e4037968db37878e695c1");
+
+ byte[] primeExp2_1024 = Hex.decode(
+ "95297b0f95a2fa67d00707d609dfd4fc"
+ + "05c89dafc2ef6d6ea55bec771ea33373"
+ + "4d9251e79082ecda866efef13c459e1a"
+ + "631386b7e354c899f5f112ca85d71583");
+
+ byte[] crtCoef_1024 = Hex.decode(
+ "4f456c502493bdc0ed2ab756a3a6ed4d"
+ + "67352a697d4216e93212b127a63d5411"
+ + "ce6fa98d5dbefd73263e372814274381"
+ + "8166ed7dd63687dd2a8ca1d2f4fbd8e1");
+
+ byte[] input_1024_1 = Hex.decode(
+ "6628194e12073db03ba94cda9ef95323"
+ + "97d50dba79b987004afefe34");
+
+ byte[] seed_1024_1 = Hex.decode(
+ "18b776ea21069d69776a33e96bad48e1"
+ + "dda0a5ef");
+
+ byte[] output_1024_1 = Hex.decode(
+ "354fe67b4a126d5d35fe36c777791a3f"
+ + "7ba13def484e2d3908aff722fad468fb"
+ + "21696de95d0be911c2d3174f8afcc201"
+ + "035f7b6d8e69402de5451618c21a535f"
+ + "a9d7bfc5b8dd9fc243f8cf927db31322"
+ + "d6e881eaa91a996170e657a05a266426"
+ + "d98c88003f8477c1227094a0d9fa1e8c"
+ + "4024309ce1ecccb5210035d47ac72e8a");
+
+ byte[] input_1024_2 = Hex.decode(
+ "750c4047f547e8e41411856523298ac9"
+ + "bae245efaf1397fbe56f9dd5");
+
+ byte[] seed_1024_2 = Hex.decode(
+ "0cc742ce4a9b7f32f951bcb251efd925"
+ + "fe4fe35f");
+
+ byte[] output_1024_2 = Hex.decode(
+ "640db1acc58e0568fe5407e5f9b701df"
+ + "f8c3c91e716c536fc7fcec6cb5b71c11"
+ + "65988d4a279e1577d730fc7a29932e3f"
+ + "00c81515236d8d8e31017a7a09df4352"
+ + "d904cdeb79aa583adcc31ea698a4c052"
+ + "83daba9089be5491f67c1a4ee48dc74b"
+ + "bbe6643aef846679b4cb395a352d5ed1"
+ + "15912df696ffe0702932946d71492b44");
+
+ byte[] input_1024_3 = Hex.decode(
+ "d94ae0832e6445ce42331cb06d531a82"
+ + "b1db4baad30f746dc916df24d4e3c245"
+ + "1fff59a6423eb0e1d02d4fe646cf699d"
+ + "fd818c6e97b051");
+
+ byte[] seed_1024_3 = Hex.decode(
+ "2514df4695755a67b288eaf4905c36ee"
+ + "c66fd2fd");
+
+ byte[] output_1024_3 = Hex.decode(
+ "423736ed035f6026af276c35c0b3741b"
+ + "365e5f76ca091b4e8c29e2f0befee603"
+ + "595aa8322d602d2e625e95eb81b2f1c9"
+ + "724e822eca76db8618cf09c5343503a4"
+ + "360835b5903bc637e3879fb05e0ef326"
+ + "85d5aec5067cd7cc96fe4b2670b6eac3"
+ + "066b1fcf5686b68589aafb7d629b02d8"
+ + "f8625ca3833624d4800fb081b1cf94eb");
+
+ byte[] input_1024_4 = Hex.decode(
+ "52e650d98e7f2a048b4f86852153b97e"
+ + "01dd316f346a19f67a85");
+
+ byte[] seed_1024_4 = Hex.decode(
+ "c4435a3e1a18a68b6820436290a37cef"
+ + "b85db3fb");
+
+ byte[] output_1024_4 = Hex.decode(
+ "45ead4ca551e662c9800f1aca8283b05"
+ + "25e6abae30be4b4aba762fa40fd3d38e"
+ + "22abefc69794f6ebbbc05ddbb1121624"
+ + "7d2f412fd0fba87c6e3acd888813646f"
+ + "d0e48e785204f9c3f73d6d8239562722"
+ + "dddd8771fec48b83a31ee6f592c4cfd4"
+ + "bc88174f3b13a112aae3b9f7b80e0fc6"
+ + "f7255ba880dc7d8021e22ad6a85f0755");
+
+ byte[] input_1024_5 = Hex.decode(
+ "8da89fd9e5f974a29feffb462b49180f"
+ + "6cf9e802");
+
+ byte[] seed_1024_5 = Hex.decode(
+ "b318c42df3be0f83fea823f5a7b47ed5"
+ + "e425a3b5");
+
+ byte[] output_1024_5 = Hex.decode(
+ "36f6e34d94a8d34daacba33a2139d00a"
+ + "d85a9345a86051e73071620056b920e2"
+ + "19005855a213a0f23897cdcd731b4525"
+ + "7c777fe908202befdd0b58386b1244ea"
+ + "0cf539a05d5d10329da44e13030fd760"
+ + "dcd644cfef2094d1910d3f433e1c7c6d"
+ + "d18bc1f2df7f643d662fb9dd37ead905"
+ + "9190f4fa66ca39e869c4eb449cbdc439");
+
+ byte[] input_1024_6 = Hex.decode(
+ "26521050844271");
+
+ byte[] seed_1024_6 = Hex.decode(
+ "e4ec0982c2336f3a677f6a356174eb0c"
+ + "e887abc2");
+
+ byte[] output_1024_6 = Hex.decode(
+ "42cee2617b1ecea4db3f4829386fbd61"
+ + "dafbf038e180d837c96366df24c097b4"
+ + "ab0fac6bdf590d821c9f10642e681ad0"
+ + "5b8d78b378c0f46ce2fad63f74e0ad3d"
+ + "f06b075d7eb5f5636f8d403b9059ca76"
+ + "1b5c62bb52aa45002ea70baace08ded2"
+ + "43b9d8cbd62a68ade265832b56564e43"
+ + "a6fa42ed199a099769742df1539e8255");
+
+ byte[] modulus_1027 = Hex.decode(
+ "051240b6cc0004fa48d0134671c078c7"
+ + "c8dec3b3e2f25bc2564467339db38853"
+ + "d06b85eea5b2de353bff42ac2e46bc97"
+ + "fae6ac9618da9537a5c8f553c1e35762"
+ + "5991d6108dcd7885fb3a25413f53efca"
+ + "d948cb35cd9b9ae9c1c67626d113d57d"
+ + "de4c5bea76bb5bb7de96c00d07372e96"
+ + "85a6d75cf9d239fa148d70931b5f3fb0"
+ + "39");
+
+ byte[] pubExp_1027 = Hex.decode(
+ "010001");
+
+ byte[] privExp_1027 = Hex.decode(
+ "0411ffca3b7ca5e9e9be7fe38a85105e"
+ + "353896db05c5796aecd2a725161eb365"
+ + "1c8629a9b862b904d7b0c7b37f8cb5a1"
+ + "c2b54001018a00a1eb2cafe4ee4e9492"
+ + "c348bc2bedab4b9ebbf064e8eff322b9"
+ + "009f8eec653905f40df88a3cdc49d456"
+ + "7f75627d41aca624129b46a0b7c698e5"
+ + "e65f2b7ba102c749a10135b6540d0401");
+
+ byte[] prime1_1027 = Hex.decode(
+ "027458c19ec1636919e736c9af25d609"
+ + "a51b8f561d19c6bf6943dd1ee1ab8a4a"
+ + "3f232100bd40b88decc6ba235548b6ef"
+ + "792a11c9de823d0a7922c7095b6eba57"
+ + "01");
+
+ byte[] prime2_1027 = Hex.decode(
+ "0210ee9b33ab61716e27d251bd465f4b"
+ + "35a1a232e2da00901c294bf22350ce49"
+ + "0d099f642b5375612db63ba1f2038649"
+ + "2bf04d34b3c22bceb909d13441b53b51"
+ + "39");
+
+ byte[] primeExp1_1027 = Hex.decode(
+ "39fa028b826e88c1121b750a8b242fa9"
+ + "a35c5b66bdfd1fa637d3cc48a84a4f45"
+ + "7a194e7727e49f7bcc6e5a5a412657fc"
+ + "470c7322ebc37416ef458c307a8c0901");
+
+ byte[] primeExp2_1027 = Hex.decode(
+ "015d99a84195943979fa9e1be2c3c1b6"
+ + "9f432f46fd03e47d5befbbbfd6b1d137"
+ + "1d83efb330a3e020942b2fed115e5d02"
+ + "be24fd92c9019d1cecd6dd4cf1e54cc8"
+ + "99");
+
+ byte[] crtCoef_1027 = Hex.decode(
+ "01f0b7015170b3f5e42223ba30301c41"
+ + "a6d87cbb70e30cb7d3c67d25473db1f6"
+ + "cbf03e3f9126e3e97968279a865b2c2b"
+ + "426524cfc52a683d31ed30eb984be412"
+ + "ba");
+
+ byte[] input_1027_1 = Hex.decode(
+ "4a86609534ee434a6cbca3f7e962e76d"
+ + "455e3264c19f605f6e5ff6137c65c56d"
+ + "7fb344cd52bc93374f3d166c9f0c6f9c"
+ + "506bad19330972d2");
+
+ byte[] seed_1027_1 = Hex.decode(
+ "1cac19ce993def55f98203f6852896c9"
+ + "5ccca1f3");
+
+ byte[] output_1027_1 = Hex.decode(
+ "04cce19614845e094152a3fe18e54e33"
+ + "30c44e5efbc64ae16886cb1869014cc5"
+ + "781b1f8f9e045384d0112a135ca0d12e"
+ + "9c88a8e4063416deaae3844f60d6e96f"
+ + "e155145f4525b9a34431ca3766180f70"
+ + "e15a5e5d8e8b1a516ff870609f13f896"
+ + "935ced188279a58ed13d07114277d75c"
+ + "6568607e0ab092fd803a223e4a8ee0b1"
+ + "a8");
+
+ byte[] input_1027_2 = Hex.decode(
+ "b0adc4f3fe11da59ce992773d9059943"
+ + "c03046497ee9d9f9a06df1166db46d98"
+ + "f58d27ec074c02eee6cbe2449c8b9fc5"
+ + "080c5c3f4433092512ec46aa793743c8");
+
+ byte[] seed_1027_2 = Hex.decode(
+ "f545d5897585e3db71aa0cb8da76c51d"
+ + "032ae963");
+
+ byte[] output_1027_2 = Hex.decode(
+ "0097b698c6165645b303486fbf5a2a44"
+ + "79c0ee85889b541a6f0b858d6b6597b1"
+ + "3b854eb4f839af03399a80d79bda6578"
+ + "c841f90d645715b280d37143992dd186"
+ + "c80b949b775cae97370e4ec97443136c"
+ + "6da484e970ffdb1323a20847821d3b18"
+ + "381de13bb49aaea66530c4a4b8271f3e"
+ + "ae172cd366e07e6636f1019d2a28aed1"
+ + "5e");
+
+ byte[] input_1027_3 = Hex.decode(
+ "bf6d42e701707b1d0206b0c8b45a1c72"
+ + "641ff12889219a82bdea965b5e79a96b"
+ + "0d0163ed9d578ec9ada20f2fbcf1ea3c"
+ + "4089d83419ba81b0c60f3606da99");
+
+ byte[] seed_1027_3 = Hex.decode(
+ "ad997feef730d6ea7be60d0dc52e72ea"
+ + "cbfdd275");
+
+ byte[] output_1027_3 = Hex.decode(
+ "0301f935e9c47abcb48acbbe09895d9f"
+ + "5971af14839da4ff95417ee453d1fd77"
+ + "319072bb7297e1b55d7561cd9d1bb24c"
+ + "1a9a37c619864308242804879d86ebd0"
+ + "01dce5183975e1506989b70e5a834341"
+ + "54d5cbfd6a24787e60eb0c658d2ac193"
+ + "302d1192c6e622d4a12ad4b53923bca2"
+ + "46df31c6395e37702c6a78ae081fb9d0"
+ + "65");
+
+ byte[] input_1027_4 = Hex.decode(
+ "fb2ef112f5e766eb94019297934794f7"
+ + "be2f6fc1c58e");
+
+ byte[] seed_1027_4 = Hex.decode(
+ "136454df5730f73c807a7e40d8c1a312"
+ + "ac5b9dd3");
+
+ byte[] output_1027_4 = Hex.decode(
+ "02d110ad30afb727beb691dd0cf17d0a"
+ + "f1a1e7fa0cc040ec1a4ba26a42c59d0a"
+ + "796a2e22c8f357ccc98b6519aceb682e"
+ + "945e62cb734614a529407cd452bee3e4"
+ + "4fece8423cc19e55548b8b994b849c7e"
+ + "cde4933e76037e1d0ce44275b08710c6"
+ + "8e430130b929730ed77e09b015642c55"
+ + "93f04e4ffb9410798102a8e96ffdfe11"
+ + "e4");
+
+ byte[] input_1027_5 = Hex.decode(
+ "28ccd447bb9e85166dabb9e5b7d1adad"
+ + "c4b9d39f204e96d5e440ce9ad928bc1c"
+ + "2284");
+
+ byte[] seed_1027_5 = Hex.decode(
+ "bca8057f824b2ea257f2861407eef63d"
+ + "33208681");
+
+ byte[] output_1027_5 = Hex.decode(
+ "00dbb8a7439d90efd919a377c54fae8f"
+ + "e11ec58c3b858362e23ad1b8a4431079"
+ + "9066b99347aa525691d2adc58d9b06e3"
+ + "4f288c170390c5f0e11c0aa3645959f1"
+ + "8ee79e8f2be8d7ac5c23d061f18dd74b"
+ + "8c5f2a58fcb5eb0c54f99f01a8324756"
+ + "8292536583340948d7a8c97c4acd1e98"
+ + "d1e29dc320e97a260532a8aa7a758a1e"
+ + "c2");
+
+ byte[] input_1027_6 = Hex.decode(
+ "f22242751ec6b1");
+
+ byte[] seed_1027_6 = Hex.decode(
+ "2e7e1e17f647b5ddd033e15472f90f68"
+ + "12f3ac4e");
+
+ byte[] output_1027_6 = Hex.decode(
+ "00a5ffa4768c8bbecaee2db77e8f2eec"
+ + "99595933545520835e5ba7db9493d3e1"
+ + "7cddefe6a5f567624471908db4e2d83a"
+ + "0fbee60608fc84049503b2234a07dc83"
+ + "b27b22847ad8920ff42f674ef79b7628"
+ + "0b00233d2b51b8cb2703a9d42bfbc825"
+ + "0c96ec32c051e57f1b4ba528db89c37e"
+ + "4c54e27e6e64ac69635ae887d9541619"
+ + "a9");
+
+ private void oaepVecTest(
+ int keySize,
+ int no,
+ RSAKeyParameters pubParam,
+ RSAKeyParameters privParam,
+ byte[] seed,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ encDec(keySize + " " + no, pubParam, privParam, seed, input, output);
+ }
+
+ public OAEPTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "OAEP";
+ }
+
+ public void performTest() throws Exception
+ {
+ baseOaepTest(1, pubKeyEnc1, privKeyEnc1, output1);
+ baseOaepTest(2, pubKeyEnc2, privKeyEnc2, output2);
+ baseOaepTest(3, pubKeyEnc3, privKeyEnc3, output3);
+
+ RSAKeyParameters pubParam = new RSAKeyParameters(false, new BigInteger(1, modulus_1024), new BigInteger(1, pubExp_1024));
+ RSAKeyParameters privParam = new RSAPrivateCrtKeyParameters(pubParam.getModulus(), pubParam.getExponent(), new BigInteger(1, privExp_1024), new BigInteger(1, prime1_1024), new BigInteger(1, prime2_1024), new BigInteger(1, primeExp1_1024), new BigInteger(1, primeExp2_1024), new BigInteger(1, crtCoef_1024));
+
+ oaepVecTest(1024, 1, pubParam, privParam, seed_1024_1, input_1024_1, output_1024_1);
+ oaepVecTest(1024, 2, pubParam, privParam, seed_1024_2, input_1024_2, output_1024_2);
+ oaepVecTest(1024, 3, pubParam, privParam, seed_1024_3, input_1024_3, output_1024_3);
+ oaepVecTest(1024, 4, pubParam, privParam, seed_1024_4, input_1024_4, output_1024_4);
+ oaepVecTest(1024, 5, pubParam, privParam, seed_1024_5, input_1024_5, output_1024_5);
+ oaepVecTest(1024, 6, pubParam, privParam, seed_1024_6, input_1024_6, output_1024_6);
+
+ pubParam = new RSAKeyParameters(false, new BigInteger(1, modulus_1027), new BigInteger(1, pubExp_1027));
+ privParam = new RSAPrivateCrtKeyParameters(pubParam.getModulus(), pubParam.getExponent(), new BigInteger(1, privExp_1027), new BigInteger(1, prime1_1027), new BigInteger(1, prime2_1027), new BigInteger(1, primeExp1_1027), new BigInteger(1, primeExp2_1027), new BigInteger(1, crtCoef_1027));
+
+ oaepVecTest(1027, 1, pubParam, privParam, seed_1027_1, input_1027_1, output_1027_1);
+ oaepVecTest(1027, 2, pubParam, privParam, seed_1027_2, input_1027_2, output_1027_2);
+ oaepVecTest(1027, 3, pubParam, privParam, seed_1027_3, input_1027_3, output_1027_3);
+ oaepVecTest(1027, 4, pubParam, privParam, seed_1027_4, input_1027_4, output_1027_4);
+ oaepVecTest(1027, 5, pubParam, privParam, seed_1027_5, input_1027_5, output_1027_5);
+ oaepVecTest(1027, 6, pubParam, privParam, seed_1027_6, input_1027_6, output_1027_6);
+
+ //
+ // OAEP - public encrypt, private decrypt differring hashes
+ //
+ AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine(), new SHA256Digest(), new SHA1Digest(), new byte[10]);
+
+ cipher.init(true, new ParametersWithRandom(pubParam, new SecureRandom()));
+
+ byte[] input = new byte[10];
+
+ byte[] out = cipher.processBlock(input, 0, input.length);
+
+ cipher.init(false, privParam);
+
+ out = cipher.processBlock(out, 0, out.length);
+
+ for (int i = 0; i != input.length; i++)
+ {
+ if (out[i] != input[i])
+ {
+ fail("mixed digest failed decoding");
+ }
+ }
+
+ cipher = new OAEPEncoding(new RSAEngine(), new SHA1Digest(), new SHA256Digest(), new byte[10]);
+
+ cipher.init(true, new ParametersWithRandom(pubParam, new SecureRandom()));
+
+ out = cipher.processBlock(input, 0, input.length);
+
+ cipher.init(false, privParam);
+
+ out = cipher.processBlock(out, 0, out.length);
+
+ for (int i = 0; i != input.length; i++)
+ {
+ if (out[i] != input[i])
+ {
+ fail("mixed digest failed decoding");
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new OAEPTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/OCBTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/OCBTest.java
new file mode 100644
index 000000000..d3bd2b784
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/OCBTest.java
@@ -0,0 +1,296 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+import org.spongycastle.crypto.modes.OCBBlockCipher;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the "work in progress" Internet-Draft The OCB Authenticated-Encryption
+ * Algorithm
+ */
+public class OCBTest
+ extends SimpleTest
+{
+
+ private static final String K = "000102030405060708090A0B0C0D0E0F";
+ private static final String N = "000102030405060708090A0B";
+
+ // Each test vector contains the strings A, P, C in order
+ private static final String[][] TEST_VECTORS = new String[][]{
+ {"", "", "197B9C3C441D3C83EAFB2BEF633B9182"},
+ {"0001020304050607", "0001020304050607",
+ "92B657130A74B85A16DC76A46D47E1EAD537209E8A96D14E"},
+ {"0001020304050607", "", "98B91552C8C009185044E30A6EB2FE21"},
+ {"", "0001020304050607", "92B657130A74B85A971EFFCAE19AD4716F88E87B871FBEED"},
+ {"000102030405060708090A0B0C0D0E0F", "000102030405060708090A0B0C0D0E0F",
+ "BEA5E8798DBE7110031C144DA0B26122776C9924D6723A1F" + "C4524532AC3E5BEB"},
+ {"000102030405060708090A0B0C0D0E0F", "", "7DDB8E6CEA6814866212509619B19CC6"},
+ {"", "000102030405060708090A0B0C0D0E0F",
+ "BEA5E8798DBE7110031C144DA0B2612213CC8B747807121A" + "4CBB3E4BD6B456AF"},
+ {"000102030405060708090A0B0C0D0E0F1011121314151617",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "BEA5E8798DBE7110031C144DA0B26122FCFCEE7A2A8D4D48" + "5FA94FC3F38820F1DC3F3D1FD4E55E1C"},
+ {"000102030405060708090A0B0C0D0E0F1011121314151617", "",
+ "282026DA3068BC9FA118681D559F10F6"},
+ {"", "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "BEA5E8798DBE7110031C144DA0B26122FCFCEE7A2A8D4D48" + "6EF2F52587FDA0ED97DC7EEDE241DF68"},
+ {
+ "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F",
+ "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F",
+ "BEA5E8798DBE7110031C144DA0B26122CEAAB9B05DF771A6"
+ + "57149D53773463CBB2A040DD3BD5164372D76D7BB6824240"},
+ {"000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F", "",
+ "E1E072633BADE51A60E85951D9C42A1B"},
+ {
+ "",
+ "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F",
+ "BEA5E8798DBE7110031C144DA0B26122CEAAB9B05DF771A6"
+ + "57149D53773463CB4A3BAE824465CFDAF8C41FC50C7DF9D9"},
+ {
+ "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627",
+ "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627",
+ "BEA5E8798DBE7110031C144DA0B26122CEAAB9B05DF771A6"
+ + "57149D53773463CB68C65778B058A635659C623211DEEA0D" + "E30D2C381879F4C8"},
+ {"000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627",
+ "", "7AEB7A69A1687DD082CA27B0D9A37096"},
+ {
+ "",
+ "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627",
+ "BEA5E8798DBE7110031C144DA0B26122CEAAB9B05DF771A6"
+ + "57149D53773463CB68C65778B058A635060C8467F4ABAB5E" + "8B3C2067A2E115DC"},
+
+ };
+
+ public String getName()
+ {
+ return "OCB";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i < TEST_VECTORS.length; ++i)
+ {
+ runTestCase("Test Case " + i, TEST_VECTORS[i]);
+ }
+
+ runLongerTestCase(128, 128, Hex.decode("B2B41CBF9B05037DA7F16C24A35C1C94"));
+ runLongerTestCase(192, 128, Hex.decode("1529F894659D2B51B776740211E7D083"));
+ runLongerTestCase(256, 128, Hex.decode("42B83106E473C0EEE086C8D631FD4C7B"));
+ runLongerTestCase(128, 96, Hex.decode("1A4F0654277709A5BDA0D380"));
+ runLongerTestCase(192, 96, Hex.decode("AD819483E01DD648978F4522"));
+ runLongerTestCase(256, 96, Hex.decode("CD2E41379C7E7C4458CCFB4A"));
+ runLongerTestCase(128, 64, Hex.decode("B7ECE9D381FE437F"));
+ runLongerTestCase(192, 64, Hex.decode("DE0574C87FF06DF9"));
+ runLongerTestCase(256, 64, Hex.decode("833E45FF7D332F7E"));
+
+ testExceptions();
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ OCBBlockCipher ocb = new OCBBlockCipher(new AESFastEngine(), new AESFastEngine());
+
+ try
+ {
+ ocb = new OCBBlockCipher(new DESEngine(), new DESEngine());
+
+ fail("incorrect block size not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ocb.init(false, new KeyParameter(new byte[16]));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ AEADTestUtil.testReset(this, new OCBBlockCipher(new AESEngine(), new AESEngine()), new OCBBlockCipher(new AESEngine(), new AESEngine()), new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[15]));
+ AEADTestUtil.testTampering(this, ocb, new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[15]));
+ }
+
+ private void runTestCase(String testName, String[] testVector)
+ throws InvalidCipherTextException
+ {
+
+ runTestCase(testName, testVector, 128);
+ }
+
+ private void runTestCase(String testName, String[] testVector, int macLengthBits)
+ throws InvalidCipherTextException
+ {
+
+ byte[] key = Hex.decode(K);
+ byte[] nonce = Hex.decode(N);
+
+ int pos = 0;
+ byte[] A = Hex.decode(testVector[pos++]);
+ byte[] P = Hex.decode(testVector[pos++]);
+ byte[] C = Hex.decode(testVector[pos++]);
+
+ int macLengthBytes = macLengthBits / 8;
+
+ // TODO Variations processing AAD and cipher bytes incrementally
+
+ KeyParameter keyParameter = new KeyParameter(key);
+ AEADParameters aeadParameters = new AEADParameters(keyParameter, macLengthBits, nonce, A);
+
+ OCBBlockCipher encCipher = initCipher(true, aeadParameters);
+ OCBBlockCipher decCipher = initCipher(false, aeadParameters);
+
+ checkTestCase(encCipher, decCipher, testName, macLengthBytes, P, C);
+ checkTestCase(encCipher, decCipher, testName + " (reused)", macLengthBytes, P, C);
+
+ // TODO Key reuse
+ }
+
+ private OCBBlockCipher initCipher(boolean forEncryption, AEADParameters parameters)
+ {
+ OCBBlockCipher c = new OCBBlockCipher(new AESFastEngine(), new AESFastEngine());
+ c.init(forEncryption, parameters);
+ return c;
+ }
+
+ private void checkTestCase(OCBBlockCipher encCipher, OCBBlockCipher decCipher, String testName,
+ int macLengthBytes, byte[] P, byte[] C)
+ throws InvalidCipherTextException
+ {
+
+ byte[] tag = Arrays.copyOfRange(C, C.length - macLengthBytes, C.length);
+
+ {
+ byte[] enc = new byte[encCipher.getOutputSize(P.length)];
+ int len = encCipher.processBytes(P, 0, P.length, enc, 0);
+ len += encCipher.doFinal(enc, len);
+
+ if (enc.length != len)
+ {
+ fail("encryption reported incorrect length: " + testName);
+ }
+
+ if (!areEqual(C, enc))
+ {
+ fail("incorrect encrypt in: " + testName);
+ }
+
+ if (!areEqual(tag, encCipher.getMac()))
+ {
+ fail("getMac() not the same as the appended tag: " + testName);
+ }
+ }
+
+ {
+ byte[] dec = new byte[decCipher.getOutputSize(C.length)];
+ int len = decCipher.processBytes(C, 0, C.length, dec, 0);
+ len += decCipher.doFinal(dec, len);
+
+ if (dec.length != len)
+ {
+ fail("decryption reported incorrect length: " + testName);
+ }
+
+ if (!areEqual(P, dec))
+ {
+ fail("incorrect decrypt in: " + testName);
+ }
+
+ if (!areEqual(tag, decCipher.getMac()))
+ {
+ fail("getMac() not the same as the appended tag: " + testName);
+ }
+ }
+ }
+
+ private void runLongerTestCase(int aesKeySize, int tagLen, byte[] expectedOutput)
+ throws InvalidCipherTextException
+ {
+ KeyParameter key = new KeyParameter(new byte[aesKeySize / 8]);
+ byte[] N = new byte[12];
+
+ AEADBlockCipher c1 = new OCBBlockCipher(new AESFastEngine(), new AESFastEngine());
+ c1.init(true, new AEADParameters(key, tagLen, N));
+
+ AEADBlockCipher c2 = new OCBBlockCipher(new AESFastEngine(), new AESFastEngine());
+
+ long total = 0;
+
+ byte[] S = new byte[128];
+
+ for (int i = 0; i < 128; ++i)
+ {
+ N[11] = (byte)i;
+
+ c2.init(true, new AEADParameters(key, tagLen, N));
+
+ total += updateCiphers(c1, c2, S, i, true, true);
+ total += updateCiphers(c1, c2, S, i, false, true);
+ total += updateCiphers(c1, c2, S, i, true, false);
+ }
+
+ long expectedTotal = 16256 + (48 * tagLen);
+
+ if (total != expectedTotal)
+ {
+ fail("test generated the wrong amount of input: " + total);
+ }
+
+ byte[] output = new byte[c1.getOutputSize(0)];
+ c1.doFinal(output, 0);
+
+ if (!areEqual(expectedOutput, output))
+ {
+ fail("incorrect encrypt in long-form test");
+ }
+ }
+
+ private int updateCiphers(AEADBlockCipher c1, AEADBlockCipher c2, byte[] S, int i,
+ boolean includeAAD, boolean includePlaintext)
+ throws InvalidCipherTextException
+ {
+
+ int inputLen = includePlaintext ? i : 0;
+ int outputLen = c2.getOutputSize(inputLen);
+
+ byte[] output = new byte[outputLen];
+
+ int len = 0;
+
+ if (includeAAD)
+ {
+ c2.processAADBytes(S, 0, i);
+ }
+
+ if (includePlaintext)
+ {
+ len += c2.processBytes(S, 0, i, output, len);
+ }
+
+ len += c2.doFinal(output, len);
+
+ c1.processAADBytes(output, 0, len);
+
+ return len;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new OCBTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PKCS12Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PKCS12Test.java
new file mode 100644
index 000000000..5374116a7
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PKCS12Test.java
@@ -0,0 +1,206 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * test for PKCS12 key generation - vectors from
+ *
+ * http://www.drh-consultancy.demon.co.uk/test.txt
+ */
+public class PKCS12Test
+ implements Test
+{
+ char[] password1 = { 's', 'm', 'e', 'g' };
+ char[] password2 = { 'q', 'u', 'e', 'e', 'g' };
+
+ private boolean isEqual(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private TestResult run1(
+ int id,
+ char[] password,
+ byte[] salt,
+ int iCount,
+ byte[] result)
+ {
+ PBEParametersGenerator generator = new PKCS12ParametersGenerator(
+ new SHA1Digest());
+
+ generator.init(
+ PBEParametersGenerator.PKCS12PasswordToBytes(password),
+ salt,
+ iCount);
+
+ CipherParameters key = generator.generateDerivedParameters(24 * 8);
+
+ if (isEqual(result, ((KeyParameter)key).getKey()))
+ {
+ return new SimpleTestResult(true, "PKCS12Test: Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, "PKCS12Test: id "
+ + id + " Failed");
+ }
+ }
+
+ private TestResult run2(
+ int id,
+ char[] password,
+ byte[] salt,
+ int iCount,
+ byte[] result)
+ {
+ PBEParametersGenerator generator = new PKCS12ParametersGenerator(
+ new SHA1Digest());
+
+ generator.init(
+ PBEParametersGenerator.PKCS12PasswordToBytes(password),
+ salt,
+ iCount);
+
+ ParametersWithIV params = (ParametersWithIV)generator.generateDerivedParameters(64, 64);
+
+ if (isEqual(result, params.getIV()))
+ {
+ return new SimpleTestResult(true, "PKCS12Test: Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, "PKCS12Test: id "
+ + id + " Failed");
+ }
+ }
+
+ private TestResult run3(
+ int id,
+ char[] password,
+ byte[] salt,
+ int iCount,
+ byte[] result)
+ {
+ PBEParametersGenerator generator = new PKCS12ParametersGenerator(
+ new SHA1Digest());
+
+ generator.init(
+ PBEParametersGenerator.PKCS12PasswordToBytes(password),
+ salt,
+ iCount);
+
+ CipherParameters key = generator.generateDerivedMacParameters(160);
+
+ if (isEqual(result, ((KeyParameter)key).getKey()))
+ {
+ return new SimpleTestResult(true, "PKCS12Test: Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, "PKCS12Test: id "
+ + id + " Failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "PKCS12Test";
+ }
+
+ public TestResult perform()
+ {
+ TestResult result;
+
+ result = run1(1, password1, Hex.decode("0A58CF64530D823F"), 1,
+ Hex.decode("8AAAE6297B6CB04642AB5B077851284EB7128F1A2A7FBCA3"));
+
+ if (result.isSuccessful())
+ {
+ result = run2(2, password1, Hex.decode("0A58CF64530D823F"), 1,
+ Hex.decode("79993DFE048D3B76"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run1(3, password1, Hex.decode("642B99AB44FB4B1F"), 1,
+ Hex.decode("F3A95FEC48D7711E985CFE67908C5AB79FA3D7C5CAA5D966"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run2(4, password1, Hex.decode("642B99AB44FB4B1F"), 1,
+ Hex.decode("C0A38D64A79BEA1D"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run3(5, password1, Hex.decode("3D83C0E4546AC140"), 1,
+ Hex.decode("8D967D88F6CAA9D714800AB3D48051D63F73A312"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run1(6, password2, Hex.decode("05DEC959ACFF72F7"), 1000,
+ Hex.decode("ED2034E36328830FF09DF1E1A07DD357185DAC0D4F9EB3D4"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run2(7, password2, Hex.decode("05DEC959ACFF72F7"), 1000,
+ Hex.decode("11DEDAD7758D4860"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run1(8, password2, Hex.decode("1682C0FC5B3F7EC5"), 1000,
+ Hex.decode("483DD6E919D7DE2E8E648BA8F862F3FBFBDC2BCB2C02957F"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run2(9, password2, Hex.decode("1682C0FC5B3F7EC5"), 1000,
+ Hex.decode("9D461D1B00355C50"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run3(10, password2, Hex.decode("263216FCC2FAB31C"), 1000,
+ Hex.decode("5EC4C7A80DF652294C3925B6489A7AB857C83476"));
+ }
+
+ return result;
+ }
+
+ public static void main(
+ String[] args)
+ {
+ PKCS12Test test = new PKCS12Test();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PKCS5Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PKCS5Test.java
new file mode 100644
index 000000000..66aef5ebe
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PKCS5Test.java
@@ -0,0 +1,265 @@
+package org.spongycastle.crypto.test;
+
+import java.io.ByteArrayInputStream;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.EncryptionScheme;
+import org.spongycastle.asn1.pkcs.KeyDerivationFunc;
+import org.spongycastle.asn1.pkcs.PBES2Parameters;
+import org.spongycastle.asn1.pkcs.PBKDF2Params;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.RC2CBCParameter;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.RC2Engine;
+import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * A test class for PKCS5 PBES2 with PBKDF2 (PKCS5 v2.0) using
+ * test vectors provider at
+ *
+ * RSA's PKCS5 Page
+ *
+ * The vectors are Base 64 encoded and encrypted using the password "password"
+ * (without quotes). They should all yield the same PrivateKeyInfo object.
+ */
+public class PKCS5Test
+ extends SimpleTest
+{
+ /**
+ * encrypted using des-cbc.
+ */
+ static byte[] sample1 = Base64.decode(
+ "MIIBozA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIfWBDXwLp4K4CAggA"
+ + "MBEGBSsOAwIHBAiaCF/AvOgQ6QSCAWDWX4BdAzCRNSQSANSuNsT5X8mWYO27mr3Y"
+ + "9c9LoBVXGNmYWKA77MI4967f7SmjNcgXj3xNE/jmnVz6hhsjS8E5VPT3kfyVkpdZ"
+ + "0lr5e9Yk2m3JWpPU7++v5zBkZmC4V/MwV/XuIs6U+vykgzMgpxQg0oZKS9zgmiZo"
+ + "f/4dOCL0UtCDnyOSvqT7mCVIcMDIEKu8QbVlgZYBop08l60EuEU3gARUo8WsYQmO"
+ + "Dz/ldx0Z+znIT0SXVuOwc+RVItC5T/Qx+aijmmpt+9l14nmaGBrEkmuhmtdvU/4v"
+ + "aptewGRgmjOfD6cqK+zs0O5NrrJ3P/6ZSxXj91CQgrThGfOv72bUncXEMNtc8pks"
+ + "2jpHFjGMdKufnadAD7XuMgzkkaklEXZ4f5tU6heIIwr51g0GBEGF96gYPFnjnSQM"
+ + "75JE02Clo+DfcfXpcybPTwwFg2jd6JTTOfkdf6OdSlA/1XNK43FA");
+
+ /**
+ * encrypted using des-ede3-cbc.
+ */
+ static byte[] sample2 = Base64.decode(
+ "MIIBpjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIeFeOWl1jywYCAggA"
+ + "MBQGCCqGSIb3DQMHBAjUJ5eGBhQGtQSCAWBrHrRgqO8UUMLcWzZEtpk1l3mjxiF/"
+ + "koCMkHsFwowgyWhEbgIkTgbSViK54LVK8PskekcGNLph+rB6bGZ7pPbL5pbXASJ8"
+ + "+MkQcG3FZdlS4Ek9tTJDApj3O1UubZGFG4uvTlJJFbF1BOJ3MkY3XQ9Gl1qwv7j5"
+ + "6e103Da7Cq9+oIDKmznza78XXQYrUsPo8mJGjUxPskEYlzwvHjKubRnYm/K6RKhi"
+ + "5f4zX4BQ/Dt3H812ZjRXrsjAJP0KrD/jyD/jCT7zNBVPH1izBds+RwizyQAHwfNJ"
+ + "BFR78TH4cgzB619X47FDVOnT0LqQNVd0O3cSwnPrXE9XR3tPayE+iOB15llFSmi8"
+ + "z0ByOXldEpkezCn92Umk++suzIVj1qfsK+bv2phZWJPbLEIWPDRHUbYf76q5ArAr"
+ + "u4xtxT/hoK3krEs/IN3d70qjlUJ36SEw1UaZ82PWhakQbdtu39ZraMJB");
+
+ /**
+ * encrypted using rc2-cbc.
+ */
+ static byte[] sample3 = Base64.decode(
+ "MIIBrjBIBgkqhkiG9w0BBQ0wOzAeBgkqhkiG9w0BBQwwEQQIrHyQPBZqWLUCAggA"
+ + "AgEQMBkGCCqGSIb3DQMCMA0CAToECEhbh7YZKiPSBIIBYCT1zp6o5jpFlIkgwPop"
+ + "7bW1+8ACr4exqzkeb3WflQ8cWJ4cURxzVdvxUnXeW1VJdaQZtjS/QHs5GhPTG/0f"
+ + "wtvnaPfwrIJ3FeGaZfcg2CrYhalOFmEb4xrE4KyoEQmUN8tb/Cg94uzd16BOPw21"
+ + "RDnE8bnPdIGY7TyL95kbkqH23mK53pi7h+xWIgduW+atIqDyyt55f7WMZcvDvlj6"
+ + "VpN/V0h+qxBHL274WA4dj6GYgeyUFpi60HdGCK7By2TBy8h1ZvKGjmB9h8jZvkx1"
+ + "MkbRumXxyFsowTZawyYvO8Um6lbfEDP9zIEUq0IV8RqH2MRyblsPNSikyYhxX/cz"
+ + "tdDxRKhilySbSBg5Kr8OfcwKp9bpinN96nmG4xr3Tch1bnVvqJzOQ5+Vva2WwVvH"
+ + "2JkWvYm5WaANg4Q6bRxu9vz7DuhbJjQdZbxFezIAgrJdSe92B00jO/0Kny1WjiVO"
+ + "6DA=");
+
+ static byte[] result = Hex.decode(
+ "30820155020100300d06092a864886f70d01010105000482013f3082013b020100024100"
+ + "debbfc2c09d61bada2a9462f24224e54cc6b3cc0755f15ce318ef57e79df17026b6a85cc"
+ + "a12428027245045df2052a329a2f9ad3d17b78a10572ad9b22bf343b020301000102402d"
+ + "90a96adcec472743527bc023153d8f0d6e96b40c8ed228276d467d843306429f8670559b"
+ + "f376dd41857f6397c2fc8d95e0e53ed62de420b855430ee4a1b8a1022100ffcaf0838239"
+ + "31e073ff534f06a5d415b3d414bc614a4544a3dff7ed271817eb022100deea30242117db"
+ + "2d3b8837f58f1da530ff83cf9283680da33683ec4e583610f1022100e6026381adb0a683"
+ + "f16a8f4c096b462979b9e4277cc89f3ed8a905b46fa9ff9f02210097c146d4d1d2b3dbaf"
+ + "53a504ff51674c5c271800de84d003f4f10ac6ab36e38102202bfa141f10bda874e1017d"
+ + "845e82767c1c38e82745daf421f0c8cd09d7652387");
+
+ private class PBETest
+ extends SimpleTest
+ {
+ int id;
+ BufferedBlockCipher cipher;
+ byte[] sample;
+ int keySize;
+
+ PBETest(
+ int id,
+ BufferedBlockCipher cipher,
+ byte[] sample,
+ int keySize)
+ {
+ this.id = id;
+ this.cipher = cipher;
+ this.sample = sample;
+ this.keySize = keySize;
+ }
+
+ public String getName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName() + " PKCS5S2 Test " + id;
+ }
+
+ public void performTest()
+ {
+ char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
+ PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
+ ByteArrayInputStream bIn = new ByteArrayInputStream(sample);
+ ASN1InputStream dIn = new ASN1InputStream(bIn);
+ EncryptedPrivateKeyInfo info = null;
+
+ try
+ {
+ info = EncryptedPrivateKeyInfo.getInstance(dIn.readObject());
+ }
+ catch (Exception e)
+ {
+ fail("failed construction - exception " + e.toString(), e);
+ }
+
+ PBES2Parameters alg = PBES2Parameters.getInstance(info.getEncryptionAlgorithm().getParameters());
+ PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
+ EncryptionScheme scheme = alg.getEncryptionScheme();
+
+ if (func.getKeyLength() != null)
+ {
+ keySize = func.getKeyLength().intValue() * 8;
+ }
+
+ int iterationCount = func.getIterationCount().intValue();
+ byte[] salt = func.getSalt();
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ iterationCount);
+
+ CipherParameters param;
+
+ if (scheme.getAlgorithm().equals(PKCSObjectIdentifiers.RC2_CBC))
+ {
+ RC2CBCParameter rc2Params = RC2CBCParameter.getInstance(scheme.getParameters());
+ byte[] iv = rc2Params.getIV();
+
+ param = new ParametersWithIV(generator.generateDerivedParameters(keySize), iv);
+ }
+ else
+ {
+ byte[] iv = ASN1OctetString.getInstance(scheme.getParameters()).getOctets();
+
+ param = new ParametersWithIV(generator.generateDerivedParameters(keySize), iv);
+ }
+
+ cipher.init(false, param);
+
+ byte[] data = info.getEncryptedData();
+ byte[] out = new byte[cipher.getOutputSize(data.length)];
+ int len = cipher.processBytes(data, 0, data.length, out, 0);
+
+ try
+ {
+ len += cipher.doFinal(out, len);
+ }
+ catch (Exception e)
+ {
+ fail("failed doFinal - exception " + e.toString());
+ }
+
+ if (result.length != len)
+ {
+ fail("failed length");
+ }
+
+ for (int i = 0; i != len; i++)
+ {
+ if (out[i] != result[i])
+ {
+ fail("failed comparison");
+ }
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "PKCS5S2";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESEngine()));
+ SimpleTest test = new PBETest(0, cipher, sample1, 64);
+
+ test.performTest();
+
+ cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
+ test = new PBETest(1, cipher, sample2, 192);
+
+ test.performTest();
+
+ cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RC2Engine()));
+ test = new PBETest(2, cipher, sample3, 0);
+ test.performTest();
+
+ //
+ // RFC 3211 tests
+ //
+ char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
+ PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
+
+ byte[] salt = Hex.decode("1234567878563412");
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ 5);
+
+ if (!areEqual(((KeyParameter)generator.generateDerivedParameters(64)).getKey(), Hex.decode("d1daa78615f287e6")))
+ {
+ fail("64 test failed");
+ }
+
+ password = "All n-entities must communicate with other n-entities via n-1 entiteeheehees".toCharArray();
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ 500);
+
+ if (!areEqual(((KeyParameter)generator.generateDerivedParameters(192)).getKey(), Hex.decode("6a8970bf68c92caea84a8df28510858607126380cc47ab2d")))
+ {
+ fail("192 test failed");
+ }
+
+ generator.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, 60000);
+ if (!areEqual(((KeyParameter)generator.generateDerivedParameters(192)).getKey(), Hex.decode("29aaef810c12ecd2236bbcfb55407f9852b5573dc1c095bb")))
+ {
+ fail("192 (60000) test failed");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PKCS5Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PSSBlindTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PSSBlindTest.java
new file mode 100644
index 000000000..36362bca7
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PSSBlindTest.java
@@ -0,0 +1,398 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.engines.RSABlindingEngine;
+import org.spongycastle.crypto.engines.RSAEngine;
+import org.spongycastle.crypto.generators.RSABlindingFactorGenerator;
+import org.spongycastle.crypto.generators.RSAKeyPairGenerator;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSABlindingParameters;
+import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.crypto.signers.PSSSigner;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/*
+ * RSA PSS test vectors for PKCS#1 V2.1 with blinding
+ */
+public class PSSBlindTest
+ extends SimpleTest
+{
+ private final int DATA_LENGTH = 1000;
+ private final int NUM_TESTS = 50;
+ private final int NUM_TESTS_WITH_KEY_GENERATION = 10;
+
+ private class FixedRandom
+ extends SecureRandom
+ {
+ byte[] vals;
+
+ FixedRandom(
+ byte[] vals)
+ {
+ this.vals = vals;
+ }
+
+ public void nextBytes(
+ byte[] bytes)
+ {
+ System.arraycopy(vals, 0, bytes, 0, vals.length);
+ }
+ }
+
+ //
+ // Example 1: A 1024-bit RSA keypair
+ //
+ private RSAKeyParameters pub1 = new RSAKeyParameters(false,
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16));
+
+ private RSAKeyParameters prv1 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16),
+ new BigInteger("33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325",16),
+ new BigInteger("e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443",16),
+ new BigInteger("b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd",16),
+ new BigInteger("28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979",16),
+ new BigInteger("1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729",16),
+ new BigInteger("27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d",16));
+
+ // PSSExample1.1
+
+ private byte[] msg1a = Hex.decode("cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0");
+
+ private byte[] slt1a = Hex.decode("dee959c7e06411361420ff80185ed57f3e6776af");
+
+ private byte[] sig1a = Hex.decode("9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c");
+
+ // PSSExample1.2
+
+ private byte[] msg1b = Hex.decode("851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e");
+
+ private byte[] slt1b = Hex.decode("ef2869fa40c346cb183dab3d7bffc98fd56df42d");
+
+ private byte[] sig1b = Hex.decode("3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843");
+
+ //
+ // Example 2: A 1025-bit RSA keypair
+ //
+
+ private RSAKeyParameters pub2 = new RSAKeyParameters(false,
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv2 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("027d147e4673057377fd1ea201565772176a7dc38358d376045685a2e787c23c15576bc16b9f444402d6bfc5d98a3e88ea13ef67c353eca0c0ddba9255bd7b8bb50a644afdfd1dd51695b252d22e7318d1b6687a1c10ff75545f3db0fe602d5f2b7f294e3601eab7b9d1cecd767f64692e3e536ca2846cb0c2dd486a39fa75b1", 16),
+ new BigInteger("016601e926a0f8c9e26ecab769ea65a5e7c52cc9e080ef519457c644da6891c5a104d3ea7955929a22e7c68a7af9fcad777c3ccc2b9e3d3650bce404399b7e59d1", 16),
+ new BigInteger("014eafa1d4d0184da7e31f877d1281ddda625664869e8379e67ad3b75eae74a580e9827abd6eb7a002cb5411f5266797768fb8e95ae40e3e8a01f35ff89e56c079", 16),
+ new BigInteger("e247cce504939b8f0a36090de200938755e2444b29539a7da7a902f6056835c0db7b52559497cfe2c61a8086d0213c472c78851800b171f6401de2e9c2756f31", 16),
+ new BigInteger("b12fba757855e586e46f64c38a70c68b3f548d93d787b399999d4c8f0bbd2581c21e19ed0018a6d5d3df86424b3abcad40199d31495b61309f27c1bf55d487c1", 16),
+ new BigInteger("564b1e1fa003bda91e89090425aac05b91da9ee25061e7628d5f51304a84992fdc33762bd378a59f030a334d532bd0dae8f298ea9ed844636ad5fb8cbdc03cad", 16));
+
+ // PSS Example 2.1
+
+ private byte[] msg2a = Hex.decode("daba032066263faedb659848115278a52c44faa3a76f37515ed336321072c40a9d9b53bc05014078adf520875146aae70ff060226dcb7b1f1fc27e9360");
+ private byte[] slt2a = Hex.decode("57bf160bcb02bb1dc7280cf0458530b7d2832ff7");
+ private byte[] sig2a = Hex.decode("014c5ba5338328ccc6e7a90bf1c0ab3fd606ff4796d3c12e4b639ed9136a5fec6c16d8884bdd99cfdc521456b0742b736868cf90de099adb8d5ffd1deff39ba4007ab746cefdb22d7df0e225f54627dc65466131721b90af445363a8358b9f607642f78fab0ab0f43b7168d64bae70d8827848d8ef1e421c5754ddf42c2589b5b3");
+
+ // PSS Example 2.2
+
+ private byte[] msg2b = Hex.decode("e4f8601a8a6da1be34447c0959c058570c3668cfd51dd5f9ccd6ad4411fe8213486d78a6c49f93efc2ca2288cebc2b9b60bd04b1e220d86e3d4848d709d032d1e8c6a070c6af9a499fcf95354b14ba6127c739de1bb0fd16431e46938aec0cf8ad9eb72e832a7035de9b7807bdc0ed8b68eb0f5ac2216be40ce920c0db0eddd3860ed788efaccaca502d8f2bd6d1a7c1f41ff46f1681c8f1f818e9c4f6d91a0c7803ccc63d76a6544d843e084e363b8acc55aa531733edb5dee5b5196e9f03e8b731b3776428d9e457fe3fbcb3db7274442d785890e9cb0854b6444dace791d7273de1889719338a77fe");
+ private byte[] slt2b = Hex.decode("7f6dd359e604e60870e898e47b19bf2e5a7b2a90");
+ private byte[] sig2b = Hex.decode("010991656cca182b7f29d2dbc007e7ae0fec158eb6759cb9c45c5ff87c7635dd46d150882f4de1e9ae65e7f7d9018f6836954a47c0a81a8a6b6f83f2944d6081b1aa7c759b254b2c34b691da67cc0226e20b2f18b42212761dcd4b908a62b371b5918c5742af4b537e296917674fb914194761621cc19a41f6fb953fbcbb649dea");
+
+ //
+ // Example 4: A 1027-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub4 = new RSAKeyParameters(false,
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv4 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("fa041f8cd9697ceed38ec8caa275523b4dd72b09a301d3541d72f5d31c05cbce2d6983b36183af10690bd46c46131e35789431a556771dd0049b57461bf060c1f68472e8a67c25f357e5b6b4738fa541a730346b4a07649a2dfa806a69c975b6aba64678acc7f5913e89c622f2d8abb1e3e32554e39df94ba60c002e387d9011", 16),
+ new BigInteger("029232336d2838945dba9dd7723f4e624a05f7375b927a87abe6a893a1658fd49f47f6c7b0fa596c65fa68a23f0ab432962d18d4343bd6fd671a5ea8d148413995", 16),
+ new BigInteger("020ef5efe7c5394aed2272f7e81a74f4c02d145894cb1b3cab23a9a0710a2afc7e3329acbb743d01f680c4d02afb4c8fde7e20930811bb2b995788b5e872c20bb1", 16),
+ new BigInteger("026e7e28010ecf2412d9523ad704647fb4fe9b66b1a681581b0e15553a89b1542828898f27243ebab45ff5e1acb9d4df1b051fbc62824dbc6f6c93261a78b9a759", 16),
+ new BigInteger("012ddcc86ef655998c39ddae11718669e5e46cf1495b07e13b1014cd69b3af68304ad2a6b64321e78bf3bbca9bb494e91d451717e2d97564c6549465d0205cf421", 16),
+ new BigInteger("010600c4c21847459fe576703e2ebecae8a5094ee63f536bf4ac68d3c13e5e4f12ac5cc10ab6a2d05a199214d1824747d551909636b774c22cac0b837599abcc75", 16));
+
+ // PSS Example 4.1
+
+ private byte[] msg4a = Hex.decode("9fb03b827c8217d9");
+
+ private byte[] slt4a = Hex.decode("ed7c98c95f30974fbe4fbddcf0f28d6021c0e91d");
+
+ private byte[] sig4a = Hex.decode("0323d5b7bf20ba4539289ae452ae4297080feff4518423ff4811a817837e7d82f1836cdfab54514ff0887bddeebf40bf99b047abc3ecfa6a37a3ef00f4a0c4a88aae0904b745c846c4107e8797723e8ac810d9e3d95dfa30ff4966f4d75d13768d20857f2b1406f264cfe75e27d7652f4b5ed3575f28a702f8c4ed9cf9b2d44948");
+
+ // PSS Example 4.2
+
+ private byte[] msg4b = Hex.decode("0ca2ad77797ece86de5bf768750ddb5ed6a3116ad99bbd17edf7f782f0db1cd05b0f677468c5ea420dc116b10e80d110de2b0461ea14a38be68620392e7e893cb4ea9393fb886c20ff790642305bf302003892e54df9f667509dc53920df583f50a3dd61abb6fab75d600377e383e6aca6710eeea27156e06752c94ce25ae99fcbf8592dbe2d7e27453cb44de07100ebb1a2a19811a478adbeab270f94e8fe369d90b3ca612f9f");
+
+ private byte[] slt4b = Hex.decode("22d71d54363a4217aa55113f059b3384e3e57e44");
+
+ private byte[] sig4b = Hex.decode("049d0185845a264d28feb1e69edaec090609e8e46d93abb38371ce51f4aa65a599bdaaa81d24fba66a08a116cb644f3f1e653d95c89db8bbd5daac2709c8984000178410a7c6aa8667ddc38c741f710ec8665aa9052be929d4e3b16782c1662114c5414bb0353455c392fc28f3db59054b5f365c49e1d156f876ee10cb4fd70598");
+
+
+ //
+ // Example 8: A 1031-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub8 = new RSAKeyParameters(false,
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv8 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6c66ffe98980c38fcdeab5159898836165f4b4b817c4f6a8d486ee4ea9130fe9b9092bd136d184f95f504a607eac565846d2fdd6597a8967c7396ef95a6eeebb4578a643966dca4d8ee3de842de63279c618159c1ab54a89437b6a6120e4930afb52a4ba6ced8a4947ac64b30a3497cbe701c2d6266d517219ad0ec6d347dbe9", 16),
+ new BigInteger("08dad7f11363faa623d5d6d5e8a319328d82190d7127d2846c439b0ab72619b0a43a95320e4ec34fc3a9cea876422305bd76c5ba7be9e2f410c8060645a1d29edb", 16),
+ new BigInteger("0847e732376fc7900f898ea82eb2b0fc418565fdae62f7d9ec4ce2217b97990dd272db157f99f63c0dcbb9fbacdbd4c4dadb6df67756358ca4174825b48f49706d", 16),
+ new BigInteger("05c2a83c124b3621a2aa57ea2c3efe035eff4560f33ddebb7adab81fce69a0c8c2edc16520dda83d59a23be867963ac65f2cc710bbcfb96ee103deb771d105fd85", 16),
+ new BigInteger("04cae8aa0d9faa165c87b682ec140b8ed3b50b24594b7a3b2c220b3669bb819f984f55310a1ae7823651d4a02e99447972595139363434e5e30a7e7d241551e1b9", 16),
+ new BigInteger("07d3e47bf686600b11ac283ce88dbb3f6051e8efd04680e44c171ef531b80b2b7c39fc766320e2cf15d8d99820e96ff30dc69691839c4b40d7b06e45307dc91f3f", 16));
+
+ // PSS Example 8.1
+
+ private byte[] msg8a = Hex.decode("81332f4be62948415ea1d899792eeacf6c6e1db1da8be13b5cea41db2fed467092e1ff398914c714259775f595f8547f735692a575e6923af78f22c6997ddb90fb6f72d7bb0dd5744a31decd3dc3685849836ed34aec596304ad11843c4f88489f209735f5fb7fdaf7cec8addc5818168f880acbf490d51005b7a8e84e43e54287977571dd99eea4b161eb2df1f5108f12a4142a83322edb05a75487a3435c9a78ce53ed93bc550857d7a9fb");
+
+ private byte[] slt8a = Hex.decode("1d65491d79c864b373009be6f6f2467bac4c78fa");
+
+ private byte[] sig8a = Hex.decode("0262ac254bfa77f3c1aca22c5179f8f040422b3c5bafd40a8f21cf0fa5a667ccd5993d42dbafb409c520e25fce2b1ee1e716577f1efa17f3da28052f40f0419b23106d7845aaf01125b698e7a4dfe92d3967bb00c4d0d35ba3552ab9a8b3eef07c7fecdbc5424ac4db1e20cb37d0b2744769940ea907e17fbbca673b20522380c5");
+
+ // PSS Example 8.2
+
+ private byte[] msg8b = Hex.decode("e2f96eaf0e05e7ba326ecca0ba7fd2f7c02356f3cede9d0faabf4fcc8e60a973e5595fd9ea08");
+
+ private byte[] slt8b = Hex.decode("435c098aa9909eb2377f1248b091b68987ff1838");
+
+ private byte[] sig8b = Hex.decode("2707b9ad5115c58c94e932e8ec0a280f56339e44a1b58d4ddcff2f312e5f34dcfe39e89c6a94dcee86dbbdae5b79ba4e0819a9e7bfd9d982e7ee6c86ee68396e8b3a14c9c8f34b178eb741f9d3f121109bf5c8172fada2e768f9ea1433032c004a8aa07eb990000a48dc94c8bac8aabe2b09b1aa46c0a2aa0e12f63fbba775ba7e");
+
+ //
+ // Example 9: A 1536-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub9 = new RSAKeyParameters(false,
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv9 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6a7fd84fb85fad073b34406db74f8d61a6abc12196a961dd79565e9da6e5187bce2d980250f7359575359270d91590bb0e427c71460b55d51410b191bcf309fea131a92c8e702738fa719f1e0041f52e40e91f229f4d96a1e6f172e15596b4510a6daec26105f2bebc53316b87bdf21311666070e8dfee69d52c71a976caae79c72b68d28580dc686d9f5129d225f82b3d615513a882b3db91416b48ce08888213e37eeb9af800d81cab328ce420689903c00c7b5fd31b75503a6d419684d629", 16),
+ new BigInteger("f8eb97e98df12664eefdb761596a69ddcd0e76daece6ed4bf5a1b50ac086f7928a4d2f8726a77e515b74da41988f220b1cc87aa1fc810ce99a82f2d1ce821edced794c6941f42c7a1a0b8c4d28c75ec60b652279f6154a762aed165d47dee367", 16),
+ new BigInteger("ed4d71d0a6e24b93c2e5f6b4bbe05f5fb0afa042d204fe3378d365c2f288b6a8dad7efe45d153eef40cacc7b81ff934002d108994b94a5e4728cd9c963375ae49965bda55cbf0efed8d6553b4027f2d86208a6e6b489c176128092d629e49d3d", 16),
+ new BigInteger("2bb68bddfb0c4f56c8558bffaf892d8043037841e7fa81cfa61a38c5e39b901c8ee71122a5da2227bd6cdeeb481452c12ad3d61d5e4f776a0ab556591befe3e59e5a7fddb8345e1f2f35b9f4cee57c32414c086aec993e9353e480d9eec6289f", 16),
+ new BigInteger("4ff897709fad079746494578e70fd8546130eeab5627c49b080f05ee4ad9f3e4b7cba9d6a5dff113a41c3409336833f190816d8a6bc42e9bec56b7567d0f3c9c696db619b245d901dd856db7c8092e77e9a1cccd56ee4dba42c5fdb61aec2669", 16),
+ new BigInteger("77b9d1137b50404a982729316efafc7dfe66d34e5a182600d5f30a0a8512051c560d081d4d0a1835ec3d25a60f4e4d6aa948b2bf3dbb5b124cbbc3489255a3a948372f6978496745f943e1db4f18382ceaa505dfc65757bb3f857a58dce52156", 16));
+
+ // PSS Example 9.1
+
+ private byte[] msg9a = Hex.decode("a88e265855e9d7ca36c68795f0b31b591cd6587c71d060a0b3f7f3eaef43795922028bc2b6ad467cfc2d7f659c5385aa70ba3672cdde4cfe4970cc7904601b278872bf51321c4a972f3c95570f3445d4f57980e0f20df54846e6a52c668f1288c03f95006ea32f562d40d52af9feb32f0fa06db65b588a237b34e592d55cf979f903a642ef64d2ed542aa8c77dc1dd762f45a59303ed75e541ca271e2b60ca709e44fa0661131e8d5d4163fd8d398566ce26de8730e72f9cca737641c244159420637028df0a18079d6208ea8b4711a2c750f5");
+
+ private byte[] slt9a = Hex.decode("c0a425313df8d7564bd2434d311523d5257eed80");
+
+ private byte[] sig9a = Hex.decode("586107226c3ce013a7c8f04d1a6a2959bb4b8e205ba43a27b50f124111bc35ef589b039f5932187cb696d7d9a32c0c38300a5cdda4834b62d2eb240af33f79d13dfbf095bf599e0d9686948c1964747b67e89c9aba5cd85016236f566cc5802cb13ead51bc7ca6bef3b94dcbdbb1d570469771df0e00b1a8a06777472d2316279edae86474668d4e1efff95f1de61c6020da32ae92bbf16520fef3cf4d88f61121f24bbd9fe91b59caf1235b2a93ff81fc403addf4ebdea84934a9cdaf8e1a9e");
+
+ // PSS Example 9.2
+
+ private byte[] msg9b = Hex.decode("c8c9c6af04acda414d227ef23e0820c3732c500dc87275e95b0d095413993c2658bc1d988581ba879c2d201f14cb88ced153a01969a7bf0a7be79c84c1486bc12b3fa6c59871b6827c8ce253ca5fefa8a8c690bf326e8e37cdb96d90a82ebab69f86350e1822e8bd536a2e");
+
+ private byte[] slt9b = Hex.decode("b307c43b4850a8dac2f15f32e37839ef8c5c0e91");
+
+ private byte[] sig9b = Hex.decode("80b6d643255209f0a456763897ac9ed259d459b49c2887e5882ecb4434cfd66dd7e1699375381e51cd7f554f2c271704b399d42b4be2540a0eca61951f55267f7c2878c122842dadb28b01bd5f8c025f7e228418a673c03d6bc0c736d0a29546bd67f786d9d692ccea778d71d98c2063b7a71092187a4d35af108111d83e83eae46c46aa34277e06044589903788f1d5e7cee25fb485e92949118814d6f2c3ee361489016f327fb5bc517eb50470bffa1afa5f4ce9aa0ce5b8ee19bf5501b958");
+
+
+ public String getName()
+ {
+ return "PSSBlindTest";
+ }
+
+ private void testSig(
+ int id,
+ RSAKeyParameters pub,
+ RSAKeyParameters prv,
+ byte[] slt,
+ byte[] msg,
+ byte[] sig)
+ throws Exception
+ {
+ RSABlindingFactorGenerator blindFactorGen = new RSABlindingFactorGenerator();
+ RSABlindingEngine blindingEngine = new RSABlindingEngine();
+ PSSSigner blindSigner = new PSSSigner(blindingEngine, new SHA1Digest(), 20);
+ PSSSigner signer = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ blindFactorGen.init(pub);
+
+ BigInteger blindFactor = blindFactorGen.generateBlindingFactor();
+ RSABlindingParameters params = new RSABlindingParameters(pub, blindFactor);
+
+ // generate a blind signature
+ blindSigner.init(true, new ParametersWithRandom(params, new FixedRandom(slt)));
+
+ blindSigner.update(msg, 0, msg.length);
+
+ byte[] blindedData = blindSigner.generateSignature();
+
+ RSAEngine signerEngine = new RSAEngine();
+
+ signerEngine.init(true, prv);
+
+ byte[] blindedSig = signerEngine.processBlock(blindedData, 0, blindedData.length);
+
+ // unblind the signature
+ blindingEngine.init(false, params);
+
+ byte[] s = blindingEngine.processBlock(blindedSig, 0, blindedSig.length);
+
+ //signature verification
+ if (!areEqual(s, sig))
+ {
+ fail("test " + id + " failed generation");
+ }
+
+ //verify signature with PSSSigner
+ signer.init(false, pub);
+ signer.update(msg, 0, msg.length);
+
+ if (!signer.verifySignature(s))
+ {
+ fail("test " + id + " failed PSSSigner verification");
+ }
+ }
+
+ private boolean isProcessingOkay(
+ RSAKeyParameters pub,
+ RSAKeyParameters prv,
+ byte[] data,
+ SecureRandom random)
+ throws Exception
+ {
+ RSABlindingFactorGenerator blindFactorGen = new RSABlindingFactorGenerator();
+ RSABlindingEngine blindingEngine = new RSABlindingEngine();
+ PSSSigner blindSigner = new PSSSigner(blindingEngine, new SHA1Digest(), 20);
+ PSSSigner pssEng = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ random.nextBytes(data);
+
+ blindFactorGen.init(pub);
+
+ BigInteger blindFactor = blindFactorGen.generateBlindingFactor();
+ RSABlindingParameters params = new RSABlindingParameters(pub, blindFactor);
+
+ // generate a blind signature
+ blindSigner.init(true, new ParametersWithRandom(params, random));
+
+ blindSigner.update(data, 0, data.length);
+
+ byte[] blindedData = blindSigner.generateSignature();
+
+ RSAEngine signerEngine = new RSAEngine();
+
+ signerEngine.init(true, prv);
+
+ byte[] blindedSig = signerEngine.processBlock(blindedData, 0, blindedData.length);
+
+ // unblind the signature
+ blindingEngine.init(false, params);
+
+ byte[] s = blindingEngine.processBlock(blindedSig, 0, blindedSig.length);
+
+ //verify signature with PSSSigner
+ pssEng.init(false, pub);
+ pssEng.update(data, 0, data.length);
+
+ return pssEng.verifySignature(s);
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testSig(1, pub1, prv1, slt1a, msg1a, sig1a);
+ testSig(2, pub1, prv1, slt1b, msg1b, sig1b);
+ testSig(3, pub2, prv2, slt2a, msg2a, sig2a);
+ testSig(4, pub2, prv2, slt2b, msg2b, sig2b);
+ testSig(5, pub4, prv4, slt4a, msg4a, sig4a);
+ testSig(6, pub4, prv4, slt4b, msg4b, sig4b);
+ testSig(7, pub8, prv8, slt8a, msg8a, sig8a);
+ testSig(8, pub8, prv8, slt8b, msg8b, sig8b);
+ testSig(9, pub9, prv9, slt9a, msg9a, sig9a);
+ testSig(10, pub9, prv9, slt9b, msg9b, sig9b);
+
+ //
+ // loop test
+ //
+ int failed = 0;
+ byte[] data = new byte[DATA_LENGTH];
+
+ SecureRandom random = new SecureRandom();
+
+
+ RSAKeyParameters[] kprv ={prv1, prv2, prv4, prv8, prv9};
+ RSAKeyParameters[] kpub ={pub1, pub2, pub4, pub8, pub9};
+
+ int i = 0;
+ for (int j = 0; j < NUM_TESTS; j++, i++)
+ {
+ if (i == kprv.length)
+ {
+ i = 0;
+ }
+
+ if (!isProcessingOkay(kpub[i], kprv[i], data, random))
+ {
+ failed++;
+ }
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test failed - failures: " + failed);
+ }
+
+ //
+ // key generation test
+ //
+ RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+ RSAKeyGenerationParameters genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25);
+
+ pGen.init(genParam);
+ failed = 0;
+
+ for (int k = 0; k < NUM_TESTS_WITH_KEY_GENERATION; k++)
+ {
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ for (int j = 0; j < NUM_TESTS; j++)
+ {
+ if (!isProcessingOkay((RSAKeyParameters)pair.getPublic(), (RSAKeyParameters)pair.getPrivate(), data, random))
+ {
+ failed++;
+ }
+ }
+
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test with key generation failed - failures: " + failed);
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PSSBlindTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PSSTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PSSTest.java
new file mode 100644
index 000000000..517433a61
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PSSTest.java
@@ -0,0 +1,332 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.engines.RSAEngine;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.crypto.signers.PSSSigner;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/*
+ * RSA PSS test vectors for PKCS#1 V2.1
+ */
+public class PSSTest
+ extends SimpleTest
+{
+ private final int DATA_LENGTH = 1000;
+ private final int NUM_TESTS = 500;
+
+ private class FixedRandom
+ extends SecureRandom
+ {
+ byte[] vals;
+
+ FixedRandom(
+ byte[] vals)
+ {
+ this.vals = vals;
+ }
+
+ public void nextBytes(
+ byte[] bytes)
+ {
+ System.arraycopy(vals, 0, bytes, 0, vals.length);
+ }
+ }
+
+ //
+ // Example 1: A 1024-bit RSA keypair
+ //
+ private RSAKeyParameters pub1 = new RSAKeyParameters(false,
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16));
+
+ private RSAKeyParameters prv1 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16),
+ new BigInteger("33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325",16),
+ new BigInteger("e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443",16),
+ new BigInteger("b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd",16),
+ new BigInteger("28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979",16),
+ new BigInteger("1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729",16),
+ new BigInteger("27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d",16));
+
+ // PSSExample1.1
+
+ private byte[] msg1a = Hex.decode("cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0");
+
+ private byte[] slt1a = Hex.decode("dee959c7e06411361420ff80185ed57f3e6776af");
+
+ private byte[] sig1a = Hex.decode("9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c");
+
+ // PSSExample1.2
+
+ private byte[] msg1b = Hex.decode("851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e");
+
+ private byte[] slt1b = Hex.decode("ef2869fa40c346cb183dab3d7bffc98fd56df42d");
+
+ private byte[] sig1b = Hex.decode("3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843");
+
+ //
+ // Example 2: A 1025-bit RSA keypair
+ //
+
+ private RSAKeyParameters pub2 = new RSAKeyParameters(false,
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv2 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("027d147e4673057377fd1ea201565772176a7dc38358d376045685a2e787c23c15576bc16b9f444402d6bfc5d98a3e88ea13ef67c353eca0c0ddba9255bd7b8bb50a644afdfd1dd51695b252d22e7318d1b6687a1c10ff75545f3db0fe602d5f2b7f294e3601eab7b9d1cecd767f64692e3e536ca2846cb0c2dd486a39fa75b1", 16),
+ new BigInteger("016601e926a0f8c9e26ecab769ea65a5e7c52cc9e080ef519457c644da6891c5a104d3ea7955929a22e7c68a7af9fcad777c3ccc2b9e3d3650bce404399b7e59d1", 16),
+ new BigInteger("014eafa1d4d0184da7e31f877d1281ddda625664869e8379e67ad3b75eae74a580e9827abd6eb7a002cb5411f5266797768fb8e95ae40e3e8a01f35ff89e56c079", 16),
+ new BigInteger("e247cce504939b8f0a36090de200938755e2444b29539a7da7a902f6056835c0db7b52559497cfe2c61a8086d0213c472c78851800b171f6401de2e9c2756f31", 16),
+ new BigInteger("b12fba757855e586e46f64c38a70c68b3f548d93d787b399999d4c8f0bbd2581c21e19ed0018a6d5d3df86424b3abcad40199d31495b61309f27c1bf55d487c1", 16),
+ new BigInteger("564b1e1fa003bda91e89090425aac05b91da9ee25061e7628d5f51304a84992fdc33762bd378a59f030a334d532bd0dae8f298ea9ed844636ad5fb8cbdc03cad", 16));
+
+ // PSS Example 2.1
+
+ private byte[] msg2a = Hex.decode("daba032066263faedb659848115278a52c44faa3a76f37515ed336321072c40a9d9b53bc05014078adf520875146aae70ff060226dcb7b1f1fc27e9360");
+ private byte[] slt2a = Hex.decode("57bf160bcb02bb1dc7280cf0458530b7d2832ff7");
+ private byte[] sig2a = Hex.decode("014c5ba5338328ccc6e7a90bf1c0ab3fd606ff4796d3c12e4b639ed9136a5fec6c16d8884bdd99cfdc521456b0742b736868cf90de099adb8d5ffd1deff39ba4007ab746cefdb22d7df0e225f54627dc65466131721b90af445363a8358b9f607642f78fab0ab0f43b7168d64bae70d8827848d8ef1e421c5754ddf42c2589b5b3");
+
+ // PSS Example 2.2
+
+ private byte[] msg2b = Hex.decode("e4f8601a8a6da1be34447c0959c058570c3668cfd51dd5f9ccd6ad4411fe8213486d78a6c49f93efc2ca2288cebc2b9b60bd04b1e220d86e3d4848d709d032d1e8c6a070c6af9a499fcf95354b14ba6127c739de1bb0fd16431e46938aec0cf8ad9eb72e832a7035de9b7807bdc0ed8b68eb0f5ac2216be40ce920c0db0eddd3860ed788efaccaca502d8f2bd6d1a7c1f41ff46f1681c8f1f818e9c4f6d91a0c7803ccc63d76a6544d843e084e363b8acc55aa531733edb5dee5b5196e9f03e8b731b3776428d9e457fe3fbcb3db7274442d785890e9cb0854b6444dace791d7273de1889719338a77fe");
+ private byte[] slt2b = Hex.decode("7f6dd359e604e60870e898e47b19bf2e5a7b2a90");
+ private byte[] sig2b = Hex.decode("010991656cca182b7f29d2dbc007e7ae0fec158eb6759cb9c45c5ff87c7635dd46d150882f4de1e9ae65e7f7d9018f6836954a47c0a81a8a6b6f83f2944d6081b1aa7c759b254b2c34b691da67cc0226e20b2f18b42212761dcd4b908a62b371b5918c5742af4b537e296917674fb914194761621cc19a41f6fb953fbcbb649dea");
+
+ //
+ // Example 4: A 1027-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub4 = new RSAKeyParameters(false,
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv4 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("fa041f8cd9697ceed38ec8caa275523b4dd72b09a301d3541d72f5d31c05cbce2d6983b36183af10690bd46c46131e35789431a556771dd0049b57461bf060c1f68472e8a67c25f357e5b6b4738fa541a730346b4a07649a2dfa806a69c975b6aba64678acc7f5913e89c622f2d8abb1e3e32554e39df94ba60c002e387d9011", 16),
+ new BigInteger("029232336d2838945dba9dd7723f4e624a05f7375b927a87abe6a893a1658fd49f47f6c7b0fa596c65fa68a23f0ab432962d18d4343bd6fd671a5ea8d148413995", 16),
+ new BigInteger("020ef5efe7c5394aed2272f7e81a74f4c02d145894cb1b3cab23a9a0710a2afc7e3329acbb743d01f680c4d02afb4c8fde7e20930811bb2b995788b5e872c20bb1", 16),
+ new BigInteger("026e7e28010ecf2412d9523ad704647fb4fe9b66b1a681581b0e15553a89b1542828898f27243ebab45ff5e1acb9d4df1b051fbc62824dbc6f6c93261a78b9a759", 16),
+ new BigInteger("012ddcc86ef655998c39ddae11718669e5e46cf1495b07e13b1014cd69b3af68304ad2a6b64321e78bf3bbca9bb494e91d451717e2d97564c6549465d0205cf421", 16),
+ new BigInteger("010600c4c21847459fe576703e2ebecae8a5094ee63f536bf4ac68d3c13e5e4f12ac5cc10ab6a2d05a199214d1824747d551909636b774c22cac0b837599abcc75", 16));
+
+ // PSS Example 4.1
+
+ private byte[] msg4a = Hex.decode("9fb03b827c8217d9");
+
+ private byte[] slt4a = Hex.decode("ed7c98c95f30974fbe4fbddcf0f28d6021c0e91d");
+
+ private byte[] sig4a = Hex.decode("0323d5b7bf20ba4539289ae452ae4297080feff4518423ff4811a817837e7d82f1836cdfab54514ff0887bddeebf40bf99b047abc3ecfa6a37a3ef00f4a0c4a88aae0904b745c846c4107e8797723e8ac810d9e3d95dfa30ff4966f4d75d13768d20857f2b1406f264cfe75e27d7652f4b5ed3575f28a702f8c4ed9cf9b2d44948");
+
+ // PSS Example 4.2
+
+ private byte[] msg4b = Hex.decode("0ca2ad77797ece86de5bf768750ddb5ed6a3116ad99bbd17edf7f782f0db1cd05b0f677468c5ea420dc116b10e80d110de2b0461ea14a38be68620392e7e893cb4ea9393fb886c20ff790642305bf302003892e54df9f667509dc53920df583f50a3dd61abb6fab75d600377e383e6aca6710eeea27156e06752c94ce25ae99fcbf8592dbe2d7e27453cb44de07100ebb1a2a19811a478adbeab270f94e8fe369d90b3ca612f9f");
+
+ private byte[] slt4b = Hex.decode("22d71d54363a4217aa55113f059b3384e3e57e44");
+
+ private byte[] sig4b = Hex.decode("049d0185845a264d28feb1e69edaec090609e8e46d93abb38371ce51f4aa65a599bdaaa81d24fba66a08a116cb644f3f1e653d95c89db8bbd5daac2709c8984000178410a7c6aa8667ddc38c741f710ec8665aa9052be929d4e3b16782c1662114c5414bb0353455c392fc28f3db59054b5f365c49e1d156f876ee10cb4fd70598");
+
+
+ //
+ // Example 8: A 1031-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub8 = new RSAKeyParameters(false,
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv8 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6c66ffe98980c38fcdeab5159898836165f4b4b817c4f6a8d486ee4ea9130fe9b9092bd136d184f95f504a607eac565846d2fdd6597a8967c7396ef95a6eeebb4578a643966dca4d8ee3de842de63279c618159c1ab54a89437b6a6120e4930afb52a4ba6ced8a4947ac64b30a3497cbe701c2d6266d517219ad0ec6d347dbe9", 16),
+ new BigInteger("08dad7f11363faa623d5d6d5e8a319328d82190d7127d2846c439b0ab72619b0a43a95320e4ec34fc3a9cea876422305bd76c5ba7be9e2f410c8060645a1d29edb", 16),
+ new BigInteger("0847e732376fc7900f898ea82eb2b0fc418565fdae62f7d9ec4ce2217b97990dd272db157f99f63c0dcbb9fbacdbd4c4dadb6df67756358ca4174825b48f49706d", 16),
+ new BigInteger("05c2a83c124b3621a2aa57ea2c3efe035eff4560f33ddebb7adab81fce69a0c8c2edc16520dda83d59a23be867963ac65f2cc710bbcfb96ee103deb771d105fd85", 16),
+ new BigInteger("04cae8aa0d9faa165c87b682ec140b8ed3b50b24594b7a3b2c220b3669bb819f984f55310a1ae7823651d4a02e99447972595139363434e5e30a7e7d241551e1b9", 16),
+ new BigInteger("07d3e47bf686600b11ac283ce88dbb3f6051e8efd04680e44c171ef531b80b2b7c39fc766320e2cf15d8d99820e96ff30dc69691839c4b40d7b06e45307dc91f3f", 16));
+
+ // PSS Example 8.1
+
+ private byte[] msg8a = Hex.decode("81332f4be62948415ea1d899792eeacf6c6e1db1da8be13b5cea41db2fed467092e1ff398914c714259775f595f8547f735692a575e6923af78f22c6997ddb90fb6f72d7bb0dd5744a31decd3dc3685849836ed34aec596304ad11843c4f88489f209735f5fb7fdaf7cec8addc5818168f880acbf490d51005b7a8e84e43e54287977571dd99eea4b161eb2df1f5108f12a4142a83322edb05a75487a3435c9a78ce53ed93bc550857d7a9fb");
+
+ private byte[] slt8a = Hex.decode("1d65491d79c864b373009be6f6f2467bac4c78fa");
+
+ private byte[] sig8a = Hex.decode("0262ac254bfa77f3c1aca22c5179f8f040422b3c5bafd40a8f21cf0fa5a667ccd5993d42dbafb409c520e25fce2b1ee1e716577f1efa17f3da28052f40f0419b23106d7845aaf01125b698e7a4dfe92d3967bb00c4d0d35ba3552ab9a8b3eef07c7fecdbc5424ac4db1e20cb37d0b2744769940ea907e17fbbca673b20522380c5");
+
+ // PSS Example 8.2
+
+ private byte[] msg8b = Hex.decode("e2f96eaf0e05e7ba326ecca0ba7fd2f7c02356f3cede9d0faabf4fcc8e60a973e5595fd9ea08");
+
+ private byte[] slt8b = Hex.decode("435c098aa9909eb2377f1248b091b68987ff1838");
+
+ private byte[] sig8b = Hex.decode("2707b9ad5115c58c94e932e8ec0a280f56339e44a1b58d4ddcff2f312e5f34dcfe39e89c6a94dcee86dbbdae5b79ba4e0819a9e7bfd9d982e7ee6c86ee68396e8b3a14c9c8f34b178eb741f9d3f121109bf5c8172fada2e768f9ea1433032c004a8aa07eb990000a48dc94c8bac8aabe2b09b1aa46c0a2aa0e12f63fbba775ba7e");
+
+ //
+ // Example 9: A 1536-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub9 = new RSAKeyParameters(false,
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv9 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6a7fd84fb85fad073b34406db74f8d61a6abc12196a961dd79565e9da6e5187bce2d980250f7359575359270d91590bb0e427c71460b55d51410b191bcf309fea131a92c8e702738fa719f1e0041f52e40e91f229f4d96a1e6f172e15596b4510a6daec26105f2bebc53316b87bdf21311666070e8dfee69d52c71a976caae79c72b68d28580dc686d9f5129d225f82b3d615513a882b3db91416b48ce08888213e37eeb9af800d81cab328ce420689903c00c7b5fd31b75503a6d419684d629", 16),
+ new BigInteger("f8eb97e98df12664eefdb761596a69ddcd0e76daece6ed4bf5a1b50ac086f7928a4d2f8726a77e515b74da41988f220b1cc87aa1fc810ce99a82f2d1ce821edced794c6941f42c7a1a0b8c4d28c75ec60b652279f6154a762aed165d47dee367", 16),
+ new BigInteger("ed4d71d0a6e24b93c2e5f6b4bbe05f5fb0afa042d204fe3378d365c2f288b6a8dad7efe45d153eef40cacc7b81ff934002d108994b94a5e4728cd9c963375ae49965bda55cbf0efed8d6553b4027f2d86208a6e6b489c176128092d629e49d3d", 16),
+ new BigInteger("2bb68bddfb0c4f56c8558bffaf892d8043037841e7fa81cfa61a38c5e39b901c8ee71122a5da2227bd6cdeeb481452c12ad3d61d5e4f776a0ab556591befe3e59e5a7fddb8345e1f2f35b9f4cee57c32414c086aec993e9353e480d9eec6289f", 16),
+ new BigInteger("4ff897709fad079746494578e70fd8546130eeab5627c49b080f05ee4ad9f3e4b7cba9d6a5dff113a41c3409336833f190816d8a6bc42e9bec56b7567d0f3c9c696db619b245d901dd856db7c8092e77e9a1cccd56ee4dba42c5fdb61aec2669", 16),
+ new BigInteger("77b9d1137b50404a982729316efafc7dfe66d34e5a182600d5f30a0a8512051c560d081d4d0a1835ec3d25a60f4e4d6aa948b2bf3dbb5b124cbbc3489255a3a948372f6978496745f943e1db4f18382ceaa505dfc65757bb3f857a58dce52156", 16));
+
+ // PSS Example 9.1
+
+ private byte[] msg9a = Hex.decode("a88e265855e9d7ca36c68795f0b31b591cd6587c71d060a0b3f7f3eaef43795922028bc2b6ad467cfc2d7f659c5385aa70ba3672cdde4cfe4970cc7904601b278872bf51321c4a972f3c95570f3445d4f57980e0f20df54846e6a52c668f1288c03f95006ea32f562d40d52af9feb32f0fa06db65b588a237b34e592d55cf979f903a642ef64d2ed542aa8c77dc1dd762f45a59303ed75e541ca271e2b60ca709e44fa0661131e8d5d4163fd8d398566ce26de8730e72f9cca737641c244159420637028df0a18079d6208ea8b4711a2c750f5");
+
+ private byte[] slt9a = Hex.decode("c0a425313df8d7564bd2434d311523d5257eed80");
+
+ private byte[] sig9a = Hex.decode("586107226c3ce013a7c8f04d1a6a2959bb4b8e205ba43a27b50f124111bc35ef589b039f5932187cb696d7d9a32c0c38300a5cdda4834b62d2eb240af33f79d13dfbf095bf599e0d9686948c1964747b67e89c9aba5cd85016236f566cc5802cb13ead51bc7ca6bef3b94dcbdbb1d570469771df0e00b1a8a06777472d2316279edae86474668d4e1efff95f1de61c6020da32ae92bbf16520fef3cf4d88f61121f24bbd9fe91b59caf1235b2a93ff81fc403addf4ebdea84934a9cdaf8e1a9e");
+
+ // PSS Example 9.2
+
+ private byte[] msg9b = Hex.decode("c8c9c6af04acda414d227ef23e0820c3732c500dc87275e95b0d095413993c2658bc1d988581ba879c2d201f14cb88ced153a01969a7bf0a7be79c84c1486bc12b3fa6c59871b6827c8ce253ca5fefa8a8c690bf326e8e37cdb96d90a82ebab69f86350e1822e8bd536a2e");
+
+ private byte[] slt9b = Hex.decode("b307c43b4850a8dac2f15f32e37839ef8c5c0e91");
+
+ private byte[] sig9b = Hex.decode("80b6d643255209f0a456763897ac9ed259d459b49c2887e5882ecb4434cfd66dd7e1699375381e51cd7f554f2c271704b399d42b4be2540a0eca61951f55267f7c2878c122842dadb28b01bd5f8c025f7e228418a673c03d6bc0c736d0a29546bd67f786d9d692ccea778d71d98c2063b7a71092187a4d35af108111d83e83eae46c46aa34277e06044589903788f1d5e7cee25fb485e92949118814d6f2c3ee361489016f327fb5bc517eb50470bffa1afa5f4ce9aa0ce5b8ee19bf5501b958");
+
+
+ public String getName()
+ {
+ return "PSSTest";
+ }
+
+ private void testSig(
+ int id,
+ RSAKeyParameters pub,
+ RSAKeyParameters prv,
+ byte[] slt,
+ byte[] msg,
+ byte[] sig)
+ throws Exception
+ {
+ PSSSigner eng = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ eng.init(true, new ParametersWithRandom(prv, new FixedRandom(slt)));
+
+ eng.update(msg, 0, msg.length);
+
+ byte[] s = eng.generateSignature();
+
+ if (!areEqual(s, sig))
+ {
+ fail("test " + id + " failed generation");
+ }
+
+ eng.init(false, pub);
+
+ eng.update(msg, 0, msg.length);
+
+ if (!eng.verifySignature(s))
+ {
+ fail("test " + id + " failed verification");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testSig(1, pub1, prv1, slt1a, msg1a, sig1a);
+ testSig(2, pub1, prv1, slt1b, msg1b, sig1b);
+ testSig(3, pub2, prv2, slt2a, msg2a, sig2a);
+ testSig(4, pub2, prv2, slt2b, msg2b, sig2b);
+ testSig(5, pub4, prv4, slt4a, msg4a, sig4a);
+ testSig(6, pub4, prv4, slt4b, msg4b, sig4b);
+ testSig(7, pub8, prv8, slt8a, msg8a, sig8a);
+ testSig(8, pub8, prv8, slt8b, msg8b, sig8b);
+ testSig(9, pub9, prv9, slt9a, msg9a, sig9a);
+ testSig(10, pub9, prv9, slt9b, msg9b, sig9b);
+
+ //
+ // loop test - sha-1 only
+ //
+ PSSSigner eng = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+ int failed = 0;
+ byte[] data = new byte[DATA_LENGTH];
+
+ SecureRandom random = new SecureRandom();
+ random.nextBytes(data);
+
+ for (int j = 0; j < NUM_TESTS; j++)
+ {
+ eng.init(true, new ParametersWithRandom(prv8, random));
+
+ eng.update(data, 0, data.length);
+
+ byte[] s = eng.generateSignature();
+
+ eng.init(false, pub8);
+
+ eng.update(data, 0, data.length);
+
+ if (!eng.verifySignature(s))
+ {
+ failed++;
+ }
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test failed - failures: " + failed);
+ }
+
+ //
+ // loop test - sha-256 and sha-1
+ //
+ eng = new PSSSigner(new RSAEngine(), new SHA256Digest(), new SHA1Digest(), 20);
+ failed = 0;
+ data = new byte[DATA_LENGTH];
+
+ random.nextBytes(data);
+
+ for (int j = 0; j < NUM_TESTS; j++)
+ {
+ eng.init(true, new ParametersWithRandom(prv8, random));
+
+ eng.update(data, 0, data.length);
+
+ byte[] s = eng.generateSignature();
+
+ eng.init(false, pub8);
+
+ eng.update(data, 0, data.length);
+
+ if (!eng.verifySignature(s))
+ {
+ failed++;
+ }
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test failed - failures: " + failed);
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PSSTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PaddingTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PaddingTest.java
new file mode 100644
index 000000000..a1d62fd8c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/PaddingTest.java
@@ -0,0 +1,167 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.paddings.BlockCipherPadding;
+import org.spongycastle.crypto.paddings.ISO10126d2Padding;
+import org.spongycastle.crypto.paddings.ISO7816d4Padding;
+import org.spongycastle.crypto.paddings.PKCS7Padding;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.paddings.TBCPadding;
+import org.spongycastle.crypto.paddings.X923Padding;
+import org.spongycastle.crypto.paddings.ZeroBytePadding;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * General Padding tests.
+ */
+public class PaddingTest
+ extends SimpleTest
+{
+ public PaddingTest()
+ {
+ }
+
+ private void blockCheck(
+ PaddedBufferedBlockCipher cipher,
+ BlockCipherPadding padding,
+ KeyParameter key,
+ byte[] data)
+ {
+ byte[] out = new byte[data.length + 8];
+ byte[] dec = new byte[data.length];
+
+ try
+ {
+ cipher.init(true, key);
+
+ int len = cipher.processBytes(data, 0, data.length, out, 0);
+
+ len += cipher.doFinal(out, len);
+
+ cipher.init(false, key);
+
+ int decLen = cipher.processBytes(out, 0, len, dec, 0);
+
+ decLen += cipher.doFinal(dec, decLen);
+
+ if (!areEqual(data, dec))
+ {
+ fail("failed to decrypt - i = " + data.length + ", padding = " + padding.getPaddingName());
+ }
+ }
+ catch (Exception e)
+ {
+ fail("Exception - " + e.toString(), e);
+ }
+ }
+
+ public void testPadding(
+ BlockCipherPadding padding,
+ SecureRandom rand,
+ byte[] ffVector,
+ byte[] ZeroVector)
+ {
+ PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new DESEngine(), padding);
+ KeyParameter key = new KeyParameter(Hex.decode("0011223344556677"));
+
+ //
+ // ff test
+ //
+ byte[] data = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0 };
+
+ if (ffVector != null)
+ {
+ padding.addPadding(data, 3);
+
+ if (!areEqual(data, ffVector))
+ {
+ fail("failed ff test for " + padding.getPaddingName());
+ }
+ }
+
+ //
+ // zero test
+ //
+ if (ZeroVector != null)
+ {
+ data = new byte[8];
+ padding.addPadding(data, 4);
+
+ if (!areEqual(data, ZeroVector))
+ {
+ fail("failed zero test for " + padding.getPaddingName());
+ }
+ }
+
+ for (int i = 1; i != 200; i++)
+ {
+ data = new byte[i];
+
+ rand.nextBytes(data);
+
+ blockCheck(cipher, padding, key, data);
+ }
+ }
+
+ public void performTest()
+ {
+ SecureRandom rand = new SecureRandom(new byte[20]);
+
+ rand.setSeed(System.currentTimeMillis());
+
+ testPadding(new PKCS7Padding(), rand,
+ Hex.decode("ffffff0505050505"),
+ Hex.decode("0000000004040404"));
+
+ PKCS7Padding padder = new PKCS7Padding();
+ try
+ {
+ padder.padCount(new byte[8]);
+
+ fail("invalid padding not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!"pad block corrupted".equals(e.getMessage()))
+ {
+ fail("wrong exception for corrupt padding: " + e);
+ }
+ }
+
+ testPadding(new ISO10126d2Padding(), rand,
+ null,
+ null);
+
+ testPadding(new X923Padding(), rand,
+ null,
+ null);
+
+ testPadding(new TBCPadding(), rand,
+ Hex.decode("ffffff0000000000"),
+ Hex.decode("00000000ffffffff"));
+
+ testPadding(new ZeroBytePadding(), rand,
+ Hex.decode("ffffff0000000000"),
+ null);
+
+ testPadding(new ISO7816d4Padding(), rand,
+ Hex.decode("ffffff8000000000"),
+ Hex.decode("0000000080000000"));
+ }
+
+ public String getName()
+ {
+ return "PaddingTest";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PaddingTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Poly1305Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Poly1305Test.java
new file mode 100644
index 000000000..2410f249d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Poly1305Test.java
@@ -0,0 +1,388 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherKeyGenerator;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
+import org.spongycastle.crypto.macs.Poly1305;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/*
+ */
+public class Poly1305Test
+ extends SimpleTest
+{
+ private static final int MAXLEN = 1000;
+
+ private static class KeyEngine
+ implements BlockCipher
+ {
+
+ private byte[] key;
+ private final int blockSize;
+
+ public KeyEngine(int blockSize)
+ {
+ this.blockSize = blockSize;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.key = ((KeyParameter)params).getKey();
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Key";
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ System.arraycopy(key, 0, out, outOff, key.length);
+ return key.length;
+ }
+
+ public void reset()
+ {
+ }
+
+ }
+
+ private static class TestCase
+ {
+ private final byte[] key;
+ private final byte[] nonce;
+ private final byte[] message;
+ private final byte[] expectedMac;
+
+ public TestCase(String key, String nonce, String message, String expectedMac)
+ {
+ this.key = Hex.decode(key);
+ // nacl test case keys are not pre-clamped
+ Poly1305KeyGenerator.clamp(this.key);
+ this.nonce = (nonce == null) ? null : Hex.decode(nonce);
+ this.message = Hex.decode(message);
+ this.expectedMac = Hex.decode(expectedMac);
+ }
+ }
+
+ private static TestCase[] CASES = {
+ // Raw Poly1305
+ // onetimeauth.c from nacl-20110221
+ new TestCase("2539121d8e234e652d651fa4c8cff880eea6a7251c1e72916d11c2cb214d3c25", null,
+ "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a"
+ + "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738"
+ + "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da"
+ + "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74e355a5",
+ "f3ffc7703f9400e52a7dfb4b3d3305d9"),
+
+ // Poly1305-AES
+ // Loop 1 of test-poly1305aes from poly1305aes-20050218
+ new TestCase("0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000", "", "66e94bd4ef8a2c3b884cfa59ca342b2e"),
+ new TestCase("f795bd4a52e29ed713d313fa20e98dbcf795bd0a50e29e0710d3130a20e98d0c",
+ "917cf69ebd68b2ec9b9fe9a3eadda692", "66f7", "5ca585c75e8f8f025e710cabc9a1508b"),
+ new TestCase("e69dae0aab9f91c03a325dcc9436fa903ef49901c8e11c000430d90ad45e7603",
+ "166450152e2394835606a9d1dd2cdc8b", "66f75c0e0c7a406586", "2924f51b9c2eff5df09db61dd03a9ca1"),
+ new TestCase("85a4ea91a7de0b0d96eed0d4bf6ecf1cda4afc035087d90e503f8f0ea08c3e0d",
+ "0b6ef7a0b8f8c738b0f8d5995415271f",
+ "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea",
+ "3c5a13adb18d31c64cc29972030c917d"),
+ new TestCase(
+ "25eb69bac5cdf7d6bfcee4d9d5507b82ca3c6a0da0a864024ca3090628c28e0d",
+ "046772a4f0a8de92e4f0d628cdb04484",
+ "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055de",
+ "fc5fb58dc65daf19b14d1d05da1064e8"),
+
+ // Specific test cases generated from test-poly1305aes from poly1305aes-20050218 that
+ // expose Java unsigned integer problems
+ new TestCase(
+ "95cc0e44d0b79a8856afcae1bec4fe3c" + "01bcb20bfc8b6e03609ddd09f44b060f",
+ null,
+ "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055de"
+ + "fc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55"
+ + "e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930"
+ + "aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d97"
+ + "7cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757b"
+ + "d1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4"
+ + "dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee"
+ + "71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a"
+ + "855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cd"
+ + "d78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633"
+ + "b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13"
+ + "df4c41dc0fb8", "ae345d555eb04d6947bb95c0965237e2"),
+ new TestCase(
+ "76fb3635a2dc92a1f768163ab12f2187" + "cd07fd0ef8c0be0afcbdb30af4af0009",
+ null,
+ "f05204a74f0f88a7fa1a95b84ec3d8ffb36fcdc7723ea65dfe7cd464e86e0abf6b9d51db3220cfd8496ad6e6d36ebee8d990f9ce0d3bb7f72b7ab5b3ab0a73240d11efe772c857021ae859db4933cdde4387b471d2ce700fef4b81087f8f47c307881fd83017afcd15b8d21edf9b704677f46df97b07e5b83f87c8abd90af9b1d0f9e2710e8ebd0d4d1c6a055abea861f42368bed94d9373e909c1d3715b221c16bc524c55c31ec3eab204850bb2474a84f9917038eff9d921130951391b5c54f09b5e1de833ea2cd7d3b306740abb7096d1e173da83427da2adddd3631eda30b54dbf487f2b082e8646f07d6e0a87e97522ca38d4ace4954bf3db6dd3a93b06fa18eb56856627ed6cffcd7ae26374554ca18ab8905f26331d323fe10e6e70624c7bc07a70f06ecd804b48f8f7e75e910165e1beb554f1f0ec7949c9c8d429a206b4d5c0653102249b6098e6b45fac2a07ff0220b0b8ae8f4c6bcc0c813a7cd141fa8b398b42575fc395747c5a0257ac41d6c1f434cfbf5dfe8349f5347ef6b60e611f5d6c3cbc20ca2555274d1934325824cef4809da293ea13f181929e2af025bbd1c9abdc3af93afd4c50a2854ade3887f4d2c8c225168052c16e74d76d2dd3e9467a2c5b8e15c06ffbffa42b8536384139f07e195a8c9f70f514f31dca4eb2cf262c0dcbde53654b6250a29efe21d54e83c80e005a1cad36d5934ff01c32e4bc5fe06d03064ff4a268517df4a94c759289f323734318cfa5d859d4ce9c16e63d02dff0896976f521607638535d2ee8dd3312e1ddc80a55d34fe829ab954c1ebd54d929954770f1be9d32b4c05003c5c9e97943b6431e2afe820b1e967b19843e5985a131b1100517cdc363799104af91e2cf3f53cb8fd003653a6dd8a31a3f9d566a7124b0ffe9695bcb87c482eb60106f88198f766a40bc0f4873c23653c5f9e7a8e446f770beb8034cf01d21028ba15ccee21a8db918c4829d61c88bfa927bc5def831501796c5b401a60a6b1b433c9fb905c8cd40412fffee81ab",
+ "045be28cc52009f506bdbfabedacf0b4"),
+
+ };
+
+ public String getName()
+ {
+ return "Poly1305";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testKeyGenerator();
+ testInit();
+ for (int i = 0; i < CASES.length; i++)
+ {
+ testCase(i);
+ }
+ testSequential();
+ testReset();
+ }
+
+ private void testCase(int i)
+ {
+ byte[] out = new byte[16];
+ TestCase tc = CASES[i];
+
+ final Mac mac;
+ if (tc.nonce == null)
+ {
+ // Raw Poly1305 test - don't do any transform on AES key part
+ mac = new Poly1305(new KeyEngine(16));
+ mac.init(new ParametersWithIV(new KeyParameter(tc.key), new byte[16]));
+ }
+ else
+ {
+ mac = new Poly1305(new AESFastEngine());
+ mac.init(new ParametersWithIV(new KeyParameter(tc.key), tc.nonce));
+ }
+ mac.update(tc.message, 0, tc.message.length);
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, tc.expectedMac))
+ {
+ fail("Mismatched output " + i, new String(Hex.encode(tc.expectedMac)), new String(Hex.encode(out)));
+ }
+ }
+
+ private void testSequential()
+ {
+ // Sequential test, adapted from test-poly1305aes
+ int len;
+ byte[] kr = new byte[32];
+ byte[] m = new byte[MAXLEN];
+ byte[] n = new byte[16];
+ byte[] out = new byte[16];
+
+ int c = 0;
+ final Mac mac = new Poly1305(new AESFastEngine());
+ for (int loop = 0; loop < 13; loop++)
+ {
+ len = 0;
+ for (;;)
+ {
+ c++;
+ mac.init(new ParametersWithIV(new KeyParameter(kr), n));
+ mac.update(m, 0, len);
+ mac.doFinal(out, 0);
+
+ // if (c == 678)
+ // {
+ // TestCase tc = CASES[0];
+ //
+ // if (!Arrays.areEqual(tc.key, kr))
+ // {
+ // System.err.println("Key bad");
+ // System.err.println(new String(Hex.encode(tc.key)));
+ // System.err.println(new String(Hex.encode(kr)));
+ // System.exit(1);
+ // }
+ // if (!Arrays.areEqual(tc.nonce, n))
+ // {
+ // System.err.println("Nonce bad");
+ // System.exit(1);
+ // }
+ // System.out.printf("[%d] m: %s\n", c, new String(Hex.encode(m, 0, len)));
+ // System.out.printf("[%d] K: %s\n", c, new String(Hex.encodje(kr)));
+ // System.out.printf("[%d] N: %s\n", c, new String(Hex.encode(n)));
+ // System.out.printf("[%d] M: ", c);
+ // }
+ // System.out.printf("%d/%s\n", c, new String(Hex.encode(out)));
+
+ if (len >= MAXLEN)
+ break;
+ n[0] ^= loop;
+ for (int i = 0; i < 16; ++i)
+ n[i] ^= out[i];
+ if (len % 2 != 0)
+ for (int i = 0; i < 16; ++i)
+ kr[i] ^= out[i];
+ if (len % 3 != 0)
+ for (int i = 0; i < 16; ++i)
+ kr[i + 16] ^= out[i];
+ Poly1305KeyGenerator.clamp(kr);
+ m[len++] ^= out[0];
+ }
+ }
+ // Output after 13 loops as generated by poly1305 ref
+ if (c != 13013 || !Arrays.areEqual(out, Hex.decode("c96f60a23701a5b0fd2016f58cbe4f7e")))
+ {
+ fail("Sequential Poly1305 " + c, "c96f60a23701a5b0fd2016f58cbe4f7e", new String(Hex.encode(out)));
+ }
+ }
+
+ private void testReset()
+ {
+ CipherKeyGenerator gen = new Poly1305KeyGenerator();
+ gen.init(new KeyGenerationParameters(new SecureRandom(), 256));
+ byte[] k = gen.generateKey();
+
+ byte[] m = new byte[10000];
+ byte[] check = new byte[16];
+ byte[] out = new byte[16];
+
+ // Generate baseline
+ Mac poly = new Poly1305(new AESFastEngine());
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+
+ poly.update(m, 0, m.length);
+ poly.doFinal(check, 0);
+
+ // Check reset after doFinal
+ poly.update(m, 0, m.length);
+ poly.doFinal(out, 0);
+
+ if (!Arrays.areEqual(check, out))
+ {
+ fail("Mac not reset after doFinal");
+ }
+
+ // Check reset
+ poly.update((byte)1);
+ poly.update((byte)2);
+ poly.reset();
+ poly.update(m, 0, m.length);
+ poly.doFinal(out, 0);
+
+ if (!Arrays.areEqual(check, out))
+ {
+ fail("Mac not reset after doFinal");
+ }
+
+ // Check init resets
+ poly.update((byte)1);
+ poly.update((byte)2);
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+ poly.update(m, 0, m.length);
+ poly.doFinal(out, 0);
+
+ if (!Arrays.areEqual(check, out))
+ {
+ fail("Mac not reset after doFinal");
+ }
+ }
+
+ private void testInit()
+ {
+ CipherKeyGenerator gen = new Poly1305KeyGenerator();
+ gen.init(new KeyGenerationParameters(new SecureRandom(), 256));
+ byte[] k = gen.generateKey();
+
+ Mac poly = new Poly1305(new AESFastEngine());
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+
+ try
+ {
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[15]));
+ fail("16 byte nonce required");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ byte[] k2 = new byte[k.length - 1];
+ System.arraycopy(k, 0, k2, 0, k2.length);
+ poly.init(new ParametersWithIV(new KeyParameter(k2), new byte[16]));
+ fail("32 byte key required");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ k[19] = (byte)0xFF;
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+ fail("Unclamped key should not be accepted.");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ }
+
+ private void testKeyGenerator()
+ {
+ CipherKeyGenerator gen = new Poly1305KeyGenerator();
+ gen.init(new KeyGenerationParameters(new SecureRandom(), 256));
+ byte[] k = gen.generateKey();
+
+ if (k.length != 32)
+ {
+ fail("Poly1305 key should be 256 bits.");
+ }
+
+ try
+ {
+ Poly1305KeyGenerator.checkKey(k);
+ } catch (IllegalArgumentException e)
+ {
+ fail("Poly1305 key should be clamped on generation.");
+ }
+
+ byte[] k2 = new byte[k.length];
+ System.arraycopy(k, 0, k2, 0, k2.length);
+ Poly1305KeyGenerator.clamp(k);
+ if (!Arrays.areEqual(k, k2))
+ {
+ fail("Poly1305 key should be clamped on generation.");
+ }
+
+ try
+ {
+ k2[19] = (byte)0xff;
+ Poly1305KeyGenerator.checkKey(k2);
+ fail("Unclamped key should fail check.");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ runTest(new Poly1305Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC2Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC2Test.java
new file mode 100644
index 000000000..fde133471
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC2Test.java
@@ -0,0 +1,66 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.RC2Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RC2Parameters;
+import org.spongycastle.util.encoders.Hex;
+
+/**
+ * RC2 tester - vectors from ftp://ftp.isi.edu/in-notes/rfc2268.txt
+ *
+ * RFC 2268 "A Description of the RC2(r) Encryption Algorithm"
+ */
+public class RC2Test
+ extends CipherTest
+{
+ static BlockCipherVectorTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new RC2Engine(),
+ new RC2Parameters(Hex.decode("0000000000000000"), 63),
+ "0000000000000000", "ebb773f993278eff"),
+
+ new BlockCipherVectorTest(1, new RC2Engine(),
+ new RC2Parameters(Hex.decode("ffffffffffffffff"), 64),
+ "ffffffffffffffff", "278b27e42e2f0d49"),
+
+ new BlockCipherVectorTest(2, new RC2Engine(),
+ new RC2Parameters(Hex.decode("3000000000000000"), 64),
+ "1000000000000001", "30649edf9be7d2c2"),
+
+ new BlockCipherVectorTest(3, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88"), 64),
+ "0000000000000000", "61a8a244adacccf0"),
+
+ new BlockCipherVectorTest(4, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a"), 64),
+ "0000000000000000", "6ccf4308974c267f"),
+
+ new BlockCipherVectorTest(5, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a7f0f79c384627bafb2"), 64),
+ "0000000000000000", "1a807d272bbe5db1"),
+
+ new BlockCipherVectorTest(6, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a7f0f79c384627bafb2"), 128),
+ "0000000000000000", "2269552ab0f85ca6"),
+
+ new BlockCipherVectorTest(7, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e"), 129),
+ "0000000000000000", "5b78d3a43dfff1f1")
+ };
+
+ RC2Test()
+ {
+ super(tests, new RC2Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "RC2";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RC2Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC2WrapTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC2WrapTest.java
new file mode 100644
index 000000000..27cca204d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC2WrapTest.java
@@ -0,0 +1,111 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.engines.RC2WrapEngine;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RC2Parameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * RC2 wrap tester
+ */
+public class RC2WrapTest
+ implements Test
+{
+ private class RFCRandom
+ extends SecureRandom
+ {
+ public void nextBytes(
+ byte[] nextBytes)
+ {
+ System.arraycopy(Hex.decode("4845cce7fd1250"), 0, nextBytes, 0, nextBytes.length);
+ }
+ }
+
+ private TestResult wrapTest(
+ int id,
+ CipherParameters paramsWrap,
+ CipherParameters paramsUnwrap,
+ byte[] in,
+ byte[] out)
+ {
+ Wrapper wrapper = new RC2WrapEngine();
+
+ wrapper.init(true, paramsWrap);
+
+ try
+ {
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test exception " + e.toString(), e);
+ }
+
+ wrapper.init(false, paramsUnwrap);
+
+ try
+ {
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test exception " + e.toString(), e);
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public TestResult perform()
+ {
+ byte[] kek1 = Hex.decode("fd04fd08060707fb0003fefffd02fe05");
+ byte[] iv1 = Hex.decode("c7d90059b29e97f7");
+ byte[] in1 = Hex.decode("b70a25fbc9d86a86050ce0d711ead4d9");
+ byte[] out1 = Hex.decode("70e699fb5701f7833330fb71e87c85a420bdc99af05d22af5a0e48d35f3138986cbaafb4b28d4f35");
+ //
+ // note the RFC 3217 test specifies a key to be used with an effective key size of
+ // 40 bits which is why it is done here - in practice nothing less than 128 bits should be used.
+ //
+ CipherParameters paramWrap = new ParametersWithRandom(new ParametersWithIV(new RC2Parameters(kek1, 40), iv1), new RFCRandom());
+ CipherParameters paramUnwrap = new RC2Parameters(kek1, 40);
+
+ TestResult result = wrapTest(1, paramWrap, paramUnwrap, in1, out1);
+
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "RC2Wrap";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RC2WrapTest test = new RC2WrapTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC4Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC4Test.java
new file mode 100644
index 000000000..c03b4e51a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC4Test.java
@@ -0,0 +1,45 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.RC4Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * RC4 Test
+ */
+public class RC4Test
+ extends SimpleTest
+{
+ StreamCipherVectorTest[] tests =
+ {
+ new StreamCipherVectorTest(0, new RC4Engine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "4e6f772069732074", "3afbb5c77938280d"),
+ new StreamCipherVectorTest(0, new RC4Engine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "68652074696d6520", "1cf1e29379266d59"),
+ new StreamCipherVectorTest(0, new RC4Engine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "666f7220616c6c20", "12fbb0c771276459")
+ };
+
+ public String getName()
+ {
+ return "RC4";
+ }
+
+ public void performTest()
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ tests[i].performTest();
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RC4Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC5Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC5Test.java
new file mode 100644
index 000000000..29686ee0d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC5Test.java
@@ -0,0 +1,188 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.RC532Engine;
+import org.spongycastle.crypto.engines.RC564Engine;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.RC5Parameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * RC5 tester - vectors from ftp://ftp.nordu.net/rfc/rfc2040.txt
+ *
+ * RFC 2040 "The RC5, RC5-CBC, RC5-CBC-Pad, and RC5-CTS Algorithms"
+ */
+public class RC5Test
+ implements Test
+{
+ BlockCipherVectorTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "7a7bba4d79111d1e"),
+ new BlockCipherVectorTest(1, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "797bba4d78111d1e"),
+ new BlockCipherVectorTest(2, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000001")),
+ "0000000000000000", "7a7bba4d79111d1f"),
+ new BlockCipherVectorTest(3, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000000")),
+ "0000000000000001", "7a7bba4d79111d1f"),
+ new BlockCipherVectorTest(4, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "8b9ded91ce7794a6"),
+ new BlockCipherVectorTest(5, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("11"), 1),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "2f759fe7ad86a378"),
+ new BlockCipherVectorTest(6, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 2),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "dca2694bf40e0788"),
+ new BlockCipherVectorTest(7, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00000000"), 2),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "dca2694bf40e0788"),
+ new BlockCipherVectorTest(8, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00000000"), 8),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "dcfe098577eca5ff"),
+ new BlockCipherVectorTest(9, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 8),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "9646fb77638f9ca8"),
+ new BlockCipherVectorTest(10, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 12),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "b2b3209db6594da4"),
+ new BlockCipherVectorTest(11, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 16),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "545f7f32a5fc3836"),
+ new BlockCipherVectorTest(12, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304"), 8),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "8285e7c1b5bc7402"),
+ new BlockCipherVectorTest(13, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304"), 12),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "fc586f92f7080934"),
+ new BlockCipherVectorTest(14, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304"), 16),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "cf270ef9717ff7c4"),
+ new BlockCipherVectorTest(15, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 12),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "e493f1c1bb4d6e8c"),
+ new BlockCipherVectorTest(16, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 8),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "5c4c041e0f217ac3"),
+ new BlockCipherVectorTest(17, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 12),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "921f12485373b4f7"),
+ new BlockCipherVectorTest(18, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 16),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "5ba0ca6bbe7f5fad"),
+ new BlockCipherVectorTest(19, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304050607081020304050607080"), 8),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "c533771cd0110e63"),
+ new BlockCipherVectorTest(20, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304050607081020304050607080"), 12),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "294ddb46b3278d60"),
+ new BlockCipherVectorTest(21, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304050607081020304050607080"), 16),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "dad6bda9dfe8f7e8"),
+ new BlockCipherVectorTest(22, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405"), 12),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "97e0787837ed317f"),
+ new BlockCipherVectorTest(23, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405"), 8),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "7875dbf6738c6478"),
+ new BlockCipherVectorTest(23, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405"), 8),
+ Hex.decode("7875dbf6738c6478")),
+ "0808080808080808", "8f34c3c681c99695"),
+ new BlockCipherVectorTest(640, new CBCBlockCipher(new RC564Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "9f09b98d3f6062d9d4d59973d00e0e63"),
+ new BlockCipherVectorTest(641, new CBCBlockCipher(new RC564Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("00000000000000000000000000000000")),
+ "ffffffffffffffffffffffffffffffff", "9e09b98d3f6062d9d3d59973d00e0e63")
+ };
+
+ public String getName()
+ {
+ return "RC5";
+ }
+
+ public TestResult perform()
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult res = tests[i].perform();
+
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RC5Test test = new RC5Test();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC6Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC6Test.java
new file mode 100644
index 000000000..9d91547bd
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RC6Test.java
@@ -0,0 +1,64 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.RC6Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * RC6 Test - test vectors from AES Submitted RSA Reference implementation.
+ * ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/aes/rc6-unix-refc.tar
+ */
+public class RC6Test
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000",
+ "f71f65e7b80c0c6966fee607984b5cdf"),
+ new BlockCipherVectorTest(1, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("000000000000000000000000000000008000000000000000")),
+ "00000000000000000000000000000000",
+ "dd04c176440bbc6686c90aee775bd368"),
+ new BlockCipherVectorTest(2, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("000000000000000000000000000000000000001000000000")),
+ "00000000000000000000000000000000",
+ "937fe02d20fcb72f0f57201012b88ba4"),
+ new BlockCipherVectorTest(3, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("00000001000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "8a380594d7396453771a1dfbe2914c8e"),
+ new BlockCipherVectorTest(4, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("1000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "11395d4bfe4c8258979ee2bf2d24dff4"),
+ new BlockCipherVectorTest(5, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("0000000000000000000000000000000000080000000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "3d6f7e99f6512553bb983e8f75672b97")
+ };
+
+ RC6Test()
+ {
+ super(tests, new RC6Engine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "RC6";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RC6Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RFC3211WrapTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RFC3211WrapTest.java
new file mode 100644
index 000000000..8568e5903
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RFC3211WrapTest.java
@@ -0,0 +1,220 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.RFC3211WrapEngine;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.security.SecureRandom;
+
+/**
+ * Wrap Test based on RFC3211 test vectors
+ */
+public class RFC3211WrapTest
+ extends SimpleTest
+{
+ SecureRandom r1 = new SecureRandom()
+ {
+ int[] ints = { 0xC4, 0x36, 0xF5, 0x41 };
+ int count = 0;
+
+ public int nextInt()
+ {
+ return ints[count++];
+ }
+ };
+
+ SecureRandom r2 = new SecureRandom()
+ {
+ int[] ints = { 0xFA, 0x06, 0x0A, 0x45 };
+ int count = 0;
+
+ public int nextInt()
+ {
+ return ints[count++];
+ }
+ };
+
+ public String getName()
+ {
+ return "RFC3211Wrap";
+ }
+
+ private void wrapTest(
+ int id,
+ BlockCipher engine,
+ byte[] kek,
+ byte[] iv,
+ SecureRandom rand,
+ byte[] in,
+ byte[] out)
+ throws Exception
+ {
+ Wrapper wrapper = new RFC3211WrapEngine(engine);
+
+ wrapper.init(true, new ParametersWithRandom(new ParametersWithIV(new KeyParameter(kek), iv), rand));
+
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ fail("failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+
+ wrapper.init(false, new ParametersWithIV(new KeyParameter(kek), iv));
+
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ fail("rfailed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+
+ private void testCorruption()
+ throws InvalidCipherTextException
+ {
+ byte[] kek = Hex.decode("D1DAA78615F287E6");
+ byte[] iv = Hex.decode("EFE598EF21B33D6D");
+
+ Wrapper wrapper = new RFC3211WrapEngine(new DESEngine());
+
+ wrapper.init(false, new ParametersWithIV(new KeyParameter(kek), iv));
+
+ byte[] block = Hex.decode("ff739D838C627C897323A2F8C436F541");
+ encryptBlock(kek, iv, block);
+
+ try
+ {
+ wrapper.unwrap(block, 0, block.length);
+
+ fail("bad length not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("wrapped key corrupted"))
+ {
+ fail("wrong exception on length");
+ }
+ }
+
+ block = Hex.decode("08639D838C627C897323A2F8C436F541");
+ testChecksum(kek, iv, block, wrapper);
+
+ block = Hex.decode("08736D838C627C897323A2F8C436F541");
+ testChecksum(kek, iv, block, wrapper);
+
+ block = Hex.decode("08739D638C627C897323A2F8C436F541");
+ testChecksum(kek, iv, block, wrapper);
+ }
+
+ private void testChecksum(byte[] kek, byte[] iv, byte[] block, Wrapper wrapper)
+ {
+ encryptBlock(kek, iv, block);
+
+ try
+ {
+ wrapper.unwrap(block, 0, block.length);
+
+ fail("bad checksum not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("wrapped key fails checksum"))
+ {
+ fail("wrong exception");
+ }
+ }
+ }
+
+ private void encryptBlock(byte[] key, byte[] iv, byte[] cekBlock)
+ {
+ BlockCipher engine = new CBCBlockCipher(new DESEngine());
+
+ engine.init(true, new ParametersWithIV(new KeyParameter(key), iv));
+
+ for (int i = 0; i < cekBlock.length; i += 8)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ for (int i = 0; i < cekBlock.length; i += 8)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ wrapTest(1, new DESEngine(), Hex.decode("D1DAA78615F287E6"), Hex.decode("EFE598EF21B33D6D"), r1, Hex.decode("8C627C897323A2F8"), Hex.decode("B81B2565EE373CA6DEDCA26A178B0C10"));
+ wrapTest(2, new DESedeEngine(), Hex.decode("6A8970BF68C92CAEA84A8DF28510858607126380CC47AB2D"), Hex.decode("BAF1CA7931213C4E"), r2,
+ Hex.decode("8C637D887223A2F965B566EB014B0FA5D52300A3F7EA40FFFC577203C71BAF3B"),
+ Hex.decode("C03C514ABDB9E2C5AAC038572B5E24553876B377AAFB82ECA5A9D73F8AB143D9EC74E6CAD7DB260C"));
+
+ testCorruption();
+
+ Wrapper wrapper = new RFC3211WrapEngine(new DESEngine());
+ ParametersWithIV params = new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]);
+ byte[] buf = new byte[16];
+
+ try
+ {
+ wrapper.init(true, params);
+
+ wrapper.unwrap(buf, 0, buf.length);
+
+ fail("failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("unexpected exception: " + e, e);
+ }
+
+ try
+ {
+ wrapper.init(false, params);
+
+ wrapper.wrap(buf, 0, buf.length);
+
+ fail("failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ //
+ // short test
+ //
+ try
+ {
+ wrapper.init(false, params);
+
+ wrapper.unwrap(buf, 0, buf.length / 2);
+
+ fail("failed unwrap short test.");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RFC3211WrapTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD128DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD128DigestTest.java
new file mode 100644
index 000000000..04c1c2b2f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD128DigestTest.java
@@ -0,0 +1,58 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.RIPEMD128Digest;
+
+/**
+ * RIPEMD128 Digest Test
+ */
+public class RIPEMD128DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "cdf26213a150dc3ecb610f18f6b38b46",
+ "86be7afa339d0fc7cfc785e72f578d33",
+ "c14a12199c66e4ba84636b0f69144c77",
+ "9e327b3d6e523062afc1132d7df9d1b8",
+ "fd2aa607f71dc8f510714922b371834e",
+ "a1aa0689d0fafa2ddc22e88b49133a06",
+ "d1e959eb179c911faea4624c60c5c702",
+ "3f45ef194732c2dbb2c4a2c769795fa3"
+ };
+
+ final static String million_a_digest = "4a7f5723f954eba1216c9d8f6320431f";
+
+ RIPEMD128DigestTest()
+ {
+ super(new RIPEMD128Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD128Digest((RIPEMD128Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD128DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD128HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD128HMacTest.java
new file mode 100644
index 000000000..8a0a07c21
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD128HMacTest.java
@@ -0,0 +1,86 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.RIPEMD128Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * RIPEMD128 HMac Test, test vectors from RFC 2286
+ */
+public class RIPEMD128HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "fbf61f9492aa4bbf81c172e84e0734db",
+ "875f828862b6b334b427c55f9f7ff09b",
+ "09f0b2846d2f543da363cbec8d62a38d",
+ "bdbbd7cf03e44b5aa60af815be4d2294",
+ "e79808f24b25fd031c155f0d551d9a3a",
+ "dc732928de98104a1f59d373c150acbb",
+ "5c6bec96793e16d40690c237635f30c5"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "RIPEMD128HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new RIPEMD128Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RIPEMD128HMacTest test = new RIPEMD128HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD160DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD160DigestTest.java
new file mode 100644
index 000000000..9af98c926
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD160DigestTest.java
@@ -0,0 +1,58 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.RIPEMD160Digest;
+
+/**
+ * RIPEMD160 Digest Test
+ */
+public class RIPEMD160DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "9c1185a5c5e9fc54612808977ee8f548b2258d31",
+ "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
+ "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
+ "5d0689ef49d2fae572b881b123a85ffa21595f36",
+ "f71c27109c692c1b56bbdceb5b9d2865b3708dbc",
+ "12a053384a9c0c88e405a06c27dcf49ada62eb2b",
+ "b0e20b6e3116640286ed3a87a5713079b21f5189",
+ "9b752e45573d4b39f4dbd3323cab82bf63326bfb"
+ };
+
+ final static String million_a_digest = "52783243c1697bdbe16d37f97f68f08325dc1528";
+
+ RIPEMD160DigestTest()
+ {
+ super(new RIPEMD160Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD160Digest((RIPEMD160Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD160DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD160HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD160HMacTest.java
new file mode 100644
index 000000000..1676fef34
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD160HMacTest.java
@@ -0,0 +1,86 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.RIPEMD160Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * RIPEMD160 HMac Test, test vectors from RFC 2286
+ */
+public class RIPEMD160HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668",
+ "dda6c0213a485a9e24f4742064a7f033b43c4069",
+ "b0b105360de759960ab4f35298e116e295d8e7c1",
+ "d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4",
+ "7619693978f91d90539ae786500ff3d8e0518e39",
+ "6466ca07ac5eac29e1bd523e5ada7605b791fd8b",
+ "69ea60798d71616cce5fd0871e23754cd75d5a0a"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "RIPEMD160HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new RIPEMD160Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RIPEMD160HMacTest test = new RIPEMD160HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD256DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD256DigestTest.java
new file mode 100644
index 000000000..dd8a1b9c5
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD256DigestTest.java
@@ -0,0 +1,58 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.RIPEMD256Digest;
+
+/**
+ * RIPEMD128 Digest Test
+ */
+public class RIPEMD256DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d",
+ "f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925",
+ "afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65",
+ "87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e",
+ "649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133",
+ "3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f",
+ "5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8",
+ "06fdcc7a409548aaf91368c06a6275b553e3f099bf0ea4edfd6778df89a890dd"
+ };
+
+ final static String million_a_digest = "ac953744e10e31514c150d4d8d7b677342e33399788296e43ae4850ce4f97978";
+
+ RIPEMD256DigestTest()
+ {
+ super(new RIPEMD256Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD256Digest((RIPEMD256Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD256DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD320DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD320DigestTest.java
new file mode 100644
index 000000000..c64616ba3
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RIPEMD320DigestTest.java
@@ -0,0 +1,58 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.RIPEMD320Digest;
+
+/**
+ * RIPEMD320 Digest Test
+ */
+public class RIPEMD320DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8",
+ "ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d",
+ "de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d",
+ "3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197",
+ "cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009",
+ "d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac",
+ "ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4",
+ "557888af5f6d8ed62ab66945c6d2a0a47ecd5341e915eb8fea1d0524955f825dc717e4a008ab2d42"
+ };
+
+ final static String million_a_digest = "bdee37f4371e20646b8b0d862dda16292ae36f40965e8c8509e63d1dbddecc503e2b63eb9245bb66";
+
+ RIPEMD320DigestTest()
+ {
+ super(new RIPEMD320Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD320Digest((RIPEMD320Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD320DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSABlindedTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSABlindedTest.java
new file mode 100644
index 000000000..749e6ba1c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSABlindedTest.java
@@ -0,0 +1,437 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.encodings.OAEPEncoding;
+import org.spongycastle.crypto.encodings.PKCS1Encoding;
+import org.spongycastle.crypto.engines.RSABlindedEngine;
+import org.spongycastle.crypto.generators.RSAKeyPairGenerator;
+import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class RSABlindedTest
+ extends SimpleTest
+{
+ static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ static BigInteger pubExp = new BigInteger("11", 16);
+ static BigInteger privExp = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
+ static BigInteger p = new BigInteger("f75e80839b9b9379f1cf1128f321639757dba514642c206bbbd99f9a4846208b3e93fbbe5e0527cc59b1d4b929d9555853004c7c8b30ee6a213c3d1bb7415d03", 16);
+ static BigInteger q = new BigInteger("b892d9ebdbfc37e397256dd8a5d3123534d1f03726284743ddc6be3a709edb696fc40c7d902ed804c6eee730eee3d5b20bf6bd8d87a296813c87d3b3cc9d7947", 16);
+ static BigInteger pExp = new BigInteger("1d1a2d3ca8e52068b3094d501c9a842fec37f54db16e9a67070a8b3f53cc03d4257ad252a1a640eadd603724d7bf3737914b544ae332eedf4f34436cac25ceb5", 16);
+ static BigInteger qExp = new BigInteger("6c929e4e81672fef49d9c825163fec97c4b7ba7acb26c0824638ac22605d7201c94625770984f78a56e6e25904fe7db407099cad9b14588841b94f5ab498dded", 16);
+ static BigInteger crtCoef = new BigInteger("dae7651ee69ad1d081ec5e7188ae126f6004ff39556bde90e0b870962fa7b926d070686d8244fe5a9aa709a95686a104614834b0ada4b10f53197a5cb4c97339", 16);
+
+ static String input = "4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ //
+ // to check that we handling byte extension by big number correctly.
+ //
+ static String edgeInput = "ff6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ static byte[] oversizedSig = Hex.decode("01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] dudBlock = Hex.decode("000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] truncatedDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] incorrectPadding = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] missingDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+
+ public String getName()
+ {
+ return "RSABlinded";
+ }
+
+ private void testStrictPKCS1Length(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ AsymmetricBlockCipher eng = new RSABlindedEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(oversizedSig, 0, oversizedSig.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("oversized signature block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("block incorrect size"))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+
+ //System.setProperty(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+
+ System.getProperties().put(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+ eng = new PKCS1Encoding(new RSABlindedEngine());
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ System.getProperties().remove(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY);
+ }
+
+ private void testTruncatedPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, truncatedDataBlock, "block truncated");
+ }
+
+ private void testDudPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, dudBlock, "unknown block type");
+ }
+
+ private void testWrongPaddingPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, incorrectPadding, "block padding incorrect");
+ }
+
+ private void testMissingDataPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, missingDataBlock, "no data in block");
+ }
+
+ private void checkForPKCS1Exception(RSAKeyParameters pubParameters, RSAKeyParameters privParameters, byte[] inputData, String expectedMessage)
+ {
+ AsymmetricBlockCipher eng = new RSABlindedEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(inputData, 0, inputData.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("missing data block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals(expectedMessage))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+ }
+
+ private void testOAEP(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ //
+ // OAEP - public encrypt, private decrypt
+ //
+ AsymmetricBlockCipher eng = new OAEPEncoding(new RSABlindedEngine());
+ byte[] data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed OAEP Test");
+ }
+ }
+
+ public void performTest()
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
+ byte[] data = Hex.decode(edgeInput);
+
+ //
+ // RAW
+ //
+ AsymmetricBlockCipher eng = new RSABlindedEngine();
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!edgeInput.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW edge Test");
+ }
+
+ data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW Test");
+ }
+
+ //
+ // PKCS1 - public encrypt, private decrypt
+ //
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(true, pubParameters);
+
+ if (eng.getOutputBlockSize() != ((PKCS1Encoding)eng).getUnderlyingCipher().getOutputBlockSize())
+ {
+ fail("PKCS1 output block size incorrect");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 public/private Test");
+ }
+
+ //
+ // PKCS1 - private encrypt, public decrypt
+ //
+ eng = new PKCS1Encoding(((PKCS1Encoding)eng).getUnderlyingCipher());
+
+ eng.init(true, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 private/public Test");
+ }
+
+ //
+ // key generation test
+ //
+ RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+ RSAKeyGenerationParameters genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 768, 25);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ eng = new RSABlindedEngine();
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 768)
+ {
+ fail("failed key generation (768) length test");
+ }
+
+ eng.init(true, pair.getPublic());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (768) Test");
+ }
+
+ genParam = new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25);
+
+ pGen.init(genParam);
+ pair = pGen.generateKeyPair();
+
+ eng.init(true, pair.getPublic());
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 1024)
+ {
+ fail("failed key generation (1024) length test");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (1024) test");
+ }
+
+ testOAEP(pubParameters, privParameters);
+ testStrictPKCS1Length(pubParameters, privParameters);
+ testDudPKCS1Block(pubParameters, privParameters);
+ testMissingDataPKCS1Block(pubParameters, privParameters);
+ testTruncatedPKCS1Block(pubParameters, privParameters);
+ testWrongPaddingPKCS1Block(pubParameters, privParameters);
+
+ try
+ {
+ new RSABlindedEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RSABlindedTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSADigestSignerTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSADigestSignerTest.java
new file mode 100644
index 000000000..5587dcf8a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSADigestSignerTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.asn1.x509.X509ObjectIdentifiers;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.crypto.signers.RSADigestSigner;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+
+public class RSADigestSignerTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "RSADigestSigner";
+ }
+
+ public void performTest() throws Exception
+ {
+ BigInteger rsaPubMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+ BigInteger rsaPubExp = new BigInteger(Base64.decode("EQ=="));
+ BigInteger rsaPrivMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+ BigInteger rsaPrivDP = new BigInteger(Base64.decode("JXzfzG5v+HtLJIZqYMUefJfFLu8DPuJGaLD6lI3cZ0babWZ/oPGoJa5iHpX4Ul/7l3s1PFsuy1GhzCdOdlfRcQ=="));
+ BigInteger rsaPrivDQ = new BigInteger(Base64.decode("YNdJhw3cn0gBoVmMIFRZzflPDNthBiWy/dUMSRfJCxoZjSnr1gysZHK01HteV1YYNGcwPdr3j4FbOfri5c6DUQ=="));
+ BigInteger rsaPrivExp = new BigInteger(Base64.decode("DxFAOhDajr00rBjqX+7nyZ/9sHWRCCp9WEN5wCsFiWVRPtdB+NeLcou7mWXwf1Y+8xNgmmh//fPV45G2dsyBeZbXeJwB7bzx9NMEAfedchyOwjR8PYdjK3NpTLKtZlEJ6Jkh4QihrXpZMO4fKZWUm9bid3+lmiq43FwW+Hof8/E="));
+ BigInteger rsaPrivP = new BigInteger(Base64.decode("AJ9StyTVW+AL/1s7RBtFwZGFBgd3zctBqzzwKPda6LbtIFDznmwDCqAlIQH9X14X7UPLokCDhuAa76OnDXb1OiE="));
+ BigInteger rsaPrivQ = new BigInteger(Base64.decode("AM3JfD79dNJ5A3beScSzPtWxx/tSLi0QHFtkuhtSizeXdkv5FSba7lVzwEOGKHmW829bRoNxThDy4ds1IihW1w0="));
+ BigInteger rsaPrivQinv = new BigInteger(Base64.decode("Lt0g7wrsNsQxuDdB8q/rH8fSFeBXMGLtCIqfOec1j7FEIuYA/ACiRDgXkHa0WgN7nLXSjHoy630wC5Toq8vvUg=="));
+ RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp);
+ RSAPrivateCrtKeyParameters rsaPrivate = new RSAPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv);
+
+ byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+ RSADigestSigner signer = new RSADigestSigner(new SHA1Digest());
+ signer.init(true, rsaPrivate);
+ signer.update(msg, 0, msg.length);
+ byte[] sig = signer.generateSignature();
+
+ signer = new RSADigestSigner(new SHA1Digest(), X509ObjectIdentifiers.id_SHA1);
+ signer.init(false, rsaPublic);
+ signer.update(msg, 0, msg.length);
+ if (!signer.verifySignature(sig))
+ {
+ fail("RSA Digest Signer failed.");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new RSADigestSignerTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSAKeyEncapsulationTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSAKeyEncapsulationTest.java
new file mode 100755
index 000000000..b2f0f8fde
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSAKeyEncapsulationTest.java
@@ -0,0 +1,61 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.generators.KDF2BytesGenerator;
+import org.spongycastle.crypto.generators.RSAKeyPairGenerator;
+import org.spongycastle.crypto.kems.RSAKeyEncapsulation;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Tests for the RSA Key Encapsulation Mechanism
+ */
+public class RSAKeyEncapsulationTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "RSAKeyEncapsulation";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ // Generate RSA key pair
+ RSAKeyPairGenerator rsaGen = new RSAKeyPairGenerator();
+ rsaGen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(65537), new SecureRandom(), 1024, 5));
+ AsymmetricCipherKeyPair keys = rsaGen.generateKeyPair();
+
+ // Set RSA-KEM parameters
+ RSAKeyEncapsulation kem;
+ KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest());
+ SecureRandom rnd = new SecureRandom();
+ byte[] out = new byte[128];
+ KeyParameter key1, key2;
+
+ // Test RSA-KEM
+ kem = new RSAKeyEncapsulation(kdf, rnd);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed test");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RSAKeyEncapsulationTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSATest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSATest.java
new file mode 100644
index 000000000..bf1150032
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RSATest.java
@@ -0,0 +1,498 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.encodings.OAEPEncoding;
+import org.spongycastle.crypto.encodings.PKCS1Encoding;
+import org.spongycastle.crypto.engines.RSAEngine;
+import org.spongycastle.crypto.generators.RSAKeyPairGenerator;
+import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class RSATest
+ extends SimpleTest
+{
+ static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ static BigInteger pubExp = new BigInteger("11", 16);
+ static BigInteger privExp = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
+ static BigInteger p = new BigInteger("f75e80839b9b9379f1cf1128f321639757dba514642c206bbbd99f9a4846208b3e93fbbe5e0527cc59b1d4b929d9555853004c7c8b30ee6a213c3d1bb7415d03", 16);
+ static BigInteger q = new BigInteger("b892d9ebdbfc37e397256dd8a5d3123534d1f03726284743ddc6be3a709edb696fc40c7d902ed804c6eee730eee3d5b20bf6bd8d87a296813c87d3b3cc9d7947", 16);
+ static BigInteger pExp = new BigInteger("1d1a2d3ca8e52068b3094d501c9a842fec37f54db16e9a67070a8b3f53cc03d4257ad252a1a640eadd603724d7bf3737914b544ae332eedf4f34436cac25ceb5", 16);
+ static BigInteger qExp = new BigInteger("6c929e4e81672fef49d9c825163fec97c4b7ba7acb26c0824638ac22605d7201c94625770984f78a56e6e25904fe7db407099cad9b14588841b94f5ab498dded", 16);
+ static BigInteger crtCoef = new BigInteger("dae7651ee69ad1d081ec5e7188ae126f6004ff39556bde90e0b870962fa7b926d070686d8244fe5a9aa709a95686a104614834b0ada4b10f53197a5cb4c97339", 16);
+
+ static String input = "4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ //
+ // to check that we handling byte extension by big number correctly.
+ //
+ static String edgeInput = "ff6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ static byte[] oversizedSig = Hex.decode("01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] dudBlock = Hex.decode("000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] truncatedDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] incorrectPadding = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] missingDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+
+ public String getName()
+ {
+ return "RSA";
+ }
+
+ private void testStrictPKCS1Length(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ AsymmetricBlockCipher eng = new RSAEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(oversizedSig, 0, oversizedSig.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("oversized signature block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("block incorrect size"))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+
+ //System.setProperty(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+
+ System.getProperties().put(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+ eng = new PKCS1Encoding(new RSAEngine());
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ System.getProperties().remove(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY);
+ }
+
+ private void testTruncatedPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, truncatedDataBlock, "block truncated");
+ }
+
+ private void testDudPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, dudBlock, "unknown block type");
+ }
+
+ private void testWrongPaddingPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, incorrectPadding, "block padding incorrect");
+ }
+
+ private void testMissingDataPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, missingDataBlock, "no data in block");
+ }
+
+ private void checkForPKCS1Exception(RSAKeyParameters pubParameters, RSAKeyParameters privParameters, byte[] inputData, String expectedMessage)
+ {
+ AsymmetricBlockCipher eng = new RSAEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(inputData, 0, inputData.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("missing data block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals(expectedMessage))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+ }
+
+ private void testOAEP(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ //
+ // OAEP - public encrypt, private decrypt
+ //
+ AsymmetricBlockCipher eng = new OAEPEncoding(new RSAEngine());
+ byte[] data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed OAEP Test");
+ }
+ }
+
+ private void zeroBlockTest(CipherParameters encParameters, CipherParameters decParameters)
+ {
+ AsymmetricBlockCipher eng = new PKCS1Encoding(new RSAEngine());
+
+ eng.init(true, encParameters);
+
+ if (eng.getOutputBlockSize() != ((PKCS1Encoding)eng).getUnderlyingCipher().getOutputBlockSize())
+ {
+ fail("PKCS1 output block size incorrect");
+ }
+
+ byte[] zero = new byte[0];
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(zero, 0, zero.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, decParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!Arrays.areEqual(zero, data))
+ {
+ fail("failed PKCS1 zero Test");
+ }
+ }
+
+ public void performTest()
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
+ byte[] data = Hex.decode(edgeInput);
+
+ //
+ // RAW
+ //
+ AsymmetricBlockCipher eng = new RSAEngine();
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!edgeInput.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW edge Test");
+ }
+
+ data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW Test");
+ }
+
+ //
+ // PKCS1 - public encrypt, private decrypt
+ //
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(true, pubParameters);
+
+ if (eng.getOutputBlockSize() != ((PKCS1Encoding)eng).getUnderlyingCipher().getOutputBlockSize())
+ {
+ fail("PKCS1 output block size incorrect");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 public/private Test");
+ }
+
+ //
+ // PKCS1 - private encrypt, public decrypt
+ //
+ eng = new PKCS1Encoding(((PKCS1Encoding)eng).getUnderlyingCipher());
+
+ eng.init(true, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 private/public Test");
+ }
+
+ zeroBlockTest(pubParameters, privParameters);
+ zeroBlockTest(privParameters, pubParameters);
+
+ //
+ // key generation test
+ //
+ RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+ RSAKeyGenerationParameters genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 768, 25);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ eng = new RSAEngine();
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 768)
+ {
+ fail("failed key generation (768) length test");
+ }
+
+ eng.init(true, pair.getPublic());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (768) Test");
+ }
+
+ genParam = new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25);
+
+ pGen.init(genParam);
+ pair = pGen.generateKeyPair();
+
+ eng.init(true, pair.getPublic());
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 1024)
+ {
+ fail("failed key generation (1024) length test");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (1024) test");
+ }
+
+ genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 16, 25);
+ pGen.init(genParam);
+
+ for (int i = 0; i < 100; ++i)
+ {
+ pair = pGen.generateKeyPair();
+ RSAPrivateCrtKeyParameters privKey = (RSAPrivateCrtKeyParameters) pair.getPrivate();
+ BigInteger pqDiff = privKey.getP().subtract(privKey.getQ()).abs();
+
+ if (pqDiff.bitLength() < 5)
+ {
+ fail("P and Q too close in RSA key pair");
+ }
+ }
+
+ testOAEP(pubParameters, privParameters);
+ testStrictPKCS1Length(pubParameters, privParameters);
+ testDudPKCS1Block(pubParameters, privParameters);
+ testMissingDataPKCS1Block(pubParameters, privParameters);
+ testTruncatedPKCS1Block(pubParameters, privParameters);
+ testWrongPaddingPKCS1Block(pubParameters, privParameters);
+
+ try
+ {
+ new RSAEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RSATest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RegressionTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RegressionTest.java
new file mode 100644
index 000000000..63dd638e1
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RegressionTest.java
@@ -0,0 +1,155 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+public class RegressionTest
+{
+ public static Test[] tests =
+ {
+ new AESTest(),
+ new AESLightTest(),
+ new AESFastTest(),
+ new AESWrapTest(),
+ new DESTest(),
+ new DESedeTest(),
+ new ModeTest(),
+ new PaddingTest(),
+ new DHTest(),
+ new ElGamalTest(),
+ new DSATest(),
+ new ECTest(),
+ new DeterministicDSATest(),
+ new GOST3410Test(),
+ new ECGOST3410Test(),
+ new ECIESTest(),
+ new ECNRTest(),
+ new MacTest(),
+ new GOST28147MacTest(),
+ new RC2Test(),
+ new RC2WrapTest(),
+ new RC4Test(),
+ new RC5Test(),
+ new RC6Test(),
+ new RijndaelTest(),
+ new SerpentTest(),
+ new CamelliaTest(),
+ new CamelliaLightTest(),
+ new DigestRandomNumberTest(),
+ new SkipjackTest(),
+ new BlowfishTest(),
+ new TwofishTest(),
+ new Threefish256Test(),
+ new Threefish512Test(),
+ new Threefish1024Test(),
+ new SkeinDigestTest(),
+ new SkeinMacTest(),
+ new CAST5Test(),
+ new CAST6Test(),
+ new GOST28147Test(),
+ new IDEATest(),
+ new RSATest(),
+ new RSABlindedTest(),
+ new RSADigestSignerTest(),
+ new PSSBlindTest(),
+ new ISO9796Test(),
+ new ISO9797Alg3MacTest(),
+ new MD2DigestTest(),
+ new MD4DigestTest(),
+ new MD5DigestTest(),
+ new SHA1DigestTest(),
+ new SHA224DigestTest(),
+ new SHA256DigestTest(),
+ new SHA384DigestTest(),
+ new SHA512DigestTest(),
+ new SHA512t224DigestTest(),
+ new SHA512t256DigestTest(),
+ new SHA3DigestTest(),
+ new RIPEMD128DigestTest(),
+ new RIPEMD160DigestTest(),
+ new RIPEMD256DigestTest(),
+ new RIPEMD320DigestTest(),
+ new TigerDigestTest(),
+ new GOST3411DigestTest(),
+ new WhirlpoolDigestTest(),
+ new MD5HMacTest(),
+ new SHA1HMacTest(),
+ new SHA224HMacTest(),
+ new SHA256HMacTest(),
+ new SHA384HMacTest(),
+ new SHA512HMacTest(),
+ new RIPEMD128HMacTest(),
+ new RIPEMD160HMacTest(),
+ new OAEPTest(),
+ new PSSTest(),
+ new CTSTest(),
+ new CCMTest(),
+ new PKCS5Test(),
+ new PKCS12Test(),
+ new KDF1GeneratorTest(),
+ new KDF2GeneratorTest(),
+ new MGF1GeneratorTest(),
+ new HKDFGeneratorTest(),
+ new DHKEKGeneratorTest(),
+ new ECDHKEKGeneratorTest(),
+ new ShortenedDigestTest(),
+ new EqualsHashCodeTest(),
+ new TEATest(),
+ new XTEATest(),
+ new RFC3211WrapTest(),
+ new SEEDTest(),
+ new Salsa20Test(),
+ new XSalsa20Test(),
+ new ChaChaTest(),
+ new CMacTest(),
+ new EAXTest(),
+ new GCMTest(),
+ new GMacTest(),
+ new HCFamilyTest(),
+ new HCFamilyVecTest(),
+ new ISAACTest(),
+ new NoekeonTest(),
+ new VMPCKSA3Test(),
+ new VMPCMacTest(),
+ new VMPCTest(),
+ new Grainv1Test(),
+ new Grain128Test(),
+ //new NaccacheSternTest(),
+ new SRP6Test(),
+ new SCryptTest(),
+ new ResetTest(),
+ new NullTest(),
+ new DSTU4145Test(),
+ new SipHashTest(),
+ new Poly1305Test(),
+ new OCBTest(),
+ new NonMemoableDigestTest(),
+ new RSAKeyEncapsulationTest(),
+ new ECIESKeyEncapsulationTest(),
+ new HashCommitmentTest(),
+ new CipherStreamTest(),
+ new BlockCipherResetTest(),
+ new StreamCipherResetTest(),
+ new SM3DigestTest(),
+ new Shacal2Test(),
+ new KDFCounterGeneratorTest(),
+ new KDFDoublePipelineIteratorGeneratorTest(),
+ new KDFFeedbackGeneratorTest()
+ };
+
+ public static void main(
+ String[] args)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace();
+ }
+
+ System.out.println(result);
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ResetTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ResetTest.java
new file mode 100644
index 000000000..dd323d7d4
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ResetTest.java
@@ -0,0 +1,99 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ResetTest
+ extends SimpleTest
+{
+ private static final byte[] input = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+ private static final byte[] output = Hex.decode("3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53");
+ public String getName()
+ {
+ return "Reset";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new BufferedBlockCipher(new DESEngine());
+
+ KeyParameter param = new KeyParameter(Hex.decode("0123456789abcdef"));
+
+ basicTrial(cipher, param);
+
+ cipher.init(false, param);
+
+ byte[] out = new byte[input.length];
+
+ int len2 = cipher.processBytes(output, 0, output.length - 1, out, 0);
+
+ try
+ {
+ cipher.doFinal(out, len2);
+ fail("no DataLengthException - short input");
+ }
+ catch (DataLengthException e)
+ {
+ // ignore
+ }
+
+ len2 = cipher.processBytes(output, 0, output.length, out, 0);
+
+ cipher.doFinal(out, len2);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal one got " + new String(Hex.encode(out)));
+ }
+
+ len2 = cipher.processBytes(output, 0, output.length - 1, out, 0);
+
+ try
+ {
+ cipher.doFinal(out, len2);
+ fail("no DataLengthException - short output");
+ }
+ catch (DataLengthException e)
+ {
+ // ignore
+ }
+
+ len2 = cipher.processBytes(output, 0, output.length, out, 0);
+
+ cipher.doFinal(out, len2);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal two got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void basicTrial(BufferedBlockCipher cipher, KeyParameter param)
+ throws InvalidCipherTextException
+ {
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ int len1 = cipher.processBytes(input, 0, input.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (!areEqual(out, output))
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ResetTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RijndaelTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RijndaelTest.java
new file mode 100644
index 000000000..6c5d742bc
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/RijndaelTest.java
@@ -0,0 +1,116 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.RijndaelEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ *
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ */
+public class RijndaelTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a409382", "16e73aec921314c29df905432bc8968ab64b1f51"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160")),
+ "3243f6a8885a308d313198a2e03707344a409382", "0553eb691670dd8a5a5b5addf1aa7450f7a0e587"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a409382", "73cd6f3423036790463aa9e19cfcde894ea16623"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a409382", "601b5dcd1cf4ece954c740445340bf0afdc048df"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a409382", "579e930b36c1529aa3e86628bacfe146942882cf"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "b24d275489e82bb8f7375e0d5fcdb1f481757c538b65148a"),
+ new BlockCipherVectorTest(9, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "725ae43b5f3161de806a7c93e0bca93c967ec1ae1b71e1cf"),
+ new BlockCipherVectorTest(10, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "bbfc14180afbf6a36382a061843f0b63e769acdc98769130"),
+ new BlockCipherVectorTest(11, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "0ebacf199e3315c2e34b24fcc7c46ef4388aa475d66c194c"),
+ new BlockCipherVectorTest(12, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "b0a8f78f6b3c66213f792ffd2a61631f79331407a5e5c8d3793aceb1"),
+ new BlockCipherVectorTest(13, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "08b99944edfce33a2acb131183ab0168446b2d15e958480010f545e3"),
+ new BlockCipherVectorTest(14, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "be4c597d8f7efe22a2f7e5b1938e2564d452a5bfe72399c7af1101e2"),
+ new BlockCipherVectorTest(15, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "ef529598ecbce297811b49bbed2c33bbe1241d6e1a833dbe119569e8"),
+ new BlockCipherVectorTest(16, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "02fafc200176ed05deb8edb82a3555b0b10d47a388dfd59cab2f6c11"),
+ new BlockCipherVectorTest(17, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "7d15479076b69a46ffb3b3beae97ad8313f622f67fedb487de9f06b9ed9c8f19"),
+ new BlockCipherVectorTest(18, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "514f93fb296b5ad16aa7df8b577abcbd484decacccc7fb1f18dc567309ceeffd"),
+ new BlockCipherVectorTest(19, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "5d7101727bb25781bf6715b0e6955282b9610e23a43c2eb062699f0ebf5887b2"),
+ new BlockCipherVectorTest(20, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "d56c5a63627432579e1dd308b2c8f157b40a4bfb56fea1377b25d3ed3d6dbf80"),
+ new BlockCipherVectorTest(21, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "a49406115dfb30a40418aafa4869b7c6a886ff31602a7dd19c889dc64f7e4e7a")
+ };
+
+ RijndaelTest()
+ {
+ super(tests, new RijndaelEngine(128), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "Rijndael";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RijndaelTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SCryptTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SCryptTest.java
new file mode 100644
index 000000000..95731f087
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SCryptTest.java
@@ -0,0 +1,96 @@
+package org.spongycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import org.spongycastle.crypto.generators.SCrypt;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/*
+ * scrypt test vectors from "Stronger Key Derivation Via Sequential Memory-hard Functions" Appendix B.
+ * (http://www.tarsnap.com/scrypt/scrypt.pdf)
+ */
+public class SCryptTest extends SimpleTest
+{
+ public String getName()
+ {
+ return "SCrypt";
+ }
+
+ public void performTest() throws Exception
+ {
+ BufferedReader br = new BufferedReader(new InputStreamReader(
+ getClass().getResourceAsStream("SCryptTestVectors.txt")));
+
+ int count = 0;
+ String line = br.readLine();
+
+ while (line != null)
+ {
+ ++count;
+ String header = line;
+ StringBuffer data = new StringBuffer();
+
+ while (!isEndData(line = br.readLine()))
+ {
+ for (int i = 0; i != line.length(); i++)
+ {
+ if (line.charAt(i) != ' ')
+ {
+ data.append(line.charAt(i));
+ }
+ }
+ }
+
+ int start = header.indexOf('(') + 1;
+ int limit = header.lastIndexOf(')');
+ String argStr = header.substring(start, limit);
+ String[] args = Strings.split(argStr, ',');
+
+ byte[] P = extractQuotedString(args[0]);
+ byte[] S = extractQuotedString(args[1]);
+ int N = extractInteger(args[2]);
+ int r = extractInteger(args[3]);
+ int p = extractInteger(args[4]);
+ int dkLen = extractInteger(args[5]);
+ byte[] expected = Hex.decode(data.toString());
+
+ // This skips very expensive test case(s), remove check to re-enable
+ if (N <= 16384)
+ {
+ byte[] result = SCrypt.generate(P, S, N, r, p, dkLen);
+
+ if (!areEqual(expected, result))
+ {
+ fail("Result does not match expected value in test case " + count);
+ }
+ }
+ }
+
+ br.close();
+ }
+
+ private static boolean isEndData(String line)
+ {
+ return line == null || line.startsWith("scrypt");
+ }
+
+ private static byte[] extractQuotedString(String arg)
+ {
+ arg = arg.trim();
+ arg = arg.substring(1, arg.length() - 1);
+ return Strings.toByteArray(arg);
+ }
+
+ private static int extractInteger(String arg)
+ {
+ return Integer.parseInt(arg.trim());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SCryptTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SEEDTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SEEDTest.java
new file mode 100644
index 000000000..a38c59c15
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SEEDTest.java
@@ -0,0 +1,53 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.SEEDEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * SEED tester - vectors http://www.ietf.org/rfc/rfc4009.txt
+ */
+public class SEEDTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "000102030405060708090a0b0c0d0e0f",
+ "5EBAC6E0054E166819AFF1CC6D346CDB"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "00000000000000000000000000000000",
+ "c11f22f20140505084483597e4370f43"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("4706480851E61BE85D74BFB3FD956185")),
+ "83A2F8A288641FB9A4E9A5CC2F131C7D",
+ "EE54D13EBCAE706D226BC3142CD40D4A"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("28DBC3BC49FFD87DCFA509B11D422BE7")),
+ "B41E6BE2EBA84A148E2EED84593C5EC7",
+ "9B9B7BFCD1813CB95D0B3618F40F5122"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E")),
+ "0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E",
+ "8296F2F1B007AB9D533FDEE35A9AD850"),
+ };
+
+ SEEDTest()
+ {
+ super(tests, new SEEDEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "SEED";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SEEDTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA1DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA1DigestTest.java
new file mode 100644
index 000000000..0dc1bdcfd
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA1DigestTest.java
@@ -0,0 +1,43 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+
+/**
+ * standard vector test for SHA-1 from "Handbook of Applied Cryptography", page 345.
+ */
+public class SHA1DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghijklmnopqrstuvwxyz"
+ };
+
+ private static String[] digests =
+ {
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89"
+ };
+
+ SHA1DigestTest()
+ {
+ super(new SHA1Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA1Digest((SHA1Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA1DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA1HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA1HMacTest.java
new file mode 100644
index 000000000..4740988ce
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA1HMacTest.java
@@ -0,0 +1,111 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * SHA1 HMac Test, test vectors from RFC 2202
+ */
+public class SHA1HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"
+ };
+
+ final static String[] digests = {
+ "b617318655057264e28bc0b6fb378c8ef146be00",
+ "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+ "125d7342b9ac11cd91a39af48aa17b4f63f175d3",
+ "4c9007f4026250c6bc8414f9bf50c86c2d7235da",
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91",
+ "5FD596EE78D5553C8FF4E72D266DFD192366DA29"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ "Sample message for keylen=blocklen"
+ };
+
+ public String getName()
+ {
+ return "SHA1HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA1Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA1HMacTest test = new SHA1HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA224DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA224DigestTest.java
new file mode 100644
index 000000000..2e793dfd9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA224DigestTest.java
@@ -0,0 +1,54 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA224Digest;
+
+/**
+ * standard vector test for SHA-224 from RFC 3874 - only the last three are in
+ * the RFC.
+ */
+public class SHA224DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ };
+
+ private static String[] digests =
+ {
+ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
+ "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5",
+ "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
+ "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67";
+
+ SHA224DigestTest()
+ {
+ super(new SHA224Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA224Digest((SHA224Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA224DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA224HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA224HMacTest.java
new file mode 100644
index 000000000..5f4c73ed8
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA224HMacTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.SHA224Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * SHA224 HMac Test
+ */
+public class SHA224HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
+ "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
+ "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
+ "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
+ "0e2aea68a90c8d37c988bcdb9fca6fa8099cd857c7ec4a1815cac54c",
+ "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
+ "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA224HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA224Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA224HMacTest test = new SHA224HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA256DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA256DigestTest.java
new file mode 100644
index 000000000..556962659
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA256DigestTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+
+/**
+ * standard vector test for SHA-256 from FIPS Draft 180-2.
+ *
+ * Note, the first two vectors are _not_ from the draft, the last three are.
+ */
+public class SHA256DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ };
+
+ private static String[] digests =
+ {
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0";
+
+ SHA256DigestTest()
+ {
+ super(new SHA256Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA256Digest((SHA256Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA256DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA256HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA256HMacTest.java
new file mode 100644
index 000000000..f29a81d16
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA256HMacTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * SHA256 HMac Test
+ */
+public class SHA256HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+ "a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5",
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA256HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA256Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA256HMacTest test = new SHA256HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA384DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA384DigestTest.java
new file mode 100644
index 000000000..5b8af467f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA384DigestTest.java
@@ -0,0 +1,54 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA384Digest;
+
+/**
+ * standard vector test for SHA-384 from FIPS Draft 180-2.
+ *
+ * Note, the first two vectors are _not_ from the draft, the last three are.
+ */
+public class SHA384DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
+ "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31",
+ "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7",
+ "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039"
+ };
+
+ static private String million_a_digest = "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985";
+
+ SHA384DigestTest()
+ {
+ super(new SHA384Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA384Digest((SHA384Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA384DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA384HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA384HMacTest.java
new file mode 100644
index 000000000..771524710
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA384HMacTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * SHA384 HMac Test
+ */
+public class SHA384HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6",
+ "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649",
+ "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27",
+ "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb",
+ "3abf34c3503b2a23a46efc619baef897f4c8e42c934ce55ccbae9740fcbc1af4ca62269e2a37cd88ba926341efe4aeea",
+ "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952",
+ "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA384HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA384Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA384HMacTest test = new SHA384HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA3DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA3DigestTest.java
new file mode 100644
index 000000000..eb636e2aa
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA3DigestTest.java
@@ -0,0 +1,363 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.digests.SHA3Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * SHA3 Digest Test
+ */
+public class SHA3DigestTest
+ 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");
+
+ SHA3DigestTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "SHA3";
+ }
+
+ 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("sha3 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("sha3 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("sha3 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("sha3 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("sha3 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("sha3 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("sha3 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("sha3 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()
+ {
+ testDigest(new SHA3Digest(), digests288);
+ testDigest(new SHA3Digest(224), digests224);
+ testDigest(new SHA3Digest(256), digests256);
+ testDigest(new SHA3Digest(384), digests384);
+ testDigest(new SHA3Digest(512), digests512);
+
+ testMac(new SHA3Digest(224), macKeys, macData, mac224, trunc224);
+ testMac(new SHA3Digest(256), macKeys, macData, mac256, trunc256);
+ testMac(new SHA3Digest(384), macKeys, macData, mac384, trunc384);
+ testMac(new SHA3Digest(512), macKeys, macData, mac512, trunc512);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA3Digest((SHA3Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA3DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512DigestTest.java
new file mode 100644
index 000000000..6e9e6272f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512DigestTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * standard vector test for SHA-512 from FIPS Draft 180-2.
+ *
+ * Note, the first two vectors are _not_ from the draft, the last three are.
+ */
+public class SHA512DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+ "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
+ "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+ "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b";
+
+ SHA512DigestTest()
+ {
+ super(new SHA512Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA512Digest((SHA512Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA512DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512HMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512HMacTest.java
new file mode 100644
index 000000000..c4b4d66ae
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512HMacTest.java
@@ -0,0 +1,108 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.Test;
+import org.spongycastle.util.test.TestResult;
+
+/**
+ * SHA512 HMac Test
+ */
+public class SHA512HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
+ "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
+ "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb",
+ "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd",
+ "415fad6271580a531d4179bc891d87a650188707922a4fbb36663a1eb16da008711c5b50ddd0fc235084eb9d3364a1454fb2ef67cd1d29fe6773068ea266e96b",
+ "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598",
+ "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA512HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA512Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA512HMacTest test = new SHA512HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512t224DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512t224DigestTest.java
new file mode 100644
index 000000000..c2f75969b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512t224DigestTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA512tDigest;
+
+/**
+ * standard vector test for SHA-512/224 from FIPS 180-4.
+ *
+ * Note, only the last 2 message entries are FIPS originated..
+ */
+public class SHA512t224DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4",
+ "d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327",
+ "4634270F707B6A54DAAE7530460842E20E37ED265CEEE9A43E8924AA",
+ "23FEC5BB94D60B23308192640B0C453335D664734FE40E7268674AF9"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287";
+
+ SHA512t224DigestTest()
+ {
+ super(new SHA512tDigest(224), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA512tDigest((SHA512tDigest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA512t224DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512t256DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512t256DigestTest.java
new file mode 100644
index 000000000..ee6f13efb
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SHA512t256DigestTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA512tDigest;
+
+/**
+ * standard vector test for SHA-512/256 from FIPS 180-4.
+ *
+ * Note, only the last 2 message entries are FIPS originated..
+ */
+public class SHA512t256DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a",
+ "455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8",
+ "53048E2681941EF99B2E29B76B4C7DABE4C2D0C634FC6D46E0E2F13107E7AF23",
+ "3928E184FB8690F840DA3988121D31BE65CB9D3EF83EE6146FEAC861E19B563A"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21";
+
+ SHA512t256DigestTest()
+ {
+ super(new SHA512tDigest(256), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA512tDigest((SHA512tDigest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA512t256DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SM3DigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SM3DigestTest.java
new file mode 100644
index 000000000..df0bec325
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SM3DigestTest.java
@@ -0,0 +1,57 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SM3Digest;
+
+/**
+ * standard vector test for SM3 digest from chinese specification
+ */
+public class SM3DigestTest
+ extends DigestTest
+{
+ private static String[] messages = {
+ // Standard test vectors
+ "abc",
+ "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
+ // Non-standard test vectors
+ "",
+ "a",
+ "abcdefghijklmnopqrstuvwxyz",
+ };
+
+ private static String[] digests = {
+ // Standard test vectors
+ "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0",
+ "debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732",
+ // Non-standard test vectors
+ "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b",
+ "623476ac18f65a2909e43c7fec61b49c7e764a91a18ccb82f1917a29c86c5e88",
+ "b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595",
+ };
+
+ final static String sixtyFourKdigest = "97049bdc8f0736bc7300eafa9980aeb9cf00f24f7ec3a8f1f8884954d7655c1d";
+ final static String million_a_digest = "c8aaf89429554029e231941a2acc0ad61ff2a5acd8fadd25847a3a732b3b02c3";
+
+ SM3DigestTest()
+ {
+ super(new SM3Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ sixtyFourKTest(sixtyFourKdigest);
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SM3Digest((SM3Digest)digest);
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SM3DigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SRP6Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SRP6Test.java
new file mode 100644
index 000000000..c2ab0ad82
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SRP6Test.java
@@ -0,0 +1,276 @@
+package org.spongycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.agreement.srp.SRP6Client;
+import org.spongycastle.crypto.agreement.srp.SRP6Server;
+import org.spongycastle.crypto.agreement.srp.SRP6Util;
+import org.spongycastle.crypto.agreement.srp.SRP6VerifierGenerator;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.generators.DHParametersGenerator;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SRP6Test extends SimpleTest
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private static BigInteger fromHex(String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
+
+ // 1024 bit example prime from RFC5054 and corresponding generator
+ private static final BigInteger N_1024 = fromHex("EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
+ + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
+ + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
+ + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A"
+ + "FD5138FE8376435B9FC61D2FC0EB06E3");
+ private static final BigInteger g_1024 = BigInteger.valueOf(2);
+
+ private final SecureRandom random = new SecureRandom();
+
+ public String getName()
+ {
+ return "SRP6";
+ }
+
+ public void performTest() throws Exception
+ {
+ rfc5054AppendixBTestVectors();
+
+ testMutualVerification(N_1024, g_1024);
+ testClientCatchesBadB(N_1024, g_1024);
+ testServerCatchesBadA(N_1024, g_1024);
+
+ testWithRandomParams(256);
+ testWithRandomParams(384);
+ testWithRandomParams(512);
+ }
+
+ private void rfc5054AppendixBTestVectors() throws Exception
+ {
+ byte[] I = "alice".getBytes("UTF8");
+ byte[] P = "password123".getBytes("UTF8");
+ byte[] s = Hex.decode("BEB25379D1A8581EB5A727673A2441EE");
+ BigInteger N = N_1024;
+ BigInteger g = g_1024;
+ BigInteger a = fromHex("60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DDDA2D4393");
+ BigInteger b = fromHex("E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D105284D20");
+
+ BigInteger expect_k = fromHex("7556AA045AEF2CDD07ABAF0F665C3E818913186F");
+ BigInteger expect_x = fromHex("94B7555AABE9127CC58CCF4993DB6CF84D16C124");
+ BigInteger expect_v = fromHex("7E273DE8696FFC4F4E337D05B4B375BEB0DDE1569E8FA00A9886D812"
+ + "9BADA1F1822223CA1A605B530E379BA4729FDC59F105B4787E5186F5"
+ + "C671085A1447B52A48CF1970B4FB6F8400BBF4CEBFBB168152E08AB5"
+ + "EA53D15C1AFF87B2B9DA6E04E058AD51CC72BFC9033B564E26480D78"
+ + "E955A5E29E7AB245DB2BE315E2099AFB");
+ BigInteger expect_A = fromHex("61D5E490F6F1B79547B0704C436F523DD0E560F0C64115BB72557EC4"
+ + "4352E8903211C04692272D8B2D1A5358A2CF1B6E0BFCF99F921530EC"
+ + "8E39356179EAE45E42BA92AEACED825171E1E8B9AF6D9C03E1327F44"
+ + "BE087EF06530E69F66615261EEF54073CA11CF5858F0EDFDFE15EFEA"
+ + "B349EF5D76988A3672FAC47B0769447B");
+ BigInteger expect_B = fromHex("BD0C61512C692C0CB6D041FA01BB152D4916A1E77AF46AE105393011"
+ + "BAF38964DC46A0670DD125B95A981652236F99D9B681CBF87837EC99"
+ + "6C6DA04453728610D0C6DDB58B318885D7D82C7F8DEB75CE7BD4FBAA"
+ + "37089E6F9C6059F388838E7A00030B331EB76840910440B1B27AAEAE"
+ + "EB4012B7D7665238A8E3FB004B117B58");
+ BigInteger expect_u = fromHex("CE38B9593487DA98554ED47D70A7AE5F462EF019");
+ BigInteger expect_S = fromHex("B0DC82BABCF30674AE450C0287745E7990A3381F63B387AAF271A10D"
+ + "233861E359B48220F7C4693C9AE12B0A6F67809F0876E2D013800D6C"
+ + "41BB59B6D5979B5C00A172B4A2A5903A0BDCAF8A709585EB2AFAFA8F"
+ + "3499B200210DCC1F10EB33943CD67FC88A2F39A4BE5BEC4EC0A3212D"
+ + "C346D7E474B29EDE8A469FFECA686E5A");
+
+ BigInteger k = SRP6Util.calculateK(new SHA1Digest(), N, g);
+ if (!k.equals(expect_k))
+ {
+ fail("wrong value of 'k'");
+ }
+
+ BigInteger x = SRP6Util.calculateX(new SHA1Digest(), N, s, I, P);
+ if (!x.equals(expect_x))
+ {
+ fail("wrong value of 'x'");
+ }
+
+ SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
+ gen.init(N, g, new SHA1Digest());
+ BigInteger v = gen.generateVerifier(s, I, P);
+ if (!v.equals(expect_v))
+ {
+ fail("wrong value of 'v'");
+ }
+
+ final BigInteger aVal = a;
+ SRP6Client client = new SRP6Client()
+ {
+ protected BigInteger selectPrivateValue()
+ {
+ return aVal;
+ }
+ };
+ client.init(N, g, new SHA1Digest(), random);
+
+ BigInteger A = client.generateClientCredentials(s, I, P);
+ if (!A.equals(expect_A))
+ {
+ fail("wrong value of 'A'");
+ }
+
+ final BigInteger bVal = b;
+ SRP6Server server = new SRP6Server()
+ {
+ protected BigInteger selectPrivateValue()
+ {
+ return bVal;
+ }
+ };
+ server.init(N, g, v, new SHA1Digest(), random);
+
+ BigInteger B = server.generateServerCredentials();
+ if (!B.equals(expect_B))
+ {
+ fail("wrong value of 'B'");
+ }
+
+ BigInteger u = SRP6Util.calculateU(new SHA1Digest(), N, A, B);
+ if (!u.equals(expect_u))
+ {
+ fail("wrong value of 'u'");
+ }
+
+ BigInteger clientS = client.calculateSecret(B);
+ if (!clientS.equals(expect_S))
+ {
+ fail("wrong value of 'S' (client)");
+ }
+
+ BigInteger serverS = server.calculateSecret(A);
+ if (!serverS.equals(expect_S))
+ {
+ fail("wrong value of 'S' (server)");
+ }
+ }
+
+ private void testWithRandomParams(int bits) throws CryptoException
+ {
+ DHParametersGenerator paramGen = new DHParametersGenerator();
+ paramGen.init(bits, 25, random);
+ DHParameters parameters = paramGen.generateParameters();
+
+ BigInteger g = parameters.getG();
+ BigInteger p = parameters.getP();
+
+ testMutualVerification(p, g);
+ }
+
+ private void testMutualVerification(BigInteger N, BigInteger g) throws CryptoException
+ {
+ byte[] I = "username".getBytes();
+ byte[] P = "password".getBytes();
+ byte[] s = new byte[16];
+ random.nextBytes(s);
+
+ SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
+ gen.init(N, g, new SHA256Digest());
+ BigInteger v = gen.generateVerifier(s, I, P);
+
+ SRP6Client client = new SRP6Client();
+ client.init(N, g, new SHA256Digest(), random);
+
+ SRP6Server server = new SRP6Server();
+ server.init(N, g, v, new SHA256Digest(), random);
+
+ BigInteger A = client.generateClientCredentials(s, I, P);
+ BigInteger B = server.generateServerCredentials();
+
+ BigInteger clientS = client.calculateSecret(B);
+ BigInteger serverS = server.calculateSecret(A);
+
+ if (!clientS.equals(serverS))
+ {
+ fail("SRP agreement failed - client/server calculated different secrets");
+ }
+ }
+
+ private void testClientCatchesBadB(BigInteger N, BigInteger g)
+ {
+ byte[] I = "username".getBytes();
+ byte[] P = "password".getBytes();
+ byte[] s = new byte[16];
+ random.nextBytes(s);
+
+ SRP6Client client = new SRP6Client();
+ client.init(N, g, new SHA256Digest(), random);
+
+ client.generateClientCredentials(s, I, P);
+
+ try
+ {
+ client.calculateSecret(ZERO);
+ fail("Client failed to detect invalid value for 'B'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ client.calculateSecret(N);
+ fail("Client failed to detect invalid value for 'B'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+ }
+
+ private void testServerCatchesBadA(BigInteger N, BigInteger g)
+ {
+ byte[] I = "username".getBytes();
+ byte[] P = "password".getBytes();
+ byte[] s = new byte[16];
+ random.nextBytes(s);
+
+ SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
+ gen.init(N, g, new SHA256Digest());
+ BigInteger v = gen.generateVerifier(s, I, P);
+
+ SRP6Server server = new SRP6Server();
+ server.init(N, g, v, new SHA256Digest(), random);
+
+ server.generateServerCredentials();
+
+ try
+ {
+ server.calculateSecret(ZERO);
+ fail("Client failed to detect invalid value for 'A'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ server.calculateSecret(N);
+ fail("Client failed to detect invalid value for 'A'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SRP6Test());
+ }
+}
+
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Salsa20Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Salsa20Test.java
new file mode 100644
index 000000000..1fc90b849
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Salsa20Test.java
@@ -0,0 +1,270 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.Salsa20Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Salsa20 Test
+ */
+public class Salsa20Test
+ extends SimpleTest
+{
+ byte[] zeroes = Hex.decode(
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000");
+
+ String set1v0_0 = "4DFA5E481DA23EA09A31022050859936"
+ + "DA52FCEE218005164F267CB65F5CFD7F"
+ + "2B4F97E0FF16924A52DF269515110A07"
+ + "F9E460BC65EF95DA58F740B7D1DBB0AA";
+
+ String set1v0_192 = "DA9C1581F429E0A00F7D67E23B730676"
+ + "783B262E8EB43A25F55FB90B3E753AEF"
+ + "8C6713EC66C51881111593CCB3E8CB8F"
+ + "8DE124080501EEEB389C4BCB6977CF95";
+
+ String set1v0_256 = "7D5789631EB4554400E1E025935DFA7B"
+ + "3E9039D61BDC58A8697D36815BF1985C"
+ + "EFDF7AE112E5BB81E37ECF0616CE7147"
+ + "FC08A93A367E08631F23C03B00A8DA2F";
+
+ String set1v0_448 = "B375703739DACED4DD4059FD71C3C47F"
+ + "C2F9939670FAD4A46066ADCC6A564578"
+ + "3308B90FFB72BE04A6B147CBE38CC0C3"
+ + "B9267C296A92A7C69873F9F263BE9703";
+
+ String set1v9_0 = "0471076057830FB99202291177FBFE5D"
+ + "38C888944DF8917CAB82788B91B53D1C"
+ + "FB06D07A304B18BB763F888A61BB6B75"
+ + "5CD58BEC9C4CFB7569CB91862E79C459";
+
+ String set1v9_192 = "D1D7E97556426E6CFC21312AE3811425"
+ + "9E5A6FB10DACBD88E4354B0472556935"
+ + "2B6DA5ACAFACD5E266F9575C2ED8E6F2"
+ + "EFE4B4D36114C3A623DD49F4794F865B";
+
+ String set1v9_256 = "AF06FAA82C73291231E1BD916A773DE1"
+ + "52FD2126C40A10C3A6EB40F22834B8CC"
+ + "68BD5C6DBD7FC1EC8F34165C517C0B63"
+ + "9DB0C60506D3606906B8463AA0D0EC2F";
+
+ String set1v9_448 = "AB3216F1216379EFD5EC589510B8FD35"
+ + "014D0AA0B613040BAE63ECAB90A9AF79"
+ + "661F8DA2F853A5204B0F8E72E9D9EB4D"
+ + "BA5A4690E73A4D25F61EE7295215140C";
+
+ String set6v0_0 = "F5FAD53F79F9DF58C4AEA0D0ED9A9601"
+ + "F278112CA7180D565B420A48019670EA"
+ + "F24CE493A86263F677B46ACE1924773D"
+ + "2BB25571E1AA8593758FC382B1280B71";
+
+ String set6v0_65472 = "B70C50139C63332EF6E77AC54338A407"
+ + "9B82BEC9F9A403DFEA821B83F7860791"
+ + "650EF1B2489D0590B1DE772EEDA4E3BC"
+ + "D60FA7CE9CD623D9D2FD5758B8653E70";
+
+ String set6v0_65536 = "81582C65D7562B80AEC2F1A673A9D01C"
+ + "9F892A23D4919F6AB47B9154E08E699B"
+ + "4117D7C666477B60F8391481682F5D95"
+ + "D96623DBC489D88DAA6956B9F0646B6E";
+
+ String set6v1_0 = "3944F6DC9F85B128083879FDF190F7DE"
+ + "E4053A07BC09896D51D0690BD4DA4AC1"
+ + "062F1E47D3D0716F80A9B4D85E6D6085"
+ + "EE06947601C85F1A27A2F76E45A6AA87";
+
+ String set6v1_65472 = "36E03B4B54B0B2E04D069E690082C8C5"
+ + "92DF56E633F5D8C7682A02A65ECD1371"
+ + "8CA4352AACCB0DA20ED6BBBA62E177F2"
+ + "10E3560E63BB822C4158CAA806A88C82";
+
+ String set6v1_65536 = "1B779E7A917C8C26039FFB23CF0EF8E0"
+ + "8A1A13B43ACDD9402CF5DF38501098DF"
+ + "C945A6CC69A6A17367BC03431A86B3ED"
+ + "04B0245B56379BF997E25800AD837D7D";
+
+ // Salsa20/12
+ String salsa12_set1v0_0 = "FC207DBFC76C5E1774961E7A5AAD0906"
+ + "9B2225AC1CE0FE7A0CE77003E7E5BDF8"
+ + "B31AF821000813E6C56B8C1771D6EE70"
+ + "39B2FBD0A68E8AD70A3944B677937897";
+
+ String salsa12_set1v0_192 = "4B62A4881FA1AF9560586510D5527ED4"
+ + "8A51ECAFA4DECEEBBDDC10E9918D44AB"
+ + "26B10C0A31ED242F146C72940C6E9C37"
+ + "53F641DA84E9F68B4F9E76B6C48CA5AC";
+
+ String salsa12_set1v0_256 = "F52383D9DEFB20810325F7AEC9EADE34"
+ + "D9D883FEE37E05F74BF40875B2D0BE79"
+ + "ED8886E5BFF556CEA8D1D9E86B1F68A9"
+ + "64598C34F177F8163E271B8D2FEB5996";
+
+ String salsa12_set1v0_448 = "A52ED8C37014B10EC0AA8E05B5CEEE12"
+ + "3A1017557FB3B15C53E6C5EA8300BF74"
+ + "264A73B5315DC821AD2CAB0F3BB2F152"
+ + "BDAEA3AEE97BA04B8E72A7B40DCC6BA4";
+
+ // Salsa20/8
+ String salsa8_set1v0_0 = "A9C9F888AB552A2D1BBFF9F36BEBEB33"
+ + "7A8B4B107C75B63BAE26CB9A235BBA9D"
+ + "784F38BEFC3ADF4CD3E266687EA7B9F0"
+ + "9BA650AE81EAC6063AE31FF12218DDC5";
+
+ String salsa8_set1v0_192 = "BB5B6BB2CC8B8A0222DCCC1753ED4AEB"
+ + "23377ACCBD5D4C0B69A8A03BB115EF71"
+ + "871BC10559080ACA7C68F0DEF32A80DD"
+ + "BAF497259BB76A3853A7183B51CC4B9F";
+
+ String salsa8_set1v0_256 = "4436CDC0BE39559F5E5A6B79FBDB2CAE"
+ + "4782910F27FFC2391E05CFC78D601AD8"
+ + "CD7D87B074169361D997D1BED9729C0D"
+ + "EB23418E0646B7997C06AA84E7640CE3";
+
+ String salsa8_set1v0_448 = "BEE85903BEA506B05FC04795836FAAAC"
+ + "7F93F785D473EB762576D96B4A65FFE4"
+ + "63B34AAE696777FC6351B67C3753B89B"
+ + "A6B197BD655D1D9CA86E067F4D770220";
+
+
+ public String getName()
+ {
+ return "Salsa20";
+ }
+
+ public void performTest()
+ {
+ salsa20Test1(20, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v0_0, set1v0_192, set1v0_256, set1v0_448);
+ salsa20Test1(20, new ParametersWithIV(new KeyParameter(Hex.decode("00400000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v9_0, set1v9_192, set1v9_256, set1v9_448);
+ salsa20Test1(12, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ salsa12_set1v0_0, salsa12_set1v0_192, salsa12_set1v0_256, salsa12_set1v0_448);
+ salsa20Test1(8, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ salsa8_set1v0_0, salsa8_set1v0_192, salsa8_set1v0_256, salsa8_set1v0_448);
+ salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE")),
+ set6v0_0, set6v0_65472, set6v0_65536);
+ salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.decode("167DE44BB21980E7")),
+ set6v1_0, set6v1_65472, set6v1_65536);
+ reinitBug();
+ }
+
+ private void salsa20Test1(int rounds, CipherParameters params, String v0, String v192, String v256, String v448)
+ {
+ StreamCipher salsa = new Salsa20Engine(rounds);
+ byte[] buf = new byte[64];
+
+ salsa.init(true, params);
+
+ for (int i = 0; i != 7; i++)
+ {
+ salsa.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0/" + rounds, v0, buf);
+ }
+ break;
+ case 3:
+ if (!areEqual(buf, Hex.decode(v192)))
+ {
+ mismatch("v192/" + rounds, v192, buf);
+ }
+ break;
+ case 4:
+ if (!areEqual(buf, Hex.decode(v256)))
+ {
+ mismatch("v256/" + rounds, v256, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ buf[i] = salsa.returnByte(zeroes[i]);
+ }
+
+ if (!areEqual(buf, Hex.decode(v448)))
+ {
+ mismatch("v448", v448, buf);
+ }
+ }
+
+ private void salsa20Test2(CipherParameters params, String v0, String v65472, String v65536)
+ {
+ StreamCipher salsa = new Salsa20Engine();
+ byte[] buf = new byte[64];
+
+ salsa.init(true, params);
+
+ for (int i = 0; i != 1025; i++)
+ {
+ salsa.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0", v0, buf);
+ }
+ break;
+ case 1023:
+ if (!areEqual(buf, Hex.decode(v65472)))
+ {
+ mismatch("v65472", v65472, buf);
+ }
+ break;
+ case 1024:
+ if (!areEqual(buf, Hex.decode(v65536)))
+ {
+ mismatch("v65536", v65536, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+
+ private void reinitBug()
+ {
+ KeyParameter key = new KeyParameter(Hex.decode("80000000000000000000000000000000"));
+ ParametersWithIV parameters = new ParametersWithIV(key, Hex.decode("0000000000000000"));
+
+ StreamCipher salsa = new Salsa20Engine();
+
+ salsa.init(true, parameters);
+
+ try
+ {
+ salsa.init(true, key);
+ fail("Salsa20 should throw exception if no IV in Init");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Salsa20Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SerpentTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SerpentTest.java
new file mode 100644
index 000000000..bf687980e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SerpentTest.java
@@ -0,0 +1,103 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.SerpentEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ */
+public class SerpentTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000000", "8910494504181950f98dd998a82b6749"),
+ new BlockCipherVectorTest(1, new SerpentEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "10b5ffb720b8cb9002a1142b0ba2e94a"),
+ new BlockCipherVectorTest(2, new SerpentEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000008000000000000000000000", "4f057a42d8d5bd9746e434680ddcd5e5"),
+ new BlockCipherVectorTest(3, new SerpentEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000400000000000", "99407bf8582ef12550886ef5b6f169b9"),
+ new BlockCipherVectorTest(4, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "40000000000000000000000000000000", "d522a3b8d6d89d4d2a124fdd88f36896"),
+ new BlockCipherVectorTest(5, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000200000000000000000000", "189b8ec3470085b3da97e82ca8964e32"),
+ new BlockCipherVectorTest(6, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000000000000008000000000", "f77d868cf760b9143a89809510ccb099"),
+ new BlockCipherVectorTest(7, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "08000000000000000000000000000000", "d43b7b981b829342fce0e3ec6f5f4c82"),
+ new BlockCipherVectorTest(8, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000100000000000000", "0bf30e1a0c33ccf6d5293177886912a7"),
+ new BlockCipherVectorTest(9, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000001", "6a7f3b805d2ddcba49b89770ade5e507"),
+ new BlockCipherVectorTest(10, new SerpentEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "49afbfad9d5a34052cd8ffa5986bd2dd"),
+ new BlockCipherVectorTest(11, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000004000000000000000000000")),
+ "00000000000000000000000000000000", "ba8829b1de058c4b48615d851fc74f17"),
+ new BlockCipherVectorTest(12, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000100000000")),
+ "00000000000000000000000000000000", "89f64377bf1e8a46c8247044e8056a98"),
+/*
+ new BlockCipherMonteCarloTest(13, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "003380e19f10065740394f48e2fe80b7"),
+*/
+ new BlockCipherMonteCarloTest(13, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "4db75303d815c2f7cc6ca935d1c5a046"),
+/*
+ new BlockCipherMonteCarloTest(14, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "afb5425426906db26b70bdf842ac5400"),
+*/
+ new BlockCipherMonteCarloTest(14, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "fc53a50f4d3bc9836001893d2f41742d"),
+/*
+ new BlockCipherMonteCarloTest(15, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "1949d506ada7de1f1344986e8ea049b2"),
+*/
+ new BlockCipherMonteCarloTest(15, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "77117e6a9e80f40b2a36b7d755573c2d"),
+/*
+ new BlockCipherMonteCarloTest(16, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "e57f45559027cb1f2ed9603d814e1c34"),
+*/
+ new BlockCipherMonteCarloTest(16, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "dcd7f13ea0dcdfd0139d1a42e2ffb84b")
+ };
+
+ SerpentTest()
+ {
+ super(tests, new SerpentEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Serpent";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SerpentTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Shacal2Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Shacal2Test.java
new file mode 100644
index 000000000..5ffd94734
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Shacal2Test.java
@@ -0,0 +1,200 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.engines.Shacal2Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Shacal2 tester - vectors from https://www.cosic.esat.kuleuven.be/nessie/testvectors/
+ */
+public class Shacal2Test
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ // set 8.0
+ new BlockCipherVectorTest(0, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F" +
+ "101112131415161718191A1B1C1D1E1F" +
+ "202122232425262728292A2B2C2D2E2F" +
+ "303132333435363738393A3B3C3D3E3F")),
+ "98BCC10405AB0BFC686BECECAAD01AC1" +
+ "9B452511BCEB9CB094F905C51CA45430",
+ "00112233445566778899AABBCCDDEEFF" +
+ "102132435465768798A9BACBDCEDFE0F"),
+ // set 8.1
+ new BlockCipherVectorTest(1, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("2BD6459F82C5B300952C49104881FF48" +
+ "2BD6459F82C5B300952C49104881FF48" +
+ "2BD6459F82C5B300952C49104881FF48" +
+ "2BD6459F82C5B300952C49104881FF48")),
+ "481F122A75F2C4C3395140B5A951EBBA" +
+ "06D96BDFD9D8FF4FB59CBD1287808D5A",
+ "EA024714AD5C4D84EA024714AD5C4D84" +
+ "EA024714AD5C4D84EA024714AD5C4D84"),
+ // 7.255
+ new BlockCipherVectorTest(2, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")),
+ "94FEDFF2A0CFE3C983D340C88D73F8CF" +
+ "4B79FC581797EC10B27D4DA1B51E1BC7",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
+ // 7.100
+ new BlockCipherVectorTest(3, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("64646464646464646464646464646464" +
+ "64646464646464646464646464646464" +
+ "64646464646464646464646464646464" +
+ "64646464646464646464646464646464")),
+ "6643CB84B3B3F126F5E50959EF4CE73D" +
+ "B8500918ABE1056368DB06CA8C1C0D45",
+ "64646464646464646464646464646464" +
+ "64646464646464646464646464646464"),
+ // 7.50
+ new BlockCipherVectorTest(4, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("32323232323232323232323232323232" +
+ "32323232323232323232323232323232" +
+ "32323232323232323232323232323232" +
+ "32323232323232323232323232323232")),
+ "92E937285AB11FE3561542C43C918966" +
+ "971DE722E9B9D38BD69EAC77899DCF81",
+ "32323232323232323232323232323232" +
+ "32323232323232323232323232323232"),
+ // 7.0
+ new BlockCipherVectorTest(5, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "F8C9259FA4F5D787B570AFA9219166A6" +
+ "3636FC5C30AC289155D0CC4FFCB4B03D",
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ // 6.255
+ new BlockCipherVectorTest(6, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "F4E976DF0172CD961D4C8D466A12F676" +
+ "5B9089046E747CD2A41BF43C18A8328E",
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000001"),
+ // 6.100
+ new BlockCipherVectorTest(7, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "3B929F0597E21D0076EC399D21B67713" +
+ "B40E3AD559704219A26A3380212D5AD6",
+ "00000000000000000000000008000000" +
+ "00000000000000000000000000000000"),
+
+ // 6.0
+ new BlockCipherVectorTest(8, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "43A0DAD8307F19FBBCF166FE20BAC075" +
+ "C56FF14042550E472094B042BE5963EE",
+ "80000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ };
+
+ Shacal2Test()
+ {
+ super(tests, new Shacal2Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ // 1.0
+ iteratedTest(0,
+ Hex.decode("80000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("361AB6322FA9E7A7BB23818D839E01BD" +
+ "DAFDF47305426EDD297AEDB9F6202BAE"),
+ Hex.decode("226A582DE04383D0F3E7DE655DD848AC" +
+ "3E14CCFB4E76F7B7069879F67C4D5420"),
+ Hex.decode("B05D5A18C0712082CFF5BA9DBBCD7269" +
+ "114FC3DF83B42DAC306D95BBC473D839"));
+
+ // 1.100
+ iteratedTest(1,
+ Hex.decode("00000000000000000000000008000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("F703282E54592A5617E10618027BB67F" +
+ "639E43A90767150D8B7F5E83054B3CBD"),
+ Hex.decode("3B442692B579485B8BA2F92CE3B90DE7" +
+ "D2EA03D8B3C8E7BE7BF6415F798EED90"),
+ Hex.decode("331B9B65F06230380BBEECFBFBA94BCF" +
+ "92AF6341F815D7651F996144A5377263"));
+ }
+
+ private void iteratedTest(int index, byte[] key, byte[] plain, byte[] cipher, byte[] cipher100, byte[] cipher1000)
+ {
+ BlockCipher engine = new Shacal2Engine();
+
+ engine.init(true, new KeyParameter(key));
+
+ byte[] buf = new byte[plain.length];
+
+ System.arraycopy(plain, 0, buf, 0, plain.length);
+
+ engine.processBlock(buf, 0, buf, 0);
+
+ if (!Arrays.areEqual(cipher, buf))
+ {
+ fail(index + " single count failed");
+ }
+
+ for (int i = 1; i != 100; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!Arrays.areEqual(cipher100, buf))
+ {
+ fail(index + " 100 count failed");
+ }
+
+ for (int i = 100; i != 1000; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!Arrays.areEqual(cipher1000, buf))
+ {
+ fail(index + " 1000 count failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "Shacal2";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Shacal2Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ShortenedDigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ShortenedDigestTest.java
new file mode 100644
index 000000000..58b701490
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/ShortenedDigestTest.java
@@ -0,0 +1,89 @@
+/*
+ * Created on 6/05/2006
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.digests.ShortenedDigest;
+import org.spongycastle.util.test.SimpleTest;
+
+public class ShortenedDigestTest
+ extends SimpleTest
+{
+ public void performTest()
+ {
+ ExtendedDigest d = new SHA1Digest();
+ ShortenedDigest sd = new ShortenedDigest(new SHA1Digest(), 10);
+
+ if (sd.getDigestSize() != 10)
+ {
+ fail("size check wrong for SHA-1");
+ }
+
+ if (sd.getByteLength() != d.getByteLength())
+ {
+ fail("byte length check wrong for SHA-1");
+ }
+
+ //
+ // check output fits
+ //
+ sd.doFinal(new byte[10], 0);
+
+ d = new SHA512Digest();
+ sd = new ShortenedDigest(new SHA512Digest(), 20);
+
+ if (sd.getDigestSize() != 20)
+ {
+ fail("size check wrong for SHA-512");
+ }
+
+ if (sd.getByteLength() != d.getByteLength())
+ {
+ fail("byte length check wrong for SHA-512");
+ }
+
+ //
+ // check output fits
+ //
+ sd.doFinal(new byte[20], 0);
+
+ try
+ {
+ new ShortenedDigest(null, 20);
+
+ fail("null parameter not caught");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new ShortenedDigest(new SHA1Digest(), 50);
+
+ fail("short digest not caught");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public String getName()
+ {
+ return "ShortenedDigest";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ShortenedDigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SipHashTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SipHashTest.java
new file mode 100644
index 000000000..ba07cc36e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SipHashTest.java
@@ -0,0 +1,62 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.macs.SipHash;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.util.Pack;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/*
+ * SipHash test values from "SipHash: a fast short-input PRF", by Jean-Philippe
+ * Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf), Appendix A.
+ */
+public class SipHashTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "SipHash";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+
+ byte[] key = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] input = Hex.decode("000102030405060708090a0b0c0d0e");
+
+ long expected = 0xa129ca6149be45e5L;
+
+ SipHash mac = new SipHash();
+ mac.init(new KeyParameter(key));
+ mac.update(input, 0, input.length);
+
+ long result = mac.doFinal();
+ if (expected != result)
+ {
+ fail("Result does not match expected value for doFinal()");
+ }
+
+ byte[] expectedBytes = new byte[8];
+ Pack.longToLittleEndian(expected, expectedBytes, 0);
+
+ mac.update(input, 0, input.length);
+
+ byte[] output = new byte[mac.getMacSize()];
+ int len = mac.doFinal(output, 0);
+ if (len != output.length)
+ {
+ fail("Result length does not equal getMacSize() for doFinal(byte[],int)");
+ }
+ if (!areEqual(expectedBytes, output))
+ {
+ fail("Result does not match expected value for doFinal(byte[],int)");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SipHashTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkeinDigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkeinDigestTest.java
new file mode 100644
index 000000000..cb5c8648a
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkeinDigestTest.java
@@ -0,0 +1,294 @@
+package org.spongycastle.crypto.test;
+
+import java.io.IOException;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SkeinDigest;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Memoable;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SkeinDigestTest
+ extends SimpleTest
+{
+ private static class Case
+ {
+ private byte[] message;
+ private byte[] digest;
+ private int blockSize;
+ private int outputSize;
+
+ public Case(int blockSize, int outputSize, String message, String digest)
+ {
+ this.blockSize = blockSize;
+ this.outputSize = outputSize;
+ this.message = Hex.decode(message);
+ this.digest = Hex.decode(digest);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSize;
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ }
+
+ // Test cases from skein_golden_kat.txt and skein_golden_kat_short.txt in Skein 1.3 NIST CD
+ private static final Case[] TEST_CASES = {
+ new Case(256, 256, "", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba"),
+ new Case(256, 256, "fb", "088eb23cc2bccfb8171aa64e966d4af937325167dfcd170700ffd21f8a4cbdac"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "5c3002ff57a627089ea2f97a5000d5678416389019e80e45a3bbcab118315d26"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "640c894a4bba6574c83e920ddf7dd2982fc634881bbbcb9d774eae0a285e89ce"),
+ new Case(256, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1"),
+ new Case(256, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf"),
+ new Case(256, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "954620fb31e8b782a2794c6542827026fe069d715df04261629fcbe81d7d529b"
+ + "95ba021fa4239fb00afaa75f5fd8e78b"),
+ new Case(256, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "51347e27c7eabba514959f899a6715ef6ad5cf01c23170590e6a8af399470bf9"
+ + "0ea7409960a708c1dbaa90e86389df254abc763639bb8cdf7fb663b29d9557c3"),
+ new Case(256, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "6c9b6facbaf116b538aa655e0be0168084aa9f1be445f7e06714585e5999a6c9"
+ + "84fffa9d41a316028692d4aad18f573fbf27cf78e84de26da1928382b023987d"
+ + "cfe002b6201ea33713c54a8a5d9eb346f0365e04330d2faaf7bc8aba92a5d7fb"
+ + "6345c6fb26750bce65ab2045c233627679ac6e9acb33602e26fe3526063ecc8b"),
+
+ new Case(512, 512, "", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af4"
+ + "1fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a"),
+ new Case(512, 512, "fb", "c49e03d50b4b2cc46bd3b7ef7014c8a45b016399fd1714467b7596c86de98240"
+ + "e35bf7f9772b7d65465cd4cffab14e6bc154c54fc67b8bc340abf08eff572b9e"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "abefb179d52f68f86941acbbe014cc67ec66ad78b7ba9508eb1400ee2cbdb06f"
+ + "9fe7c2a260a0272d0d80e8ef5e8737c0c6a5f1c02ceb00fb2746f664b85fcef5"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "5c5b7956f9d973c0989aa40a71aa9c48a65af2757590e9a758343c7e23ea2df4"
+ + "057ce0b49f9514987feff97f648e1dd065926e2c371a0211ca977c213f14149f"),
+ new Case(512, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "ef03079d61b57c6047e15fa2b35b46fa24279539"),
+ new Case(512, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231"),
+ new Case(512, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648"),
+ new Case(512, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf"
+ + "93ac7074b3b19907a0665ba3a878b262"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcd"
+ + "de9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9"),
+
+ new Case(1024, 1024, "", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62"
+ + "645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297"
+ + "d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890"
+ + "bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6"),
+ new Case(1024, 1024, "fb", "6426bdc57b2771a6ef1b0dd39f8096a9a07554565743ac3de851d28258fcff22"
+ + "9993e11c4e6bebc8b6ecb0ad1b140276081aa390ec3875960336119427827473"
+ + "4770671b79f076771e2cfdaaf5adc9b10cbae43d8e6cd2b1c1f5d6c82dc96618"
+ + "00ddc476f25865b8748253173187d81da971c027d91d32fb390301c2110d2db2"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "140e93726ab0b0467c0b8a834ad8cda4d1769d273661902b70db0dcb5ee692ac"
+ + "b3f852d03b11f857850f2428432811309c1dcbe5724f00267ea3667e89fadb4e"
+ + "4911da6b0ba8a7eddf87c1c67152ef0f07b7fead3557318478bdef5ad1e5926d"
+ + "7071fdd4bfa5076d4b3253f8de479ebdf5357676f1641b2f097e9b785e9e528e"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "31105e1ef042c30b95b16e0f6e6a1a19172bb7d54a0597dd0c711194888efe1d"
+ + "bce82d47416df9577ca387219f06e45cd10964ff36f6711edbbea0e9595b0f66"
+ + "f72b755d70a46857e0aec98561a743d49370d8e572e212811273125f66cc30bf"
+ + "117d3221894c48012bf6e2219de91e064b01523517420a1e00f71c4cc04bab62"),
+ new Case(1024, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "2e6a4cbf2ef05ea9c24b93e8d1de732ddf2739eb"),
+ new Case(1024, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "1d6de19f37f7a3c265440eecb4b9fbd3300bb5ac60895cfc0d4d3c72"),
+ new Case(1024, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "986a4d472b123e8148731a8eac9db23325f0058c4ccbc44a5bb6fe3a8db672d7"),
+ new Case(1024, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62b"
+ + "c21a41f65896ddc6a04969654c2e2ce9"),
+ new Case(1024, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c2"
+ + "6baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6"
+ + "e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc3"
+ + "1c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25"
+ + "103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086"),
+
+ };
+
+ public String getName()
+ {
+ return "SkeinDigest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ runTest(TEST_CASES[7]);
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ Case test = TEST_CASES[i];
+ runTest(test);
+ }
+ }
+
+ private void runTest(Case dc)
+ {
+ SkeinDigest digest = new SkeinDigest(dc.getBlockSize(), dc.getOutputSize());
+
+ byte[] message = dc.getMessage();
+ digest.update(message, 0, message.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ if (!Arrays.areEqual(output, dc.getDigest()))
+ {
+ fail(digest.getAlgorithmName() + " message mismatch.\n Message " + new String(Hex.encode(dc.getMessage())),
+ new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ // Clone test
+ digest.update(message, 0, message.length / 2);
+
+ // clone the Digest
+ Digest d = new SkeinDigest(digest);
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing clone vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ d.update(message, message.length / 2, message.length - message.length / 2);
+ d.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing second clone vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ //
+ // memo test
+ //
+ Memoable m = (Memoable)digest;
+
+ digest.update(message, 0, message.length / 2);
+
+ // copy the Digest
+ Memoable copy1 = m.copy();
+ Memoable copy2 = copy1.copy();
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ m.reset(copy1);
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo reset vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ Digest md = (Digest)copy2;
+
+ md.update(message, message.length / 2, message.length - message.length / 2);
+ md.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo copy vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+ }
+
+ public static void main(String[] args)
+ throws IOException
+ {
+ // generateTests();
+ runTest(new SkeinDigestTest());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkeinMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkeinMacTest.java
new file mode 100644
index 000000000..3f1c616f6
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkeinMacTest.java
@@ -0,0 +1,162 @@
+package org.spongycastle.crypto.test;
+
+import java.io.IOException;
+
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.macs.SkeinMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class SkeinMacTest
+ extends SimpleTest
+{
+ private static class Case
+ {
+ private byte[] message;
+ private byte[] digest;
+ private byte[] key;
+ private int blockSize;
+ private int outputSize;
+
+ public Case(int blockSize, int outputSize, String message, String key, String digest)
+ {
+ this.blockSize = blockSize;
+ this.outputSize = outputSize;
+ this.message = Hex.decode(message);
+ this.key = Hex.decode(key);
+ this.digest = Hex.decode(digest);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSize;
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ public String toString()
+ {
+ return "new Case(" + blockSize + ", " + outputSize + ", \"" + new String(Hex.encode(message)) + "\", \""
+ + new String(Hex.encode(key)) + "\", \"" + new String(Hex.encode(digest)) + "\"";
+ }
+
+ }
+
+ // Test cases from skein_golden_kat.txt in Skein 1.3 NIST CD
+ // Excludes empty '(none)' key 'random+MAC' tests, which are in effect digest
+ private static final Case[] TEST_CASES = {
+ new Case(256, 256, "", "cb41f1706cde09651203c2d0efbaddf8", "886e4efefc15f06aa298963971d7a25398fffe5681c84db39bd00851f64ae29d"),
+ new Case(256, 256, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "979422a94e3afaa46664124d4e5e8b9422b1d8baf11c6ae6725992ac72a112ca"),
+ new Case(256, 256, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "1d658372cbea2f9928493cc47599d6f4ad8ce33536bedfa20b739f07516519d5"),
+ new Case(256, 256, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "41ef6b0f0fad81c040284f3b1a91e9c44e4c26a6d7207f3aac4362856ef12aca"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "ca8208119b9e4e4057631ab31015cfd256f6763a0a34381633d97f640899b84f"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "9e9980fcc16ee082cf164a5147d0e0692aeffe3dcb8d620e2bb542091162e2e9"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "c353a316558ec34f8245dd2f9c2c4961fbc7decc3b69053c103e4b8aaaf20394"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf8", "b1b8c18188e69a6ecae0b6018e6b638c6a91e6de6881e32a60858468c17b520d"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "1dfd2515a412e78852cd81a7f2167711b4ca19b2891c2ea36ba94f8451944793"),
+ new Case(256, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "a097340709b443ed2c0a921f5dcefef3ead65c4f0bcd5f13da54d7ed"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "ac1b4fab6561c92d0c487e082daec53e0db4f505e08bf51cae4fd5375e37fc04"),
+ new Case(256, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "96e6cebb23573d0a70ce36a67aa05d2403148093f25c695e1254887cc97f9771d2518413af4286bf2a06b61a53f7fcec"),
+ new Case(256, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "0e95e597e71d6350f20b99c4179f54f43a4722705c06ba765a82cb0a314fe2fe87ef8090063b757e53182706ed18737dadc0da1e1c66518f08334052702c5ed7"),
+ new Case(256, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "064abd4896f460b1953f5a357e7f7c5256e29cdb62b8740d0b52295cfa2ef4c7a2"),
+ new Case(256, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "edf220e43e048603bd16197d59b673b9974de5b8bcf7cb1558a4799f6fd3743eb5fb400cd6129afc0c60e7b741b7e5806f0e0b93eb8429fbc7efa222175a9c80fd"),
+ new Case(256, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "f3f59fb07399c7b73aae02a8590883cb2fdfde75c55654e71846522301bde48d267169adcc559e038e8c2f28faa552b550d51874055384adea93c036c71a1f0af0c7bcc3bc923738d5307b9da7cb423d4e615c629c4aba71f70d4c9d1fa008176825e51bfa0203445a4083947ec19f6a0fbd082b5b970f2396fb67420639410447"),
+ new Case(256, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "80eb80d9b8836b32fa576fc84ba08edfbdfd6979123d61914e610a70a372b37f560a10909484f9f4a377c93e29ba681dfe522c41dc83b5ee0567e5370007c7bbe4df0b2b4a25e088f80d72fc30734cdcd76d817b42fbd44dca881019afb25306f19d4e91848778af306517d2072cef72caa327e877c5b6554f83cec3d00877131b47c4d3b557f5a13541c4d5080ee3ce7a658993d083efd0db3496a8752060c3c8552f44b290cabdcc867f691ad605836c08dbd59c9528d885b600b85fdfc8a9d0e636ac3ad8b4295bcb0169e78dc358e77eacc8c4b61bddfa9e5f32d2268a006cfe05c57150fe8e68cabd21cf6cf6035aa1fe4db36c922b765aad0b64e82a2c37"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "8f88de68f03cd2f396ccdd49c3a0f4ff15bcda7eb357da9753f6116b124de91d"),
+ new Case(512, 512, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "9bd43d2a2fcfa92becb9f69faab3936978f1b865b7e44338fc9c8f16aba949ba340291082834a1fc5aa81649e13d50cd98641a1d0883062bfe2c16d1faa7e3aa"),
+ new Case(512, 512, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "f0c0a10f031c8fc69cfabcd54154c318b5d6cd95d06b12cf20264402492211ee010d5cecc2dc37fd772afac0596b2bf71e6020ef2dee7c860628b6e643ed9ff6"),
+ new Case(512, 512, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "0c1f1921253dd8e5c2d4c5f4099f851042d91147892705829161f5fc64d89785226eb6e187068493ee4c78a4b7c0f55a8cbbb1a5982c2daf638fc6a74b16b0d7"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "478d7b6c0cc6e35d9ebbdedf39128e5a36585db6222891692d1747d401de34ce3db6fcbab6c968b7f2620f4a844a2903b547775579993736d2493a75ff6752a1"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "13c170bac1de35e5fb843f65fabecf214a54a6e0458a4ff6ea5df91915468f4efcd371effa8965a9e82c5388d84730490dcf3976af157b8baf550655a5a6ab78"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "a947812529a72fd3b8967ec391b298bee891babc8487a1ec4ea3d88f6b2b5be09ac6a780f30f8e8c3bbb4f18bc302a28f3e87d170ba0f858a8fefe3487478cca"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "7690ba61f10e0bba312980b0212e6a9a51b0e9aadfde7ca535754a706e042335b29172aae29d8bad18efaf92d43e6406f3098e253f41f2931eda5911dc740352"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "d10e3ba81855ac087fbf5a3bc1f99b27d05f98ba22441138026225d34a418b93fd9e8dfaf5120757451adabe050d0eb59d271b0fe1bbf04badbcf9ba25a8791b"),
+ new Case(512, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "5670b226156570dff3efe16661ab86eb24982cdf"),
+ new Case(512, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "c41b9ff9753e6c0f8ed88866e320535e927fe4da552c289841a920db"),
+ new Case(512, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "dfbf5c1319a1d9d70efb2f1600fbcf694f935907f31d24a16d6cd2fb2d7855a769681766c0a29da778eed346cd1d740f"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "04d8cddb0ad931d54d195899a094684344e902286037272890bce98a41813edc37a3cee190a693fcca613ee30049ce7ec2bdff9613f56778a13f8c28a21d167a"),
+ new Case(512, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "08fca368b3b14ac406676adf37ac9be2dbb8704e694055a0c6331184d4f0070098f23f0963ee29002495771bf56fb4d3d9ff3506abcd80be927379f7880d5d7703919fbf92184f498ac44f47f015ce676eded9165d47d53733f5a27abbc05f45acd98b97cc15ffdced641defd1a5119ef841b452a1b8f94ee69004466ccdc143"),
+ new Case(512, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "669e770ebe7eacc2b64caaf049923ad297a5b37cfa61c283392d81ccfcb9bbbc09"),
+ new Case(512, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "acc2e03f07f33e9820a6038421089429adcd6a7a83f733beec048c05bf37531a170a5537fcb565c348a70a83217f8be768ff6f95fd2b3d89cb7d8a3dc849505e3710eb4e65a8e7134bbf580d92fe18c9aa987563669b1f014aa5e092519089355534eaa9f0bdc99f6839f54080ffe74623254c906ecb8896b4346c3178a0bc2898"),
+ new Case(512, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "9f3e082223c43090a4a3ffbdcd469cbabfe0c1399d1edf45a5dfc18f4db5428928a76e979b8d0d5dffec0e6a59ada448c1ffbc06cc80a2006f002adc0c6dbf458563762228dce4381944e460ebebfe06f1237093634625107469a22a189a47f8b025899265d8890a1b39df64552394377e88ba2ad44a8c8d174f884ac8c3ae24ddb0affca5fceb6aa76e09706881e8371774b9b050a69b96ef5e97e81043f8b7e9479e287ab441bacd62caf768a82c8c3e3107be70eb8799a39856fe29842a04e25de0ef9de1b7e65bd0f1f7306835287fc957388e2035b7d22d3aa9c06a9fefbca16f3f60e1c4def89038d918942152a069aa2e0be8ae7475d859031adec84583"),
+ new Case(1024, 1024, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "bcf37b3459c88959d6b6b58b2bfe142cef60c6f4ec56b0702480d7893a2b0595aa354e87102a788b61996b9cbc1eade7dafbf6581135572c09666d844c90f066b800fc4f5fd1737644894ef7d588afc5c38f5d920bdbd3b738aea3a3267d161ed65284d1f57da73b68817e17e381ca169115152b869c66b812bb9a84275303f0"),
+ new Case(1024, 1024, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "df0596e5808835a3e304aa27923db05f61dac57c0696a1d19abf188e70aa9dbcc659e9510f7c9a37fbc025bd4e5ea293e78ed7838dd0b08864e8ad40ddb3a88031ebefc21572a89960d1916107a7da7ac0c067e34ec46a86a29ca63fa250bd398eb32ec1ed0f8ac8329f26da018b029e41e2e58d1dfc44de81615e6c987ed9c9"),
+ new Case(1024, 1024, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "3cfbb79cd88af8ee09c7670bcbab6907a31f80fa31d9d7c9d50826c9568f307a78bd254961398c76b6e338fd9ca5f351059350d30963c3320659b223b991fc46d1307686fe2b4763d9f593c57ad5adbc45caf2ea3dc6090f5a74fa5fa6d9e9838964ea0a2aa216831ab069b00629a1a9b037083403bdb25d3d06a21c430c87dd"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "0a1b960099fc9d653b0fd1f5b6b972fb366907b772cbce5a59b6171d7935506f70c212bd169d68c5cfd8618343611b7eb2e686ff1dc7c03a57e1a55ed10726848161eea903d53b58459be42d95df989c66c2eea4e51cde272c2d8be67bf3bca2aee633777eb8486781eaa060d0f538abd6c93dbd2d1bf66e6f50bfdcac3725a4"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "3e0cd7938d71c39ffbb08a6ba7995ade3ad140e2c0c45cdbafb099247e08e4c20b61c1f885ced5ed2f816680925034918236e5807f0eecf3f27e9cfca36675eb75873efa1fb41f17541dc2f7c2469eaecb35cc7ca58e489804caf56f09fb97c9f689c64ad49c6888f86c483e901bd3d25798b394ef93faf9154900f92f31f433"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "7266752f7e9aa04bd7d8a1b16030677de6021301f6a62473c76bae2b98bbf8aad73bd00a4b5035f741caf2317ab80e4e97f5c5bbe8acc0e8b424bcb13c7c6740a985801fba54addde8d4f13f69d2bfc98ae104d46a211145217e51d510ea846cec9581d14fda079f775c8b18d66cb31bf7060996ee8a69eee7f107909ce59a97"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "71f40bf2aa635125ef83c8df0d4e9ea18b73b56be4f45e89b910a7c68d396b65b09d18abc7d1b6de3f53fd5de583e6f22e612dd17b292068af6027daaf8b4cd60acf5bc85044741e9f7a1f423f5827f5e360930a2e71912239af9fc6343604fdcf3f3569854f2bb8d25a81e3b3f5261a02fe8292aaaa50c324101ab2c7a2f349"),
+ new Case(1024, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "17c3c533b27d666da556ae586e641b7a3a0bcc45"),
+ new Case(1024, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "6625df9801581009125ea4e5c94ad6f1a2d692c278822ccb6eb67235"),
+ new Case(1024, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "6c5b671c1766f6eecea6d24b641d4a6bf84bba13a1976f8f80b3f30ee2f93de6"),
+ new Case(1024, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "98af454d7fa3706dfaafbf58c3f9944868b57f68f493987347a69fce19865febba0407a16b4e82065035651f0b1e0327"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "211ac479e9961141da3aac19d320a1dbbbfad55d2dce87e6a345fcd58e36827597378432b482d89bad44dddb13e6ad86e0ee1e0882b4eb0cd6a181e9685e18dd302ebb3aa74502c06254dcadfb2bd45d288f82366b7afc3bc0f6b1a3c2e8f84d37fbedd07a3f8fcff84faf24c53c11da600aaa118e76cfdcb366d0b3f7729dce"),
+ new Case(1024, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "dc1d253b7cadbdaef18503b1809a7f1d4f8c323b7f6f8ca50b76d3864649ce1c7d"),
+ new Case(1024, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "decd79578d12bf6806530c382230a2c7836429c70cac941179e1dd982938bab91fb6f3638df1cc1ef615ecfc4249e5aca8a73c4c1eebef662a836d0be903b00146"),
+ new Case(1024, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "440fe691e04f1fed8c253d6c4670646156f33fffaea702de9445df5739eb960cecf85d56e2e6860a610211a5c909932ab774b978aa0b0d5bbce82775172ab12dceddd51d1eb030057ce61bea6c18f6bb368d26ae76a9e44a962eb132e6c42c25d9fecc4f13348300ca55c78e0990de96c1ae24eb3ee3324782c93dd628260a2c8d"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "46a42b0d7b8679f8fcea156c072cf9833c468a7d59ac5e5d326957d60dfe1cdfb27eb54c760b9e049fda47f0b847ac68d6b340c02c39d4a18c1bdfece3f405fae8aa848bdbefe3a4c277a095e921228618d3be8bd1999a071682810de748440ad416a97742cc9e8a9b85455b1d76472cf562f525116698d5cd0a35ddf86e7f8a"),
+
+ };
+
+ public String getName()
+ {
+ return "SkeinMac";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ Case test = TEST_CASES[i];
+ runTest(test);
+ }
+ }
+
+ private void runTest(Case dc)
+ {
+ Mac digest = new SkeinMac(dc.getBlockSize(), dc.getOutputSize());
+ digest.init(new KeyParameter(dc.getKey()));
+
+ byte[] message = dc.getMessage();
+ digest.update(message, 0, message.length);
+
+ byte[] output = new byte[digest.getMacSize()];
+ digest.doFinal(output, 0);
+
+ if (!Arrays.areEqual(output, dc.getDigest()))
+ {
+ fail(digest.getAlgorithmName() + " message " + (dc.getMessage().length * 8) + " mismatch.\n Message " + new String(Hex.encode(dc.getMessage()))
+ + "\n Key " + new String(Hex.encode(dc.getKey())) + "\n Expected "
+ + new String(Hex.encode(dc.getDigest())) + "\n Actual " + new String(Hex.encode(output)));
+ }
+
+ }
+
+ public static void main(String[] args)
+ throws IOException
+ {
+ runTest(new SkeinMacTest());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkipjackTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkipjackTest.java
new file mode 100644
index 000000000..e7b2d8679
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/SkipjackTest.java
@@ -0,0 +1,35 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.SkipjackEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ */
+public class SkipjackTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new SkipjackEngine(),
+ new KeyParameter(Hex.decode("00998877665544332211")),
+ "33221100ddccbbaa", "2587cae27a12d300")
+ };
+
+ SkipjackTest()
+ {
+ super(tests, new SkipjackEngine(), new KeyParameter(Hex.decode("00998877665544332211")));
+ }
+
+ public String getName()
+ {
+ return "SKIPJACK";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SkipjackTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/StreamCipherResetTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/StreamCipherResetTest.java
new file mode 100644
index 000000000..7c166adab
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/StreamCipherResetTest.java
@@ -0,0 +1,133 @@
+package org.spongycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.engines.ChaChaEngine;
+import org.spongycastle.crypto.engines.Grain128Engine;
+import org.spongycastle.crypto.engines.Grainv1Engine;
+import org.spongycastle.crypto.engines.HC128Engine;
+import org.spongycastle.crypto.engines.HC256Engine;
+import org.spongycastle.crypto.engines.ISAACEngine;
+import org.spongycastle.crypto.engines.RC4Engine;
+import org.spongycastle.crypto.engines.Salsa20Engine;
+import org.spongycastle.crypto.engines.XSalsa20Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * Test whether block ciphers implement reset contract on init, encrypt/decrypt and reset.
+ */
+public class StreamCipherResetTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "Stream Cipher Reset";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testReset(new Salsa20Engine(), new Salsa20Engine(), new ParametersWithIV(new KeyParameter(random(32)),
+ random(8)));
+ testReset(new Salsa20Engine(), new Salsa20Engine(), new ParametersWithIV(new KeyParameter(random(16)),
+ random(8)));
+ testReset(new XSalsa20Engine(), new XSalsa20Engine(), new ParametersWithIV(new KeyParameter(random(32)),
+ random(24)));
+ testReset(new ChaChaEngine(), new ChaChaEngine(), new ParametersWithIV(new KeyParameter(random(32)), random(8)));
+ testReset(new ChaChaEngine(), new ChaChaEngine(), new ParametersWithIV(new KeyParameter(random(16)), random(8)));
+ testReset(new RC4Engine(), new RC4Engine(), new KeyParameter(random(16)));
+ testReset(new ISAACEngine(), new ISAACEngine(), new KeyParameter(random(16)));
+ testReset(new HC128Engine(), new HC128Engine(), new ParametersWithIV(new KeyParameter(random(16)), random(16)));
+ testReset(new HC256Engine(), new HC256Engine(), new ParametersWithIV(new KeyParameter(random(16)), random(16)));
+ testReset(new Grainv1Engine(), new Grainv1Engine(), new ParametersWithIV(new KeyParameter(random(16)),
+ random(8)));
+ testReset(new Grain128Engine(), new Grain128Engine(), new ParametersWithIV(new KeyParameter(random(16)),
+ random(12)));
+ }
+
+ private static final SecureRandom RAND = new SecureRandom();
+
+ private byte[] random(int size)
+ {
+ final byte[] data = new byte[size];
+ RAND.nextBytes(data);
+ return data;
+ }
+
+ private void testReset(StreamCipher cipher1, StreamCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ cipher1.init(true, params);
+
+ byte[] plaintext = new byte[1023];
+ byte[] ciphertext = new byte[plaintext.length];
+
+ // Establish baseline answer
+ cipher1.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
+
+ // Test encryption resets
+ checkReset(cipher1, params, true, plaintext, ciphertext);
+
+ // Test decryption resets with fresh instance
+ cipher2.init(false, params);
+ checkReset(cipher2, params, false, ciphertext, plaintext);
+ }
+
+ private void checkReset(StreamCipher cipher,
+ CipherParameters params,
+ boolean encrypt,
+ byte[] pretext,
+ byte[] posttext)
+ throws InvalidCipherTextException
+ {
+ // Do initial run
+ byte[] output = new byte[posttext.length];
+ cipher.processBytes(pretext, 0, pretext.length, output, 0);
+
+ // Check encrypt resets cipher
+ cipher.init(encrypt, params);
+
+ try
+ {
+ cipher.processBytes(pretext, 0, pretext.length, output, 0);
+ }
+ catch (Exception e)
+ {
+ fail(cipher.getAlgorithmName() + " init did not reset: " + e.getMessage());
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(cipher.getAlgorithmName() + " init did not reset.", new String(Hex.encode(posttext)),
+ new String(Hex.encode(output)));
+ }
+
+ // Check reset resets data
+ cipher.reset();
+
+ try
+ {
+ cipher.processBytes(pretext, 0, pretext.length, output, 0);
+ }
+ catch (Exception e)
+ {
+ fail(cipher.getAlgorithmName() + " reset did not reset: " + e.getMessage());
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(cipher.getAlgorithmName() + " reset did not reset.");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new StreamCipherResetTest());
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/StreamCipherVectorTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/StreamCipherVectorTest.java
new file mode 100644
index 000000000..2d3b197db
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/StreamCipherVectorTest.java
@@ -0,0 +1,62 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * a basic test that takes a stream cipher, key parameter, and an input
+ * and output string.
+ */
+public class StreamCipherVectorTest
+ extends SimpleTest
+{
+ int id;
+ StreamCipher cipher;
+ CipherParameters param;
+ byte[] input;
+ byte[] output;
+
+ public StreamCipherVectorTest(
+ int id,
+ StreamCipher cipher,
+ CipherParameters param,
+ String input,
+ String output)
+ {
+ this.id = id;
+ this.cipher = cipher;
+ this.param = param;
+ this.input = Hex.decode(input);
+ this.output = Hex.decode(output);
+ }
+
+ public String getName()
+ {
+ return cipher.getAlgorithmName() + " Vector Test " + id;
+ }
+
+ public void performTest()
+ {
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ cipher.processBytes(input, 0, input.length, out, 0);
+
+ if (!areEqual(out, output))
+ {
+ fail("failed.", new String(Hex.encode(output)) , new String(Hex.encode(out)));
+ }
+
+ cipher.init(false, param);
+
+ cipher.processBytes(output, 0, output.length, out, 0);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal");
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TEATest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TEATest.java
new file mode 100644
index 000000000..a2740600e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TEATest.java
@@ -0,0 +1,48 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.TEAEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * TEA tester - based on C implementation results from http://www.simonshepherd.supanet.com/tea.htm
+ */
+public class TEATest
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new TEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0000000000000000",
+ "41ea3a0a94baa940"),
+ new BlockCipherVectorTest(1, new TEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0102030405060708",
+ "6a2f9cf3fccf3c55"),
+ new BlockCipherVectorTest(2, new TEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0000000000000000",
+ "34e943b0900f5dcb"),
+ new BlockCipherVectorTest(3, new TEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0102030405060708",
+ "773dc179878a81c0"),
+ };
+
+ TEATest()
+ {
+ super(tests, new TEAEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "TEA";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TEATest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish1024Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish1024Test.java
new file mode 100644
index 000000000..9e087187d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish1024Test.java
@@ -0,0 +1,60 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.TweakableBlockCipherParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class Threefish1024Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[128]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "f05c3d0a3d05b304f785ddc7d1e036015c8aa76e2f217b06c6e1544c0bc1a90d" +
+ "f0accb9473c24e0fd54fea68057f43329cb454761d6df5cf7b2e9b3614fbd5a2" +
+ "0b2e4760b40603540d82eabc5482c171c832afbe68406bc39500367a592943fa" +
+ "9a5b4a43286ca3c4cf46104b443143d560a4b230488311df4feef7e1dfe8391e"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f" +
+ "303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f" +
+ "505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f" +
+ "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" +
+ "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0" +
+ "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a0" +
+ "9f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180",
+ "a6654ddbd73cc3b05dd777105aa849bce49372eaaffc5568d254771bab85531c" +
+ "94f780e7ffaae430d5d8af8c70eebbe1760f3b42b737a89cb363490d670314bd" +
+ "8aa41ee63c2e1f45fbd477922f8360b388d6125ea6c7af0ad7056d01796e90c8" +
+ "3313f4150a5716b30ed5f569288ae974ce2b4347926fce57de44512177dd7cde")
+ };
+
+ Threefish1024Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024), new KeyParameter(new byte[128]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-1024";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish1024Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish256Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish256Test.java
new file mode 100644
index 000000000..6753c2452
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish256Test.java
@@ -0,0 +1,45 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.TweakableBlockCipherParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class Threefish256Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[32]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "84da2a1f8beaee947066ae3e3103f1ad536db1f4a1192495116b9f3ce6133fd8"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0",
+ "e0d091ff0eea8fdfc98192e62ed80ad59d865d08588df476657056b5955e97df")
+ };
+
+ Threefish256Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-256";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish256Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish512Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish512Test.java
new file mode 100644
index 000000000..ba5447f46
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/Threefish512Test.java
@@ -0,0 +1,50 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.TweakableBlockCipherParameters;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class Threefish512Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[64]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b" +
+ "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f" +
+ "303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" +
+ "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0",
+ "e304439626d45a2cb401cad8d636249a6338330eb06d45dd8b36b90e97254779" +
+ "272a0a8d99463504784420ea18c9a725af11dffea10162348927673d5c1caf3d")
+ };
+
+ Threefish512Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), new KeyParameter(new byte[64]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-512";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish512Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TigerDigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TigerDigestTest.java
new file mode 100644
index 000000000..b9a40655d
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TigerDigestTest.java
@@ -0,0 +1,59 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.TigerDigest;
+
+/**
+ * Tiger Digest Test
+ */
+public class TigerDigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "abc",
+ "Tiger",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789",
+ "Tiger - A Fast New Hash Function, by Ross Anderson and Eli Biham, proceedings of Fast Software Encryption 3, Cambridge, 1996.",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"
+ };
+
+ final static String[] digests = {
+ "3293AC630C13F0245F92BBB1766E16167A4E58492DDE73F3",
+ "2AAB1484E8C158F2BFB8C5FF41B57A525129131C957B5F93",
+ "DD00230799F5009FEC6DEBC838BB6A27DF2B9D6F110C7937",
+ "F71C8583902AFB879EDFE610F82C0D4786A3A534504486B5",
+ "38F41D9D9A710A10C3727AC0DEEAA270727D9F926EC10139",
+ "48CEEB6308B87D46E95D656112CDF18D97915F9765658957",
+ "631ABDD103EB9A3D245B6DFD4D77B257FC7439501D1568DD",
+ "C54034E5B43EB8005848A7E0AE6AAC76E4FF590AE715FD25",
+ "C54034E5B43EB8005848A7E0AE6AAC76E4FF590AE715FD25"
+ };
+
+ final static String hash64k = "FDF4F5B35139F48E710E421BE5AF411DE1A8AAC333F26204";
+
+ TigerDigestTest()
+ {
+ super(new TigerDigest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ sixtyFourKTest(hash64k);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new TigerDigest((TigerDigest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TigerDigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TwofishTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TwofishTest.java
new file mode 100644
index 000000000..84f3e7fcd
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/TwofishTest.java
@@ -0,0 +1,45 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.TwofishEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class TwofishTest
+ extends CipherTest
+{
+ static String key1 = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
+ static String key2 = "000102030405060708090a0b0c0d0e0f1011121314151617";
+ static String key3 = "000102030405060708090a0b0c0d0e0f";
+
+ static String input = "000102030405060708090A0B0C0D0E0F";
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new TwofishEngine(),
+ new KeyParameter(Hex.decode(key1)),
+ input, "8ef0272c42db838bcf7b07af0ec30f38"),
+ new BlockCipherVectorTest(1, new TwofishEngine(),
+ new KeyParameter(Hex.decode(key2)),
+ input, "95accc625366547617f8be4373d10cd7"),
+ new BlockCipherVectorTest(2, new TwofishEngine(),
+ new KeyParameter(Hex.decode(key3)),
+ input, "9fb63337151be9c71306d159ea7afaa4")
+ };
+
+ TwofishTest()
+ {
+ super(tests, new TwofishEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Twofish";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TwofishTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCKSA3Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCKSA3Test.java
new file mode 100644
index 000000000..21000db93
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCKSA3Test.java
@@ -0,0 +1,97 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.engines.VMPCKSA3Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * VMPC Test
+ */
+public class VMPCKSA3Test extends SimpleTest
+{
+ private static final byte[] input = new byte[1000000];
+
+ public String getName()
+ {
+ return "VMPC-KSA3";
+ }
+
+ private void checkByte(byte[] array, int position, byte b)
+ {
+ if (array[position] != b)
+ {
+ fail("Fail on position " + position,
+ new String(Hex.encode(new byte[] { b })),
+ new String(Hex.encode(new byte[] { array[position] })));
+ }
+ }
+
+ public void performTest()
+ {
+ byte[] key = Hex.decode("9661410AB797D8A9EB767C21172DF6C7");
+ byte[] iv = Hex.decode("4B5C2F003E67F39557A8D26F3DA2B155");
+ CipherParameters kp = new KeyParameter(key);
+ CipherParameters kpwiv = new ParametersWithIV(kp, iv);
+
+ VMPCKSA3Engine engine = new VMPCKSA3Engine();
+
+ try
+ {
+ engine.init(true, kp);
+ fail("init failed to throw expected exception");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, kpwiv);
+ checkEngine(engine);
+
+ engine.reset();
+ byte[] output = checkEngine(engine);
+
+ engine.init(false, kpwiv);
+ byte[] recovered = new byte[output.length];
+ engine.processBytes(output, 0, output.length, recovered, 0);
+
+ if (!Arrays.areEqual(input, recovered))
+ {
+ fail("decrypted bytes differ from original bytes");
+ }
+ }
+
+ private byte[] checkEngine(VMPCKSA3Engine engine)
+ {
+ byte[] output = new byte[input.length];
+ engine.processBytes(input, 0, output.length, output, 0);
+
+ checkByte(output, 0, (byte) 0xB6);
+ checkByte(output, 1, (byte) 0xEB);
+ checkByte(output, 2, (byte) 0xAE);
+ checkByte(output, 3, (byte) 0xFE);
+ checkByte(output, 252, (byte) 0x48);
+ checkByte(output, 253, (byte) 0x17);
+ checkByte(output, 254, (byte) 0x24);
+ checkByte(output, 255, (byte) 0x73);
+ checkByte(output, 1020, (byte) 0x1D);
+ checkByte(output, 1021, (byte) 0xAE);
+ checkByte(output, 1022, (byte) 0xC3);
+ checkByte(output, 1023, (byte) 0x5A);
+ checkByte(output, 102396, (byte) 0x1D);
+ checkByte(output, 102397, (byte) 0xA7);
+ checkByte(output, 102398, (byte) 0xE1);
+ checkByte(output, 102399, (byte) 0xDC);
+
+ return output;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new VMPCKSA3Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCMacTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCMacTest.java
new file mode 100644
index 000000000..a8d840293
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCMacTest.java
@@ -0,0 +1,51 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.macs.VMPCMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class VMPCMacTest extends SimpleTest
+{
+ public String getName()
+ {
+ return "VMPC-MAC";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new VMPCMacTest());
+ }
+
+ static byte[] output1 = Hex.decode("9BDA16E2AD0E284774A3ACBC8835A8326C11FAAD");
+
+ public void performTest() throws Exception
+ {
+ CipherParameters kp = new KeyParameter(
+ Hex.decode("9661410AB797D8A9EB767C21172DF6C7"));
+ CipherParameters kpwiv = new ParametersWithIV(kp,
+ Hex.decode("4B5C2F003E67F39557A8D26F3DA2B155"));
+
+ byte[] m = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ m[i] = (byte) i;
+ }
+
+ VMPCMac mac = new VMPCMac();
+ mac.init(kpwiv);
+
+ mac.update(m, 0, m.length);
+
+ byte[] out = new byte[20];
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, output1))
+ {
+ fail("Fail", new String(Hex.encode(output1)), new String(Hex.encode(out)));
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCTest.java
new file mode 100644
index 000000000..8155ddccf
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/VMPCTest.java
@@ -0,0 +1,97 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.engines.VMPCEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * VMPC Test
+ */
+public class VMPCTest extends SimpleTest
+{
+ private static final byte[] input = new byte[1000000];
+
+ public String getName()
+ {
+ return "VMPC";
+ }
+
+ private void checkByte(byte[] array, int position, byte b)
+ {
+ if (array[position] != b)
+ {
+ fail("Fail on position " + position,
+ new String(Hex.encode(new byte[] { b })),
+ new String(Hex.encode(new byte[] { array[position] })));
+ }
+ }
+
+ public void performTest()
+ {
+ byte[] key = Hex.decode("9661410AB797D8A9EB767C21172DF6C7");
+ byte[] iv = Hex.decode("4B5C2F003E67F39557A8D26F3DA2B155");
+ CipherParameters kp = new KeyParameter(key);
+ CipherParameters kpwiv = new ParametersWithIV(kp, iv);
+
+ VMPCEngine engine = new VMPCEngine();
+
+ try
+ {
+ engine.init(true, kp);
+ fail("init failed to throw expected exception");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, kpwiv);
+ checkEngine(engine);
+
+ engine.reset();
+ byte[] output = checkEngine(engine);
+
+ engine.init(false, kpwiv);
+ byte[] recovered = new byte[output.length];
+ engine.processBytes(output, 0, output.length, recovered, 0);
+
+ if (!Arrays.areEqual(input, recovered))
+ {
+ fail("decrypted bytes differ from original bytes");
+ }
+ }
+
+ private byte[] checkEngine(VMPCEngine engine)
+ {
+ byte[] output = new byte[input.length];
+ engine.processBytes(input, 0, output.length, output, 0);
+
+ checkByte(output, 0, (byte) 0xA8);
+ checkByte(output, 1, (byte) 0x24);
+ checkByte(output, 2, (byte) 0x79);
+ checkByte(output, 3, (byte) 0xF5);
+ checkByte(output, 252, (byte) 0xB8);
+ checkByte(output, 253, (byte) 0xFC);
+ checkByte(output, 254, (byte) 0x66);
+ checkByte(output, 255, (byte) 0xA4);
+ checkByte(output, 1020, (byte) 0xE0);
+ checkByte(output, 1021, (byte) 0x56);
+ checkByte(output, 1022, (byte) 0x40);
+ checkByte(output, 1023, (byte) 0xA5);
+ checkByte(output, 102396, (byte) 0x81);
+ checkByte(output, 102397, (byte) 0xCA);
+ checkByte(output, 102398, (byte) 0x49);
+ checkByte(output, 102399, (byte) 0x9A);
+
+ return output;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new VMPCTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/WhirlpoolDigestTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/WhirlpoolDigestTest.java
new file mode 100644
index 000000000..806f5cc28
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/WhirlpoolDigestTest.java
@@ -0,0 +1,105 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.WhirlpoolDigest;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * ISO vector test for Whirlpool
+ *
+ */
+public class WhirlpoolDigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "abcdbcdecdefdefgefghfghighijhijk"
+ };
+
+ private static String[] digests =
+ {
+ "19FA61D75522A4669B44E39C1D2E1726C530232130D407F89AFEE0964997F7A73E83BE698B288FEBCF88E3E03C4F0757EA8964E59B63D93708B138CC42A66EB3",
+ "8ACA2602792AEC6F11A67206531FB7D7F0DFF59413145E6973C45001D0087B42D11BC645413AEFF63A42391A39145A591A92200D560195E53B478584FDAE231A",
+ "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5",
+ "378C84A4126E2DC6E56DCC7458377AAC838D00032230F53CE1F5700C0FFB4D3B8421557659EF55C106B4B52AC5A4AAA692ED920052838F3362E86DBD37A8903E",
+ "F1D754662636FFE92C82EBB9212A484A8D38631EAD4238F5442EE13B8054E41B08BF2A9251C30B6A0B8AAE86177AB4A6F68F673E7207865D5D9819A3DBA4EB3B",
+ "DC37E008CF9EE69BF11F00ED9ABA26901DD7C28CDEC066CC6AF42E40F82F3A1E08EBA26629129D8FB7CB57211B9281A65517CC879D7B962142C65F5A7AF01467",
+ "466EF18BABB0154D25B9D38A6414F5C08784372BCCB204D6549C4AFADB6014294D5BD8DF2A6C44E538CD047B2681A51A2C60481E88C5A20B2C2A80CF3A9A083B",
+ "2A987EA40F917061F5D6F0A0E4644F488A7A5A52DEEE656207C562F988E95C6916BDC8031BC5BE1B7B947639FE050B56939BAAA0ADFF9AE6745B7B181C3BE3FD"
+ };
+
+ WhirlpoolDigestTest()
+ {
+ super(new WhirlpoolDigest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new WhirlpoolDigest((WhirlpoolDigest)digest);
+ }
+
+ private static String _millionAResultVector = "0C99005BEB57EFF50A7CF005560DDF5D29057FD86B20BFD62DECA0F1CCEA4AF51FC15490EDDC47AF32BB2B66C34FF9AD8C6008AD677F77126953B226E4ED8B01";
+
+ private static String _thirtyOneZeros = "3E3F188F8FEBBEB17A933FEAF7FE53A4858D80C915AD6A1418F0318E68D49B4E459223CD414E0FBC8A57578FD755D86E827ABEF4070FC1503E25D99E382F72BA";
+
+ public String getName()
+ {
+ return "Whirlpool";
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ byte[] thirtyOneZeros = new byte[31];
+ performStandardVectorTest("31 zeroes test",
+ thirtyOneZeros, _thirtyOneZeros);
+
+ byte[] millionAInByteArray = new byte[1000000];
+ Arrays.fill(millionAInByteArray, (byte)'a');
+
+ performStandardVectorTest("Million 'a' test",
+ millionAInByteArray, _millionAResultVector);
+ }
+
+ private void performStandardVectorTest(String testTitle, byte[] inputBytes,
+ String resultsAsHex)
+ {
+ doPerformTest(testTitle, inputBytes, resultsAsHex);
+ }
+
+ private void doPerformTest(String testTitle, byte[] inputBytes, String resultsAsHex)
+ {
+ String resStr = createHexOutputFromDigest(inputBytes);
+ if (!resultsAsHex.equals(resStr.toUpperCase()))
+ {
+ fail(testTitle, resultsAsHex, resStr);
+ }
+ }
+
+ private String createHexOutputFromDigest(byte[] digestBytes)
+ {
+ String resStr;
+ Digest digest = new WhirlpoolDigest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+ digest.update(digestBytes, 0, digestBytes.length);
+ digest.doFinal(resBuf, 0);
+ resStr = new String(Hex.encode(resBuf));
+ return resStr;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new WhirlpoolDigestTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/XSalsa20Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/XSalsa20Test.java
new file mode 100644
index 000000000..6c0ab4864
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/XSalsa20Test.java
@@ -0,0 +1,166 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.XSalsa20Engine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class XSalsa20Test extends SimpleTest
+{
+ private static class TestCase
+ {
+
+ private byte[] key;
+ private byte[] iv;
+ private byte[] plaintext;
+ private byte[] ciphertext;
+
+ public TestCase(String key, String iv, String plaintext, String ciphertext)
+ {
+ this.key = Hex.decode(key);
+ this.iv = Hex.decode(iv);
+ this.plaintext = Hex.decode(plaintext);
+ this.ciphertext = Hex.decode(ciphertext);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getIv()
+ {
+ return iv;
+ }
+
+ public byte[] getPlaintext()
+ {
+ return plaintext;
+ }
+
+ public byte[] getCiphertext()
+ {
+ return ciphertext;
+ }
+ }
+
+ // Test cases generated by naclcrypto-20090308, as used by cryptopp
+ private static final TestCase[] TEST_CASES = new TestCase[] {
+ new TestCase(
+ "a6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff88030",
+ "9e645a74e9e0a60d8243acd9177ab51a1beb8d5a2f5d700c",
+ "093c5e5585579625337bd3ab619d615760d8c5b224a85b1d0efe0eb8a7ee163abb0376529fcc09bab506c618e13ce777d82c3ae9d1a6f972d4160287cbfe60bf2130fc0a6ff6049d0a5c8a82f429231f008082e845d7e189d37f9ed2b464e6b919e6523a8c1210bd52a02a4c3fe406d3085f5068d1909eeeca6369abc981a42e87fe665583f0ab85ae71f6f84f528e6b397af86f6917d9754b7320dbdc2fea81496f2732f532ac78c4e9c6cfb18f8e9bdf74622eb126141416776971a84f94d156beaf67aecbf2ad412e76e66e8fad7633f5b6d7f3d64b5c6c69ce29003c6024465ae3b89be78e915d88b4b5621d",
+ "b2af688e7d8fc4b508c05cc39dd583d6714322c64d7f3e63147aede2d9534934b04ff6f337b031815cd094bdbc6d7a92077dce709412286822ef0737ee47f6b7ffa22f9d53f11dd2b0a3bb9fc01d9a88f9d53c26e9365c2c3c063bc4840bfc812e4b80463e69d179530b25c158f543191cff993106511aa036043bbc75866ab7e34afc57e2cce4934a5faae6eabe4f221770183dd060467827c27a354159a081275a291f69d946d6fe28ed0b9ce08206cf484925a51b9498dbde178ddd3ae91a8581b91682d860f840782f6eea49dbb9bd721501d2c67122dea3b7283848c5f13e0c0de876bd227a856e4de593a3"),
+ new TestCase(
+ "9e1da239d155f52ad37f75c7368a536668b051952923ad44f57e75ab588e475a",
+ "af06f17859dffa799891c4288f6635b5c5a45eee9017fd72",
+ "feac9d54fc8c115ae247d9a7e919dd76cfcbc72d32cae4944860817cbdfb8c04e6b1df76a16517cd33ccf1acda9206389e9e318f5966c093cfb3ec2d9ee2de856437ed581f552f26ac2907609df8c613b9e33d44bfc21ff79153e9ef81a9d66cc317857f752cc175fd8891fefebb7d041e6517c3162d197e2112837d3bc4104312ad35b75ea686e7c70d4ec04746b52ff09c421451459fb59f",
+ "2c261a2f4e61a62e1b27689916bf03453fcbc97bb2af6f329391ef063b5a219bf984d07d70f602d85f6db61474e9d9f5a2deecb4fcd90184d16f3b5b5e168ee03ea8c93f3933a22bc3d1a5ae8c2d8b02757c87c073409052a2a8a41e7f487e041f9a49a0997b540e18621cad3a24f0a56d9b19227929057ab3ba950f6274b121f193e32e06e5388781a1cb57317c0ba6305e910961d01002f0"),
+ new TestCase("d5c7f6797b7e7e9c1d7fd2610b2abf2bc5a7885fb3ff78092fb3abe8986d35e2",
+ "744e17312b27969d826444640e9c4a378ae334f185369c95",
+ "7758298c628eb3a4b6963c5445ef66971222be5d1a4ad839715d1188071739b77cc6e05d5410f963a64167629757",
+ "27b8cfe81416a76301fd1eec6a4d99675069b2da2776c360db1bdfea7c0aa613913e10f7a60fec04d11e65f2d64e"),
+ new TestCase(
+ "737d7811ce96472efed12258b78122f11deaec8759ccbd71eac6bbefa627785c",
+ "6fb2ee3dda6dbd12f1274f126701ec75c35c86607adb3edd",
+ "501325fb2645264864df11faa17bbd58312b77cad3d94ac8fb8542f0eb653ad73d7fce932bb874cb89ac39fc47f8267cf0f0c209f204b2d8578a3bdf461cb6a271a468bebaccd9685014ccbc9a73618c6a5e778a21cc8416c60ad24ddc417a130d53eda6dfbfe47d09170a7be1a708b7b5f3ad464310be36d9a2a95dc39e83d38667e842eb6411e8a23712297b165f690c2d7ca1b1346e3c1fccf5cafd4f8be0",
+ "6724c372d2e9074da5e27a6c54b2d703dc1d4c9b1f8d90f00c122e692ace7700eadca942544507f1375b6581d5a8fb39981c1c0e6e1ff2140b082e9ec016fce141d5199647d43b0b68bfd0fea5e00f468962c7384dd6129aea6a3fdfe75abb210ed5607cef8fa0e152833d5ac37d52e557b91098a322e76a45bbbcf4899e790618aa3f4c2e5e0fc3de93269a577d77a5502e8ea02f717b1dd2df1ec69d8b61ca"),
+ new TestCase(
+ "760158da09f89bbab2c99e6997f9523a95fcef10239bcca2573b7105f6898d34",
+ "43636b2cc346fc8b7c85a19bf507bdc3dafe953b88c69dba",
+ "d30a6d42dff49f0ed039a306bae9dec8d9e88366cc19e8c3642fd58fa0794ebf8029d949730339b0823a51f0f49f0d2c71f1051c1e0e2c86941f172789cdb1b0107413e70f982ff9761877bb526ef1c3eb1106a948d60ef21bd35d32cfd64f89b79ed63ecc5cca56246af736766f285d8e6b0da9cb1cd21020223ffacc5a32",
+ "c815b6b79b64f9369aec8dce8c753df8a50f2bc97c70ce2f014db33a65ac5816bac9e30ac08bdded308c65cb87e28e2e71b677dc25c5a6499c1553555daf1f55270a56959dffa0c66f24e0af00951ec4bb59ccc3a6c5f52e0981647e53e439313a52c40fa7004c855b6e6eb25b212a138e843a9ba46edb2a039ee82a263abe"),
+ new TestCase(
+ "27ba7e81e7edd4e71be53c07ce8e633138f287e155c7fa9e84c4ad804b7fa1b9",
+ "ea05f4ebcd2fb6b000da0612861ba54ff5c176fb601391aa",
+ "e09ff5d2cb050d69b2d42494bde5825238c756d6991d99d7a20d1ef0b83c371c89872690b2fc11d5369f4fc4971b6d3d6c078aef9b0f05c0e61ab89c025168054defeb03fef633858700c58b1262ce011300012673e893e44901dc18eee3105699c44c805897bdaf776af1833162a21a",
+ "a23e7ef93c5d0667c96d9e404dcbe6be62026fa98f7a3ff9ba5d458643a16a1cef7272dc6097a9b52f35983557c77a11b314b4f7d5dc2cca15ee47616f861873cbfed1d32372171a61e38e447f3cf362b3abbb2ed4170d89dcb28187b7bfd206a3e026f084a7e0ed63d319de6bc9afc0"),
+ new TestCase("6799d76e5ffb5b4920bc2768bafd3f8c16554e65efcf9a16f4683a7a06927c11",
+ "61ab951921e54ff06d9b77f313a4e49df7a057d5fd627989", "472766", "8fd7df"),
+ new TestCase(
+ "f68238c08365bb293d26980a606488d09c2f109edafa0bbae9937b5cc219a49c",
+ "5190b51e9b708624820b5abdf4e40fad1fb950ad1adc2d26",
+ "47ec6b1f73c4b7ff5274a0bfd7f45f864812c85a12fbcb3c2cf8a3e90cf66ccf2eacb521e748363c77f52eb426ae57a0c6c78f75af71284569e79d1a92f949a9d69c4efc0b69902f1e36d7562765543e2d3942d9f6ff5948d8a312cff72c1afd9ea3088aff7640bfd265f7a9946e606abc77bcedae6bddc75a0dba0bd917d73e3bd1268f727e0096345da1ed25cf553ea7a98fea6b6f285732de37431561ee1b3064887fbcbd71935e02",
+ "36160e88d3500529ba4edba17bc24d8cfaca9a0680b3b1fc97cf03f3675b7ac301c883a68c071bc54acdd3b63af4a2d72f985e51f9d60a4c7fd481af10b2fc75e252fdee7ea6b6453190617dcc6e2fe1cd56585fc2f0b0e97c5c3f8ad7eb4f31bc4890c03882aac24cc53acc1982296526690a220271c2f6e326750d3fbda5d5b63512c831f67830f59ac49aae330b3e0e02c9ea0091d19841f1b0e13d69c9fbfe8a12d6f30bb734d9d2"),
+ new TestCase(
+ "45b2bd0de4ed9293ec3e26c4840faaf64b7d619d51e9d7a2c7e36c83d584c3df",
+ "546c8c5d6be8f90952cab3f36d7c1957baaa7a59abe3d7e5",
+ "5007c8cd5b3c40e17d7fe423a87ae0ced86bec1c39dc07a25772f3e96dabd56cd3fd7319f6c9654925f2d87087a700e1b130da796895d1c9b9acd62b266144067d373ed51e787498b03c52faad16bb3826fa511b0ed2a19a8663f5ba2d6ea7c38e7212e9697d91486c49d8a000b9a1935d6a7ff7ef23e720a45855481440463b4ac8c4f6e7062adc1f1e1e25d3d65a31812f58a71160",
+ "8eacfba568898b10c0957a7d44100685e8763a71a69a8d16bc7b3f88085bb9a2f09642e4d09a9f0ad09d0aad66b22610c8bd02ff6679bb92c2c026a216bf425c6be35fb8dae7ff0c72b0efd6a18037c70eed0ca90062a49a3c97fdc90a8f9c2ea536bfdc41918a7582c9927fae47efaa3dc87967b7887dee1bf071734c7665901d9105dae2fdf66b4918e51d8f4a48c60d19fbfbbcba"),
+ new TestCase(
+ "fe559c9a282beb40814d016d6bfcb2c0c0d8bf077b1110b8703a3ce39d70e0e1",
+ "b076200cc7011259805e18b304092754002723ebec5d6200",
+ "6db65b9ec8b114a944137c821fd606be75478d928366d5284096cdef782fcff7e8f59cb8ffcda979757902c5ffa6bc477ceaa4cb5d5ea76f94d91e833f823a6bc78f1055dfa6a97bea8965c1cde67a668e001257334a585727d9e0f7c1a06e88d3d25a4e6d9096c968bf138e116a3ebeffd4bb4808adb1fd698164ba0a35c709a47f16f1f4435a2345a9194a00b95abd51851d505809a6077da9baca5831afff31578c487ee68f2767974a98a7e803aac788da98319c4ea8eaa3d394855651f484cef543f537e35158ee29",
+ "4dce9c8f97a028051b0727f34e1b9ef21f06f0760f36e71713204027902090ba2bb6b13436ee778d9f50530efbd7a32b0d41443f58ccaee781c7b716d3a96fdec0e3764ed7959f34c3941278591ea033b5cbadc0f1916032e9bebbd1a8395b83fb63b1454bd775bd20b3a2a96f951246ac14daf68166ba62f6cbff8bd121ac9498ff8852fd2be975df52b5daef3829d18eda42e715022dcbf930d0a789ee6a146c2c7088c35773c63c06b4af4559856ac199ced86863e4294707825337c5857970eb7fddeb263781309011"),
+ new TestCase(
+ "0ae10012d7e56614b03dcc89b14bae9242ffe630f3d7e35ce8bbb97bbc2c92c3",
+ "f96b025d6cf46a8a12ac2af1e2aef1fb83590adadaa5c5ea",
+ "ea0f354e96f12bc72bbaa3d12b4a8ed879b042f0689878f46b651cc4116d6f78409b11430b3aaa30b2076891e8e1fa528f2fd169ed93dc9f84e24409eec2101daf4d057be2492d11de640cbd7b355ad29fb70400fffd7cd6d425abeeb732a0eaa4330af4c656252c4173deab653eb85c58462d7ab0f35fd12b613d29d473d330310dc323d3c66348bbdbb68a326324657cae7b77a9e34358f2cec50c85609e73056856796e3be8d62b6e2fe9f953",
+ "e8abd48924b54e5b80866be7d4ebe5cf4274cafff08b39cb2d40a8f0b472398aedc776e0793812fbf1f60078635d2ed86b15efcdba60411ee23b07233592a44ec31b1013ce8964236675f8f183aef885e864f2a72edf4215b5338fa2b54653dfa1a8c55ce5d95cc605b9b311527f2e3463ffbec78a9d1d65dabad2f338769c9f43f133a791a11c7eca9af0b771a4ac32963dc8f631a2c11217ac6e1b9430c1aae1ceebe22703f429998a8fb8c641"),
+ new TestCase(
+ "082c539bc5b20f97d767cd3f229eda80b2adc4fe49c86329b5cd6250a9877450",
+ "845543502e8b64912d8f2c8d9fffb3c69365686587c08d0c",
+ "a96bb7e910281a6dfad7c8a9c370674f0ceec1ad8d4f0de32f9ae4a23ed329e3d6bc708f876640a229153ac0e7281a8188dd77695138f01cda5f41d5215fd5c6bdd46d982cb73b1efe2997970a9fdbdb1e768d7e5db712068d8ba1af6067b5753495e23e6e1963af012f9c7ce450bf2de619d3d59542fb55f3",
+ "835da74fc6de08cbda277a7966a07c8dcd627e7b17adde6d930b6581e3124b8baad096f693991fedb1572930601fc7709541839b8e3ffd5f033d2060d999c6c6e3048276613e648000acb5212cc632a916afce290e20ebdf612d08a6aa4c79a74b070d3f872a861f8dc6bb07614db515d363349d3a8e3336a3"),
+ new TestCase("3d02bff3375d403027356b94f514203737ee9a85d2052db3e4e5a217c259d18a",
+ "74216c95031895f48c1dba651555ebfa3ca326a755237025",
+ "0d4b0f54fd09ae39baa5fa4baccf2e6682e61b257e01f42b8f",
+ "16c4006c28365190411eb1593814cf15e74c22238f210afc3d"),
+ new TestCase(
+ "ad1a5c47688874e6663a0f3fa16fa7efb7ecadc175c468e5432914bdb480ffc6",
+ "e489eed440f1aae1fac8fb7a9825635454f8f8f1f52e2fcc",
+ "aa6c1e53580f03a9abb73bfdadedfecada4c6b0ebe020ef10db745e54ba861caf65f0e40dfc520203bb54d29e0a8f78f16b3f1aa525d6bfa33c54726e59988cfbec78056",
+ "02fe84ce81e178e7aabdd3ba925a766c3c24756eefae33942af75e8b464556b5997e616f3f2dfc7fce91848afd79912d9fb55201b5813a5a074d2c0d4292c1fd441807c5"),
+ new TestCase(
+ "053a02bedd6368c1fb8afc7a1b199f7f7ea2220c9a4b642a6850091c9d20ab9c",
+ "c713eea5c26dad75ad3f52451e003a9cb0d649f917c89dde",
+ "8f0a8a164760426567e388840276de3f95cb5e3fadc6ed3f3e4fe8bc169d9388804dcb94b6587dbb66cb0bd5f87b8e98b52af37ba290629b858e0e2aa7378047a26602",
+ "516710e59843e6fbd4f25d0d8ca0ec0d47d39d125e9dad987e0518d49107014cb0ae405e30c2eb3794750bca142ce95e290cf95abe15e822823e2e7d3ab21bc8fbd445"),
+ new TestCase(
+ "5b14ab0fbed4c58952548a6cb1e0000cf4481421f41288ea0aa84add9f7deb96",
+ "54bf52b911231b952ba1a6af8e45b1c5a29d97e2abad7c83",
+ "37fb44a675978b560ff9a4a87011d6f3ad2d37a2c3815b45a3c0e6d1b1d8b1784cd468927c2ee39e1dccd4765e1c3d676a335be1ccd6900a45f5d41a317648315d8a8c24adc64eb285f6aeba05b9029586353d303f17a807658b9ff790474e1737bd5fdc604aeff8dfcaf1427dcc3aacbb0256badcd183ed75a2dc52452f87d3c1ed2aa583472b0ab91cda20614e9b6fdbda3b49b098c95823cc72d8e5b717f2314b0324e9ce",
+ "ae6deb5d6ce43d4b09d0e6b1c0e9f46157bcd8ab50eaa3197ff9fa2bf7af649eb52c68544fd3adfe6b1eb316f1f23538d470c30dbfec7e57b60cbcd096c782e7736b669199c8253e70214cf2a098fda8eac5da79a9496a3aae754d03b17c6d70d1027f42bf7f95ce3d1d9c338854e158fcc803e4d6262fb639521e47116ef78a7a437ca9427ba645cd646832feab822a208278e45e93e118d780b988d65397eddfd7a819526e"),
+ new TestCase(
+ "d74636e3413a88d85f322ca80fb0bd650bd0bf0134e2329160b69609cd58a4b0",
+ "efb606aa1d9d9f0f465eaa7f8165f1ac09f5cb46fecf2a57",
+ "f85471b75f6ec81abac2799ec09e98e280b2ffd64ca285e5a0109cfb31ffab2d617b2c2952a2a8a788fc0da2af7f530758f74f1ab56391ab5ff2adbcc5be2d6c7f49fbe8118104c6ff9a23c6dfe52f57954e6a69dcee5db06f514f4a0a572a9a8525d961dae72269b987189d465df6107119c7fa790853e063cba0fab7800ca932e258880fd74c33c784675bedad0e7c09e9cc4d63dd5e9713d5d4a0196e6b562226ac31b4f57c04f90a181973737ddc7e80f364112a9fbb435ebdbcabf7d490ce52",
+ "b2b795fe6c1d4c83c1327e015a67d4465fd8e32813575cbab263e20ef05864d2dc17e0e4eb81436adfe9f638dcc1c8d78f6b0306baf938e5d2ab0b3e05e735cc6fff2d6e02e3d60484bea7c7a8e13e23197fea7b04d47d48f4a4e5944174539492800d3ef51e2ee5e4c8a0bdf050c2dd3dd74fce5e7e5c37364f7547a11480a3063b9a0a157b15b10a5a954de2731ced055aa2e2767f0891d4329c426f3808ee867bed0dc75b5922b7cfb895700fda016105a4c7b7f0bb90f029f6bbcb04ac36ac16") };
+
+ public String getName()
+ {
+ return "XSalsa20";
+ }
+
+ public void performTest() throws Exception
+ {
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ performTest(i, TEST_CASES[i]);
+ }
+ }
+
+ private void performTest(int number, TestCase testCase)
+ {
+ final byte[] plaintext = testCase.getPlaintext();
+ byte[] output = new byte[plaintext.length];
+
+ XSalsa20Engine engine = new XSalsa20Engine();
+ engine.init(false, new ParametersWithIV(new KeyParameter(testCase.getKey()), testCase.getIv()));
+
+ engine.processBytes(testCase.getPlaintext(), 0, testCase.getPlaintext().length, output, 0);
+
+ if (!Arrays.areEqual(testCase.getCiphertext(), output))
+ {
+ fail("mismatch on " + number, new String(Hex.encode(testCase.getCiphertext())),
+ new String(Hex.encode(output)));
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new XSalsa20Test());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/XTEATest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/XTEATest.java
new file mode 100644
index 000000000..74da43bed
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/XTEATest.java
@@ -0,0 +1,48 @@
+package org.spongycastle.crypto.test;
+
+import org.spongycastle.crypto.engines.XTEAEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+/**
+ * TEA tester - based on C implementation results from http://www.simonshepherd.supanet.com/tea.htm
+ */
+public class XTEATest
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new XTEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0000000000000000",
+ "dee9d4d8f7131ed9"),
+ new BlockCipherVectorTest(1, new XTEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0102030405060708",
+ "065c1b8975c6a816"),
+ new BlockCipherVectorTest(2, new XTEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0000000000000000",
+ "1ff9a0261ac64264"),
+ new BlockCipherVectorTest(3, new XTEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0102030405060708",
+ "8c67155b2ef91ead"),
+ };
+
+ XTEATest()
+ {
+ super(tests, new XTEAEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "XTEA";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new XTEATest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/CAVPListener.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/CAVPListener.java
new file mode 100644
index 000000000..71011614b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/CAVPListener.java
@@ -0,0 +1,18 @@
+package org.spongycastle.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/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/CAVPReader.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/CAVPReader.java
new file mode 100644
index 000000000..2cee45590
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/CAVPReader.java
@@ -0,0 +1,152 @@
+package org.spongycastle.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.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.digests.SHA224Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.macs.CMac;
+import org.spongycastle.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/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFCounterTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFCounterTests.java
new file mode 100644
index 000000000..b8a00fe06
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFCounterTests.java
@@ -0,0 +1,102 @@
+package org.spongycastle.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.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.KDFCounterBytesGenerator;
+import org.spongycastle.crypto.params.KDFCounterParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.util.test.TestFailedException;
+
+public final class KDFCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+ // always skip AFTER_FIXED, not included in SP 800-108
+ if (config.getProperty("CTRLOCATION").matches("AFTER_FIXED"))
+ {
+ return;
+ }
+
+ // 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"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFCounterParameters params = new KDFCounterParameters(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("KDFCTR.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java
new file mode 100644
index 000000000..8f58be497
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java
@@ -0,0 +1,107 @@
+package org.spongycastle.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.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator;
+import org.spongycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.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/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java
new file mode 100644
index 000000000..512568f51
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java
@@ -0,0 +1,88 @@
+package org.spongycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator;
+import org.spongycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.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/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFFeedbackCounterTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFFeedbackCounterTests.java
new file mode 100644
index 000000000..84cd4dd83
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFFeedbackCounterTests.java
@@ -0,0 +1,108 @@
+package org.spongycastle.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.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.KDFFeedbackBytesGenerator;
+import org.spongycastle.crypto.params.KDFFeedbackParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.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/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java
new file mode 100644
index 000000000..4c6fa942b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java
@@ -0,0 +1,89 @@
+package org.spongycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.KDFFeedbackBytesGenerator;
+import org.spongycastle.crypto.params.KDFFeedbackParameters;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTestResult;
+import org.spongycastle.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/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/MacThroughputTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/MacThroughputTest.java
new file mode 100644
index 000000000..ffbc673f9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/MacThroughputTest.java
@@ -0,0 +1,156 @@
+package org.spongycastle.crypto.test.speedy;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.NullEngine;
+import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
+import org.spongycastle.crypto.macs.CMac;
+import org.spongycastle.crypto.macs.GMac;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.macs.Poly1305;
+import org.spongycastle.crypto.macs.SipHash;
+import org.spongycastle.crypto.macs.SkeinMac;
+import org.spongycastle.crypto.modes.GCMBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Microbenchmark of MACs on short, medium, long messages, with optional object creation cost.
+ */
+public class MacThroughputTest
+{
+
+ private static final long CLOCK_SPEED = 2400000000L;
+
+ private static final SecureRandom RANDOM = new SecureRandom();
+ private static Poly1305KeyGenerator kg = new Poly1305KeyGenerator();;
+
+ private static final byte[] SHORT_MESSAGE = new byte[16];
+ private static final byte[] MEDIUM_MESSAGE = new byte[256];
+ private static final byte[] LONG_MESSAGE = new byte[8192];
+ static
+ {
+ RANDOM.nextBytes(SHORT_MESSAGE);
+ RANDOM.nextBytes(MEDIUM_MESSAGE);
+ RANDOM.nextBytes(LONG_MESSAGE);
+ }
+
+ private static final int SHORT_MESSAGE_COUNT = 20000000;
+ private static final int MEDIUM_MESSAGE_COUNT = 2200000;
+ private static final int LONG_MESSAGE_COUNT = 80000;
+
+ static
+ {
+ kg.init(new KeyGenerationParameters(RANDOM, 256));
+ }
+
+ private static KeyParameter generatePoly1305Key()
+ {
+ return new KeyParameter(kg.generateKey());
+ }
+
+ public static void main(String[] args)
+ {
+ testMac(new HMac(new SHA1Digest()), new KeyParameter(generateNonce(20)), 3);
+ testMac(new SkeinMac(SkeinMac.SKEIN_512, 128), new KeyParameter(generateNonce(64)), 2);
+ testMac(new SipHash(), new KeyParameter(generateNonce(16)), 1);
+ testMac(new CMac(new AESFastEngine()), new KeyParameter(generateNonce(16)), 3);
+ testMac(new GMac(new GCMBlockCipher(new AESFastEngine())), new ParametersWithIV(new KeyParameter(
+ generateNonce(16)), generateNonce(16)), 5);
+ testMac(new Poly1305(new NullEngine(16)), new ParametersWithIV(generatePoly1305Key(), generateNonce(16)), 1);
+ testMac(new Poly1305(new AESFastEngine()), new ParametersWithIV(generatePoly1305Key(), generateNonce(16)), 1);
+ testMac(new Poly1305Reference(new NullEngine(16)), new ParametersWithIV(generatePoly1305Key(),
+ generateNonce(16)), 1);
+ }
+
+ private static byte[] generateNonce(int sizeBytes)
+ {
+ byte[] nonce = new byte[16];
+ RANDOM.nextBytes(nonce);
+ return nonce;
+ }
+
+ private static void testMac(Mac mac, CipherParameters params, int rateFactor)
+ {
+ System.out.println("=========================");
+
+ long total = testRun(mac, params, false, MEDIUM_MESSAGE, adjust(MEDIUM_MESSAGE_COUNT, rateFactor));
+ System.out.printf("%s Warmup 1 run time: %,d ms\n", mac.getAlgorithmName(), total / 1000000);
+ total = testRun(mac, params, false, MEDIUM_MESSAGE, adjust(MEDIUM_MESSAGE_COUNT, rateFactor));
+ System.out.printf("%s Warmup 2 run time: %,d ms\n", mac.getAlgorithmName(), total / 1000000);
+ System.gc();
+ try
+ {
+ Thread.sleep(1000);
+ } catch (InterruptedException e)
+ {
+ }
+
+ test("Short", mac, params, false, SHORT_MESSAGE, adjust(SHORT_MESSAGE_COUNT, rateFactor));
+ // test("Short", mac, params, true, SHORT_MESSAGE, adjust(SHORT_MESSAGE_COUNT, rateFactor));
+ test("Medium", mac, params, false, MEDIUM_MESSAGE, adjust(MEDIUM_MESSAGE_COUNT, rateFactor));
+ // test("Medium", mac, params, true, MEDIUM_MESSAGE, adjust(MEDIUM_MESSAGE_COUNT,
+ // rateFactor));
+ test("Long", mac, params, false, LONG_MESSAGE, adjust(LONG_MESSAGE_COUNT, rateFactor));
+ // test("Long", mac, params, true, LONG_MESSAGE, adjust(LONG_MESSAGE_COUNT, rateFactor));
+ }
+
+ private static int adjust(int iterationCount, int rateFactor)
+ {
+ return (int)(iterationCount * (1.0f / rateFactor));
+ }
+
+ private static void test(String name,
+ Mac mac,
+ CipherParameters params,
+ boolean initPerMessage,
+ byte[] message,
+ int adjustedCount)
+ {
+ System.out.println("=========================");
+ long total = testRun(mac, params, initPerMessage, message, adjustedCount);
+
+ long averageRuntime = total / adjustedCount;
+ System.out.printf("%s %-7s%s Total run time: %,d ms\n", mac.getAlgorithmName(), name, initPerMessage ? "*"
+ : " ", total / 1000000);
+ System.out.printf("%s %-7s%s Average run time: %,d ns\n", mac.getAlgorithmName(), name, initPerMessage ? "*"
+ : " ", averageRuntime);
+ final long mbPerSecond = (long)((double)message.length / averageRuntime * 1000000000 / (1024 * 1024));
+ System.out.printf("%s %-7s%s Average speed: %,d MB/s\n", mac.getAlgorithmName(), name, initPerMessage ? "*"
+ : " ", mbPerSecond);
+ System.out.printf("%s %-7s%s Average speed: %,f c/b\n", mac.getAlgorithmName(), name, initPerMessage ? "*"
+ : " ", CLOCK_SPEED / (double)(mbPerSecond * (1024 * 1024)));
+ }
+
+ private static long testRun(Mac mac,
+ CipherParameters params,
+ boolean initPerMessage,
+ byte[] message,
+ int adjustedCount)
+ {
+ byte[] out = new byte[mac.getMacSize()];
+
+ if (!initPerMessage)
+ {
+ mac.init(params);
+ }
+ long start = System.nanoTime();
+
+ for (int i = 0; i < adjustedCount; i++)
+ {
+ if (initPerMessage)
+ {
+ mac.init(params);
+ }
+ mac.update(message, 0, message.length);
+ mac.doFinal(out, 0);
+ }
+ long total = System.nanoTime() - start;
+ return total;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/Poly1305Reference.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/Poly1305Reference.java
new file mode 100644
index 000000000..a30c11168
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/Poly1305Reference.java
@@ -0,0 +1,292 @@
+package org.spongycastle.crypto.test.speedy;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Poly1305 message authentication code, designed by D. J. Bernstein.
+ * ref
implementation, and is probably too slow for real usage.
+ *
+ * @see Poly1305KeyGenerator
+ */
+public class Poly1305Reference
+ implements Mac
+{
+ private static final int BLOCK_SIZE = 16;
+ private static final int STATE_SIZE = BLOCK_SIZE + 1;
+ private static int[] minusp = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252};
+
+ private final BlockCipher cipher;
+
+ /** Encrypted nonce */
+ private final byte[] encryptedNonce = new byte[BLOCK_SIZE];
+
+ /** Private integer r *, expanded to 17 bytes */
+ private final int[] r = new int[STATE_SIZE];
+
+ /** Accumulated authenticator value */
+ private final int[] h = new int[STATE_SIZE];
+
+ /** Temp buffer for incorporating into authenticator */
+ private final int[] c = new int[STATE_SIZE];
+
+ private final byte[] singleByte = new byte[1];
+
+ /** Current block of buffered input */
+ private final byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ public Poly1305Reference(BlockCipher cipher)
+ {
+ if (cipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ public void init(CipherParameters params)
+ throws IllegalArgumentException
+ {
+ final byte[] nonce;
+ final byte[] key;
+ if ((params instanceof ParametersWithIV) && ((ParametersWithIV)params).getParameters() instanceof KeyParameter)
+ {
+ nonce = ((ParametersWithIV)params).getIV();
+ key = ((KeyParameter)((ParametersWithIV)params).getParameters()).getKey();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Poly1305 requires a key and and IV.");
+ }
+
+ setKey(key, nonce);
+ reset();
+ }
+
+ private void setKey(byte[] key, byte[] nonce)
+ {
+ if (nonce.length != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
+ }
+ Poly1305KeyGenerator.checkKey(key);
+
+ // Expand private integer r
+ for (int i = 0; i < BLOCK_SIZE; i++)
+ {
+ r[i] = key[BLOCK_SIZE + i] & 0xFF;
+ }
+ r[BLOCK_SIZE] = 0;
+
+ // Calculate encrypted nonce
+ final byte[] cipherKey = new byte[BLOCK_SIZE];
+ System.arraycopy(key, 0, cipherKey, 0, cipherKey.length);
+
+ cipher.init(true, new KeyParameter(cipherKey));
+ cipher.processBlock(nonce, 0, this.encryptedNonce, 0);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Poly1305-Ref-" + cipher.getAlgorithmName();
+ }
+
+ public int getMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void update(byte in)
+ throws IllegalStateException
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == currentBlock.length)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), currentBlock.length - currentBlockOffset);
+ System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+ copied += toCopy;
+ currentBlockOffset += toCopy;
+ }
+
+ }
+
+ /**
+ * Add a full block of 16 bytes of data, padded to 17 bytes, to the MAC
+ */
+ private void processBlock()
+ {
+ for (int i = 0; i < currentBlockOffset; i++)
+ {
+ c[i] = currentBlock[i] & 0xFF;
+ }
+ c[currentBlockOffset] = 1;
+ for (int i = currentBlockOffset + 1; i < c.length; i++)
+ {
+ c[i] = 0;
+ }
+ add(h, c);
+ mulmod(h, r);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ if (outOff + BLOCK_SIZE > out.length)
+ {
+ throw new DataLengthException("Output buffer is too short.");
+ }
+
+ if (currentBlockOffset > 0)
+ {
+ // Process padded final block
+ processBlock();
+ }
+
+ freeze(h);
+
+ // Add encrypted nonce to result
+ for (int i = 0; i < BLOCK_SIZE; i++)
+ {
+ c[i] = encryptedNonce[i] & 0xFF;
+ }
+ c[BLOCK_SIZE] = 0;
+ add(h, c);
+
+ for (int i = 0; i < BLOCK_SIZE; i++)
+ {
+ out[outOff + i] = (byte)h[i];
+ }
+
+ reset();
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ currentBlockOffset = 0;
+ for (int i = 0; i < h.length; i++)
+ {
+ h[i] = 0;
+ }
+ }
+
+ // 130 bit math adapted from nacl ref implementation
+
+ /**
+ * 130 bit add with carry.
+ */
+ private static void add(int[] h, int[] c)
+ {
+ int u = 0;
+ for (int j = 0; j < 17; ++j)
+ {
+ u += h[j] + c[j];
+ h[j] = u & 255;
+ u >>= 8;
+ }
+ }
+
+ /**
+ * 130 bit multiplication mod 2^130-5
+ */
+ private void mulmod(int[] h, int[] r)
+ {
+ final int[] hr = c;
+
+ for (int i = 0; i < 17; ++i)
+ {
+ int u = 0;
+ /* Basic multiply to compute term i */
+ for (int j = 0; j <= i; ++j)
+ {
+ u += h[j] * r[i - j];
+ }
+
+ /*
+ * Modular reduction
+ *
+ * Shift overflow >> 130 bits == (>> 17 bytes = 136 bits) + (<< 6 bits = * 64)
+ *
+ * Reduction mod 2^130-5 leaves 5x remainder, so 64 * 5 = 320.
+ */
+ for (int j = i + 1; j < 17; ++j)
+ {
+ u += 320 * h[j] * r[i + 17 - j];
+ }
+ hr[i] = u;
+ }
+ System.arraycopy(hr, 0, h, 0, h.length);
+ squeeze(h);
+ }
+
+ /**
+ * Propagate carries following a modular multiplication.
+ */
+ private static void squeeze(int[] h)
+ {
+ int u = 0;
+ for (int j = 0; j < 16; ++j)
+ {
+ u += h[j];
+ h[j] = u & 255;
+ u >>= 8;
+ }
+ u += h[16];
+ h[16] = u & 3;
+ u = 5 * (u >> 2);
+ for (int j = 0; j < 16; ++j)
+ {
+ u += h[j];
+ h[j] = u & 255;
+ u >>= 8;
+ }
+ u += h[16];
+ h[16] = u;
+ }
+
+ /**
+ * Constant time correction of h to be < p (2^130 - 5).
+ */
+ private void freeze(int[] h)
+ {
+ final int[] horig = c;
+ System.arraycopy(h, 0, horig, 0, h.length);
+
+ add(h, minusp);
+ final int negative = -(h[16] >> 7);
+ for (int j = 0; j < 17; ++j)
+ {
+ h[j] ^= negative & (horig[j] ^ h[j]);
+ }
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/ThreefishReferenceEngine.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/ThreefishReferenceEngine.java
new file mode 100644
index 000000000..768bb9542
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/ThreefishReferenceEngine.java
@@ -0,0 +1,395 @@
+package org.spongycastle.crypto.test.speedy;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.TweakableBlockCipherParameters;
+
+public class ThreefishReferenceEngine
+ implements BlockCipher
+{
+
+ /**
+ * The tweak input is always 128 bits
+ */
+ private static final int TWEAK_SIZE = 16;
+
+ private static long C_240 = 0x1BD11BDAA9FC1A22L;
+
+ private final int blocksize = 64;
+ private final int rounds = 72;
+ private final int words = 8;
+
+ private boolean forEncryption;
+
+ private long[] block = new long[words];
+
+ private int[][] rotations = R8;
+
+ /**
+ * Rotation constants Rd,j for Nw = 8.
+ */
+ private static final int[][] R8 = {
+ {46, 36, 19, 37},
+ {33, 27, 14, 42},
+ {17, 49, 36, 39},
+ {44, 9, 54, 56},
+ {39, 30, 34, 24},
+ {13, 50, 10, 17},
+ {25, 29, 39, 43},
+ {8, 35, 56, 22}};
+
+ private long[] t;
+
+ private long kw[];
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof TweakableBlockCipherParameters)
+ {
+ init(forEncryption, (TweakableBlockCipherParameters)params);
+ }
+ else if (params instanceof KeyParameter)
+ {
+ init(forEncryption, new TweakableBlockCipherParameters((KeyParameter)params, new byte[TWEAK_SIZE]));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Threefish init - "
+ + params.getClass().getName());
+ }
+ }
+
+ public void init(boolean forEncryption, TweakableBlockCipherParameters params)
+ throws IllegalArgumentException
+ {
+ // TODO: Remove some of the NPEs that can be avoided in the Params
+ // classes
+ if ((params.getKey() == null) || (params.getKey().getKey() == null)
+ || (params.getKey().getKey().length != blocksize))
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (%d bytes)" + blocksize);
+ }
+
+ if ((params.getTweak() == null) || (params.getTweak().length != TWEAK_SIZE))
+ {
+ throw new IllegalArgumentException("Threefish tweak must be %d bytes" + TWEAK_SIZE);
+ }
+
+ this.forEncryption = forEncryption;
+
+ generateKeySchedule(params.getKey().getKey(), params.getTweak());
+ }
+
+ private void generateKeySchedule(byte[] key, byte[] tweak)
+ {
+ // TODO: This key schedule can/should be generated incrementally/on demand during encrypt/decrypt
+ // to reduce memory overhead (currently 1.2MB = (rounds/4+1)=19 * words=8 * 8 bytes/word)
+
+ t = new long[3];
+ t[0] = BytesToWord(tweak, 0);
+ t[1] = BytesToWord(tweak, 8);
+ t[2] = t[0] ^ t[1];
+
+ kw = new long[words + 1];
+
+ long knw = C_240;
+ for (int i = 0; i < words; i++)
+ {
+ kw[i] = BytesToWord(key, i * 8);
+ knw = knw ^ kw[i];
+ }
+ kw[kw.length - 1] = knw;
+ }
+
+ private static long BytesToWord(byte[] bytes, int off)
+ {
+ long word = 0;
+ int index = off;
+
+ word = (bytes[index++] & 0xffL);
+ word |= (bytes[index++] & 0xffL) << 8;
+ word |= (bytes[index++] & 0xffL) << 16;
+ word |= (bytes[index++] & 0xffL) << 24;
+ word |= (bytes[index++] & 0xffL) << 32;
+ word |= (bytes[index++] & 0xffL) << 40;
+ word |= (bytes[index++] & 0xffL) << 48;
+ word |= (bytes[index++] & 0xffL) << 56;
+
+ return word;
+ }
+
+ private static void WordToBytes(long word, byte[] bytes, int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte)word;
+ bytes[index++] = (byte)(word >> 8);
+ bytes[index++] = (byte)(word >> 16);
+ bytes[index++] = (byte)(word >> 24);
+ bytes[index++] = (byte)(word >> 32);
+ bytes[index++] = (byte)(word >> 40);
+ bytes[index++] = (byte)(word >> 48);
+ bytes[index++] = (byte)(word >> 56);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Threefish";
+ }
+
+ public int getBlockSize()
+ {
+ return blocksize;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ // TODO: Check init state
+ if (kw == null)
+ {
+ throw new IllegalStateException("Threefish engine not initialised");
+ }
+
+ if ((inOff + blocksize) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ if ((outOff + blocksize) > out.length)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock();
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock();
+ packBlock(out, outOff);
+ }
+
+ return blocksize;
+ }
+
+ private void decryptBlock()
+ {
+ for (int d = rounds; d > 0; d--)
+ {
+ // Add subkey every 4 rounds
+ if ((d % 4) == 0)
+ {
+ uninjectSubkey(d / 4);
+ }
+
+ // Permute
+ unpermute();
+
+ // Mix
+ for (int j = 0; j < words / 2; j++)
+ {
+ unmix(j, d - 1);
+ }
+ }
+
+ // Remove first subkey
+ uninjectSubkey(0);
+ }
+
+ private void injectSubkey(int s)
+ {
+ for (int i = 0; i < (words - 3); i++)
+ {
+ block[i] += kw[(s + i) % (words + 1)];
+ }
+ block[words - 3] += kw[(s + words - 3) % (words + 1)] + t[s % 3];
+ block[words - 2] += kw[(s + words - 2) % (words + 1)] + t[(s + 1) % 3];
+ block[words - 1] += kw[(s + words - 1) % (words + 1)] + s;
+ }
+
+ private void uninjectSubkey(int s)
+ {
+ for (int i = 0; i < (words - 3); i++)
+ {
+ block[i] -= kw[(s + i) % (words + 1)];
+ }
+ block[words - 3] -= kw[(s + words - 3) % (words + 1)] + t[s % 3];
+ block[words - 2] -= kw[(s + words - 2) % (words + 1)] + t[(s + 1) % 3];
+ block[words - 1] -= kw[(s + words - 1) % (words + 1)] + s;
+ }
+
+ private void encryptBlock()
+ {
+ for (int d = 0; d < rounds; d++)
+ {
+ // Add subkey every 4 rounds
+ if ((d % 4) == 0)
+ {
+ injectSubkey(d / 4);
+ }
+
+ // Mix
+ for (int j = 0; j < words / 2; j++)
+ {
+ mix(j, d);
+ }
+
+ // Permute
+ permute();
+ }
+
+ // Final key addition
+ injectSubkey(rounds / 4);
+ }
+
+ private void permute()
+ {
+ // Permute in place for Nw = 8
+ long f0 = block[0];
+ long f3 = block[3];
+
+ block[0] = block[2];
+ block[1] = block[1];
+ block[2] = block[4];
+ block[3] = block[7];
+ block[4] = block[6];
+ block[5] = block[5];
+ block[6] = f0;
+ block[7] = f3;
+ }
+
+ private void unpermute()
+ {
+ // TODO: Change these to tables
+ // Permute in place for Nw = 8
+ long f6 = block[6];
+ long f7 = block[7];
+
+ block[7] = block[3];
+ block[6] = block[4];
+ block[5] = block[5];
+ block[4] = block[2];
+ block[3] = f7;
+ block[2] = block[0];
+ block[1] = block[1];
+ block[0] = f6;
+ }
+
+ private void mix(int j, int d)
+ {
+ // ed,2j and ed,2j+1
+ int b0 = 2 * j;
+ int b1 = b0 + 1;
+
+ // y0 = x0 + x1
+ block[b0] = block[b0] + block[b1];
+
+ // y1 = (x1 <<< R(d mod 8,j)) xor y0
+ block[b1] = Long.rotateLeft(block[b1], rotations[d % 8][j]) ^ block[b0];
+ }
+
+ private void unmix(int j, int d)
+ {
+ // ed,2j and ed,2j+1
+ int b0 = 2 * j;
+ int b1 = b0 + 1;
+
+ // x1 = (y1 ^ y0) >>> R(d mod 8, j))
+ block[b1] = Long.rotateRight(block[b1] ^ block[b0], rotations[d % 8][j]);
+
+ // x0 = y0 - x1
+ block[b0] = block[b0] - block[b1];
+
+ }
+
+ public static void main(String[] args)
+ {
+ ThreefishReferenceEngine engine = new ThreefishReferenceEngine();
+ engine.fu();
+ }
+
+ private void fu()
+ {
+ block[0] = 0x12;
+ block[1] = 0x34;
+ block[2] = 0x56;
+ block[3] = 0x78;
+ block[4] = 0x90;
+ block[5] = 0xAB;
+ block[6] = 0xCD;
+ block[7] = 0xEF;
+
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ mix(0, 4);
+ System.err.println("=========");
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ unmix(0, 4);
+ System.err.println("=========");
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ permute();
+ System.err.println("=========");
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ unpermute();
+ System.err.println("=========");
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ generateKeySchedule(new byte[blocksize], new byte[TWEAK_SIZE]);
+ injectSubkey(5);
+ System.err.println("=========");
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ uninjectSubkey(5);
+ System.err.println("=========");
+ for (int i = 0; i < block.length; i++)
+ {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ }
+
+ private void packBlock(byte[] out, int outOff)
+ {
+ for (int i = 0; i < block.length; i++)
+ {
+ WordToBytes(block[i], out, outOff + (i * 8));
+ }
+ }
+
+ private long[] unpackBlock(byte[] bytes, int index)
+ {
+ for (int i = 0; i < block.length; i++)
+ {
+ block[i] = BytesToWord(bytes, index + (i * 8));
+ }
+ return block;
+ }
+
+ public void reset()
+ {
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/ThroughputTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/ThroughputTest.java
new file mode 100644
index 000000000..69b942fc8
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/test/speedy/ThroughputTest.java
@@ -0,0 +1,203 @@
+package org.spongycastle.crypto.test.speedy;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.engines.ThreefishEngine;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.TweakableBlockCipherParameters;
+import org.spongycastle.util.encoders.Hex;
+
+public class ThroughputTest
+{
+
+ private static final int DATA_SIZE = 100 * 1024 * 1024;
+ private static final int RUNS = 1;
+ private static final long CLOCK_SPEED = 2400000000L;
+
+ private static SecureRandom rand = new SecureRandom();
+
+ public static void main(String[] args)
+ throws InterruptedException, IOException
+ {
+// testTF_1024_1();
+// testTF_1024_2();
+ testTF_512_1();
+ testTF_512_2();
+// testTF_256_1();
+// testTF_256_2();
+ System.out.println("Initialising test data.");
+ byte[] input = new byte[DATA_SIZE];
+ rand.nextBytes(input);
+
+ System.out.println("Init complete.");
+// speedTestCipher(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256), input);
+ speedTestCipher(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), input);
+// speedTestCipher(new Skein3FishEngine(), input);
+// speedTestCipher(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024), input);
+// speedTestCipher(new ThreefishReferenceEngine(), input);
+ speedTestCipher(new AESFastEngine(), input);
+// speedTestCipher(new TwofishEngine(), input);
+// speedTestCipher(new BlowfishEngine(), input);
+ }
+
+ private static void testTF_512_1()
+ throws IOException
+ {
+ byte[] key = new byte[64];
+ byte[] tweak = new byte[16];
+ byte[] plaintext = new byte[64];
+ byte[] expected = Hex.decode("b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe");
+
+ runTestVector("Threefish-512-1: Fast", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512));
+ runTestVector("Threefish-512-1: Reference", key, tweak, plaintext, expected, new ThreefishReferenceEngine());
+ }
+
+ private static void testTF_256_1()
+ throws IOException
+ {
+ byte[] key = new byte[32];
+ byte[] tweak = new byte[16];
+ byte[] plaintext = new byte[32];
+ byte[] expected = Hex.decode("84da2a1f8beaee947066ae3e3103f1ad536db1f4a1192495116b9f3ce6133fd8");
+
+ runTestVector("Threefish-256-1: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256));
+ }
+
+ private static void testTF_1024_1()
+ throws IOException
+ {
+ byte[] key = new byte[128];
+ byte[] tweak = new byte[16];
+ byte[] plaintext = new byte[128];
+ byte[] expected = Hex.decode("f05c3d0a3d05b304f785ddc7d1e036015c8aa76e2f217b06c6e1544c0bc1a90df0accb9473c24e0fd54fea68057f43329cb454761d6df5cf7b2e9b3614fbd5a20b2e4760b40603540d82eabc5482c171c832afbe68406bc39500367a592943fa9a5b4a43286ca3c4cf46104b443143d560a4b230488311df4feef7e1dfe8391e");
+
+ runTestVector("Threefish-1024-1: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024));
+ }
+
+ private static void runTestVector(String name, byte[] key, byte[] tweak, byte[] plaintext, byte[] expected, BlockCipher cipher)
+ {
+ System.out.println("====");
+ System.out.println(name + ": ");
+ cipher.init(true, new TweakableBlockCipherParameters(new KeyParameter(key), tweak));
+
+ byte[] ciphertext = new byte[key.length];
+ cipher.processBlock(plaintext, 0, ciphertext, 0);
+
+ System.out.println("Plaintext : " + new String(Hex.encode(plaintext)));
+ System.out.println("Expected : " + new String(Hex.encode(expected)));
+ System.out.println("Ciphertext : " + new String(Hex.encode(ciphertext)));
+ System.out.println(" Encrypt : " + org.spongycastle.util.Arrays.areEqual(expected, ciphertext));
+
+ cipher.init(false, new TweakableBlockCipherParameters(new KeyParameter(key), tweak));
+ byte[] replain = new byte[plaintext.length];
+ cipher.processBlock(ciphertext, 0, replain, 0);
+
+ System.out.println("Replain : " + new String(Hex.encode(replain)));
+ System.out.println(" Decrypt : " + org.spongycastle.util.Arrays.areEqual(plaintext, replain));
+ }
+
+ private static void testTF_512_2()
+ throws IOException
+ {
+ byte[] key = Hex.decode("101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f");
+ byte[] tweak = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] plaintext = Hex.decode("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0");
+ byte[] expected = Hex.decode("e304439626d45a2cb401cad8d636249a6338330eb06d45dd8b36b90e97254779272a0a8d99463504784420ea18c9a725af11dffea10162348927673d5c1caf3d");
+
+ runTestVector("Threefish-512-2: Fast", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512));
+ runTestVector("Threefish-512-2: Reference", key, tweak, plaintext, expected, new ThreefishReferenceEngine());
+ }
+
+ private static void testTF_256_2()
+ throws IOException
+ {
+ byte[] key = Hex.decode("101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f");
+ byte[] tweak = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] plaintext = Hex.decode("FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0");
+ byte[] expected = Hex.decode("e0d091ff0eea8fdfc98192e62ed80ad59d865d08588df476657056b5955e97df");
+
+ runTestVector("Threefish-256-2: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256));
+ }
+
+ private static void testTF_1024_2()
+ throws IOException
+ {
+ byte[] key = Hex.decode("101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f");
+ byte[] tweak = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] plaintext = Hex.decode("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180");
+ byte[] expected = Hex.decode("a6654ddbd73cc3b05dd777105aa849bce49372eaaffc5568d254771bab85531c94f780e7ffaae430d5d8af8c70eebbe1760f3b42b737a89cb363490d670314bd8aa41ee63c2e1f45fbd477922f8360b388d6125ea6c7af0ad7056d01796e90c83313f4150a5716b30ed5f569288ae974ce2b4347926fce57de44512177dd7cde");
+
+ runTestVector("Threefish-1024-2: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024));
+ }
+
+ private static void speedTestCipher(BlockCipher cipher, byte[] input)
+ throws InterruptedException
+ {
+ byte[] key = new byte[cipher.getBlockSize()];
+ rand.nextBytes(key);
+
+ cipher.init(true, new KeyParameter(key));
+ speedTestCipherForMode("encrypt", cipher, input);
+ cipher.init(false, new KeyParameter(key));
+ speedTestCipherForMode("decrypt", cipher, input);
+ }
+
+ private static void speedTestCipherForMode(String mode, BlockCipher cipher, byte[] input)
+ throws InterruptedException
+ {
+ System.out.println("======");
+ System.out.println("Testing " + cipher.getAlgorithmName() + " " + cipher.getBlockSize() * 8 + " " + mode);
+ System.out.println("Beginning warmup run.");
+
+ long warmup = testCipher(cipher, input);
+ System.out.println("Warmup run 1 in " + (warmup / 1000000) + "ms");
+ Thread.sleep(100);
+ warmup = testCipher(cipher, input);
+ System.out.println("Warmup run 2 in " + (warmup / 1000000) + "ms");
+
+ System.gc();
+ Thread.sleep(500);
+ System.gc();
+ Thread.sleep(500);
+
+ System.out.println("Beginning " + RUNS + " hot runs.");
+
+ long[] runtimes = new long[RUNS];
+ long total = 0;
+ for (int i = 0; i < RUNS; i++)
+ {
+ runtimes[i] = testCipher(cipher, input);
+ total += runtimes[i];
+ System.out.println("Run " + (i + 1) + ": " + runtimes[i] / 100000 + "ms");
+ }
+ long averageRuntime = total / RUNS;
+ System.out.println(cipher.getAlgorithmName() + " Average run time: " + averageRuntime / 1000000 + "ms");
+ final long mbPerSecond = (long)((double)DATA_SIZE / averageRuntime * 1000000000 / (1024 * 1024));
+ System.out.println(cipher.getAlgorithmName() + " Average speed: " + mbPerSecond + " MB/s");
+ System.out.println(cipher.getAlgorithmName() + " Average speed: " + CLOCK_SPEED / (double)(mbPerSecond * (1024 * 1024)) + " c/b");
+ }
+
+ private static long testCipher(BlockCipher cipher, byte[] input)
+ {
+ long start = System.nanoTime();
+ int blockSize = cipher.getBlockSize();
+ byte[] out = new byte[blockSize];
+
+ for (int i = 0; i < (input.length - blockSize); i += blockSize)
+ {
+ cipher.processBlock(input, i, out, 0);
+// byte[] test = new byte[blockSize];
+// System.arraycopy(input, i, test, 0, test.length);
+// if (!Arrays.equals(out, test)) {
+// System.err.println(":(");
+// }
+ }
+
+ long end = System.nanoTime();
+ long delta = end - start;
+ return delta;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/AllTests.java
new file mode 100644
index 000000000..6970aa1d4
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/AllTests.java
@@ -0,0 +1,23 @@
+package org.spongycastle.crypto.tls.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests
+{
+ public static void main(String[] args)
+ throws Exception
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ throws Exception
+ {
+ TestSuite suite = new TestSuite("TLS tests");
+
+ suite.addTest(BasicTlsTest.suite());
+
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/BasicTlsTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/BasicTlsTest.java
new file mode 100644
index 000000000..911949920
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/BasicTlsTest.java
@@ -0,0 +1,200 @@
+package org.spongycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.spongycastle.crypto.tls.AlertDescription;
+import org.spongycastle.crypto.tls.AlertLevel;
+import org.spongycastle.crypto.tls.AlwaysValidVerifyer;
+import org.spongycastle.crypto.tls.Certificate;
+import org.spongycastle.crypto.tls.CipherSuite;
+import org.spongycastle.crypto.tls.DefaultTlsClient;
+import org.spongycastle.crypto.tls.LegacyTlsClient;
+import org.spongycastle.crypto.tls.TlsAuthentication;
+import org.spongycastle.crypto.tls.TlsClient;
+import org.spongycastle.crypto.tls.TlsClientProtocol;
+import org.spongycastle.crypto.tls.TlsFatalAlert;
+import org.spongycastle.crypto.tls.TlsKeyExchange;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+
+public class BasicTlsTest
+ extends TestCase
+{
+ private static final int PORT_NO = 8003;
+
+ // private static final String CLIENT = "client";
+ // private static final char[] CLIENT_PASSWORD = "clientPassword".toCharArray();
+ // private static final char[] SERVER_PASSWORD = "serverPassword".toCharArray();
+ // private static final char[] TRUST_STORE_PASSWORD = "trustPassword".toCharArray();
+
+ public void testConnection()
+ throws Exception
+ {
+ String vmVersion = System.getProperty("java.specification.version");
+
+ if (vmVersion == null || !vmVersion.equals("1.7"))
+ {
+ return; // only works on later VMs.
+ }
+
+ Thread server = new HTTPSServerThread();
+
+ server.start();
+
+ Thread.yield();
+
+ AlwaysValidVerifyer verifyer = new AlwaysValidVerifyer();
+ Socket s = null;
+
+ for (int i = 0; s == null && i != 3; i++)
+ {
+ Thread.sleep(1000);
+
+ try
+ {
+ s = new Socket("localhost", PORT_NO);
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ }
+
+ if (s == null)
+ {
+ throw new IOException("unable to connect");
+ }
+
+ // long time = System.currentTimeMillis();
+ TlsClientProtocol protocol = new TlsClientProtocol(s.getInputStream(), s.getOutputStream());
+ protocol.connect(new LegacyTlsClient(verifyer));
+ InputStream is = protocol.getInputStream();
+ OutputStream os = protocol.getOutputStream();
+
+ os.write("GET / HTTP/1.1\r\n\r\n".getBytes());
+
+ // time = System.currentTimeMillis();
+ byte[] buf = new byte[4096];
+ int read = 0;
+ int total = 0;
+
+ while ((read = is.read(buf, total, buf.length - total)) > 0)
+ {
+ total += read;
+ }
+
+ is.close();
+
+ byte[] expected = Hex.decode("485454502f312e3120323030204f4b0d0a436f6e74656e742d547970653a20746578742f68"
+ + "746d6c0d0a0d0a3c68746d6c3e0d0a3c626f64793e0d0a48656c6c6f20576f726c64210d0a3c2f626f64793e0d0a3c2f"
+ + "68746d6c3e0d0a");
+ assertEquals(total, expected.length);
+
+ byte[] tmp = new byte[expected.length];
+ System.arraycopy(buf, 0, tmp, 0, total);
+ assertTrue(Arrays.areEqual(expected, tmp));
+ }
+
+ public void testRSAConnectionClient()
+ throws Exception
+ {
+ MyTlsClient client = new MyTlsClient(null);
+
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, TlsTestUtils.rsaCertData);
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, TlsTestUtils.rsaCertData);
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, TlsTestUtils.rsaCertData);
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_RC4_128_SHA, TlsTestUtils.rsaCertData);
+
+ try
+ {
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, TlsTestUtils.dudRsaCertData);
+
+ fail("dud certificate not caught");
+ }
+ catch (TlsFatalAlert e)
+ {
+ assertEquals(AlertDescription.certificate_unknown, e.getAlertDescription());
+ }
+
+ try
+ {
+ checkConnectionClient(client, CipherSuite.TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, TlsTestUtils.rsaCertData);
+
+ fail("wrong certificate not caught");
+ }
+ catch (TlsFatalAlert e)
+ {
+ assertEquals(AlertDescription.internal_error, e.getAlertDescription());
+ }
+ }
+
+ private void checkConnectionClient(TlsClient client, int cipherSuite, byte[] encCert)
+ throws Exception
+ {
+ client.notifySelectedCipherSuite(cipherSuite);
+
+ TlsKeyExchange keyExchange = client.getKeyExchange();
+
+ keyExchange
+ .processServerCertificate(new Certificate(
+ new org.spongycastle.asn1.x509.Certificate[]{org.spongycastle.asn1.x509.Certificate
+ .getInstance(encCert)}));
+ }
+
+ public static TestSuite suite()
+ {
+ return new TestSuite(BasicTlsTest.class);
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ static class MyTlsClient
+ extends DefaultTlsClient
+ {
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client raised alert (AlertLevel." + alertLevel + ", AlertDescription." + alertDescription
+ + ")");
+ if (message != null)
+ {
+ out.println(message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client received alert (AlertLevel." + alertLevel + ", AlertDescription."
+ + alertDescription + ")");
+ }
+
+ private final TlsAuthentication authentication;
+
+ MyTlsClient(TlsAuthentication authentication)
+ {
+ this.authentication = authentication;
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return authentication;
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/DTLSClientTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/DTLSClientTest.java
new file mode 100644
index 000000000..d091a1326
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/crypto/tls/test/DTLSClientTest.java
@@ -0,0 +1,80 @@
+package org.spongycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.tls.DTLSClientProtocol;
+import org.spongycastle.crypto.tls.DTLSTransport;
+import org.spongycastle.crypto.tls.DatagramTransport;
+import org.spongycastle.crypto.tls.TlsClient;
+import org.spongycastle.crypto.tls.TlsSession;
+import org.spongycastle.crypto.tls.UDPTransport;
+
+/**
+ * A simple test designed to conduct a DTLS handshake with an external DTLS server.
+ * Fp
.
+ */
+ public static class Fp
+ {
+ private final BigInteger q = new BigInteger("29");
+
+ private final BigInteger a = new BigInteger("4");
+
+ private final BigInteger b = new BigInteger("20");
+
+ private final ECCurve curve = new ECCurve.Fp(q, a, b);
+
+ private final ECPoint infinity = curve.getInfinity();
+
+ private final int[] pointSource = { 5, 22, 16, 27, 13, 6, 14, 6 };
+
+ private ECPoint[] p = new ECPoint[pointSource.length / 2];
+
+ /**
+ * Creates the points on the curve with literature values.
+ */
+ private void createPoints()
+ {
+ for (int i = 0; i < pointSource.length / 2; i++)
+ {
+ p[i] = curve.createPoint(
+ new BigInteger(Integer.toString(pointSource[2 * i])),
+ new BigInteger(Integer.toString(pointSource[2 * i + 1])));
+ }
+ }
+ }
+
+ /**
+ * Nested class containing sample literature values for F2m
.
+ */
+ public static class F2m
+ {
+ // Irreducible polynomial for TPB z^4 + z + 1
+ private final int m = 4;
+
+ private final int k1 = 1;
+
+ // a = z^3
+ private final BigInteger aTpb = new BigInteger("1000", 2);
+
+ // b = z^3 + 1
+ private final BigInteger bTpb = new BigInteger("1001", 2);
+
+ private final ECCurve.F2m curve = new ECCurve.F2m(m, k1, aTpb, bTpb);
+
+ private final ECPoint.F2m infinity = (ECPoint.F2m) curve.getInfinity();
+
+ private final String[] pointSource = { "0010", "1111", "1100", "1100",
+ "0001", "0001", "1011", "0010" };
+
+ private ECPoint[] p = new ECPoint[pointSource.length / 2];
+
+ /**
+ * Creates the points on the curve with literature values.
+ */
+ private void createPoints()
+ {
+ for (int i = 0; i < pointSource.length / 2; i++)
+ {
+ p[i] = curve.createPoint(
+ new BigInteger(pointSource[2 * i], 2),
+ new BigInteger(pointSource[2 * i + 1], 2));
+ }
+ }
+ }
+
+ public void setUp()
+ {
+ fp = new ECPointTest.Fp();
+ fp.createPoints();
+
+ f2m = new ECPointTest.F2m();
+ f2m.createPoints();
+ }
+
+ /**
+ * Tests, if inconsistent points can be created, i.e. points with exactly
+ * one null coordinate (not permitted).
+ */
+ public void testPointCreationConsistency()
+ {
+ try
+ {
+ ECPoint bad = fp.curve.createPoint(new BigInteger("12"), null);
+ fail();
+ }
+ catch (IllegalArgumentException expected)
+ {
+ }
+
+ try
+ {
+ ECPoint bad = fp.curve.createPoint(null, new BigInteger("12"));
+ fail();
+ }
+ catch (IllegalArgumentException expected)
+ {
+ }
+
+ try
+ {
+ ECPoint bad = f2m.curve.createPoint(new BigInteger("1011"), null);
+ fail();
+ }
+ catch (IllegalArgumentException expected)
+ {
+ }
+
+ try
+ {
+ ECPoint bad = f2m.curve.createPoint(null, new BigInteger("1011"));
+ fail();
+ }
+ catch (IllegalArgumentException expected)
+ {
+ }
+ }
+
+ /**
+ * Tests ECPoint.add()
against literature values.
+ *
+ * @param p
+ * The array of literature values.
+ * @param infinity
+ * The point at infinity on the respective curve.
+ */
+ private void implTestAdd(ECPoint[] p, ECPoint infinity)
+ {
+ assertPointsEqual("p0 plus p1 does not equal p2", p[2], p[0].add(p[1]));
+ assertPointsEqual("p1 plus p0 does not equal p2", p[2], p[1].add(p[0]));
+ for (int i = 0; i < p.length; i++)
+ {
+ assertPointsEqual("Adding infinity failed", p[i], p[i].add(infinity));
+ assertPointsEqual("Adding to infinity failed", p[i], infinity.add(p[i]));
+ }
+ }
+
+ /**
+ * Calls implTestAdd()
for Fp
and
+ * F2m
.
+ */
+ public void testAdd()
+ {
+ implTestAdd(fp.p, fp.infinity);
+ implTestAdd(f2m.p, f2m.infinity);
+ }
+
+ /**
+ * Tests ECPoint.twice()
against literature values.
+ *
+ * @param p
+ * The array of literature values.
+ */
+ private void implTestTwice(ECPoint[] p)
+ {
+ assertPointsEqual("Twice incorrect", p[3], p[0].twice());
+ assertPointsEqual("Add same point incorrect", p[3], p[0].add(p[0]));
+ }
+
+ /**
+ * Calls implTestTwice()
for Fp
and
+ * F2m
.
+ */
+ public void testTwice()
+ {
+ implTestTwice(fp.p);
+ implTestTwice(f2m.p);
+ }
+
+ private void implTestThreeTimes(ECPoint[] p)
+ {
+ ECPoint P = p[0];
+ ECPoint _3P = P.add(P).add(P);
+ assertPointsEqual("ThreeTimes incorrect", _3P, P.threeTimes());
+ assertPointsEqual("TwicePlus incorrect", _3P, P.twicePlus(P));
+ }
+
+ /**
+ * Calls implTestThreeTimes()
for Fp
and
+ * F2m
.
+ */
+ public void testThreeTimes()
+ {
+ implTestThreeTimes(fp.p);
+ implTestThreeTimes(f2m.p);
+ }
+
+ /**
+ * Goes through all points on an elliptic curve and checks, if adding a
+ * point k
-times is the same as multiplying the point by
+ * k
, for all k
. Should be called for points
+ * on very small elliptic curves only.
+ *
+ * @param p
+ * The base point on the elliptic curve.
+ * @param infinity
+ * The point at infinity on the elliptic curve.
+ */
+ private void implTestAllPoints(ECPoint p, ECPoint infinity)
+ {
+ ECPoint adder = infinity;
+ ECPoint multiplier = infinity;
+
+ BigInteger i = BigInteger.valueOf(1);
+ do
+ {
+ adder = adder.add(p);
+ multiplier = p.multiply(i);
+ assertPointsEqual("Results of add() and multiply() are inconsistent "
+ + i, adder, multiplier);
+ i = i.add(BigInteger.ONE);
+ }
+ while (!(adder.equals(infinity)));
+ }
+
+ /**
+ * Calls implTestAllPoints()
for the small literature curves,
+ * both for Fp
and F2m
.
+ */
+ public void testAllPoints()
+ {
+ for (int i = 0; i < fp.p.length; i++)
+ {
+ implTestAllPoints(fp.p[0], fp.infinity);
+ }
+
+ for (int i = 0; i < f2m.p.length; i++)
+ {
+ implTestAllPoints(f2m.p[0], f2m.infinity);
+ }
+ }
+
+ /**
+ * Simple shift-and-add multiplication. Serves as reference implementation
+ * to verify (possibly faster) implementations in
+ * {@link org.spongycastle.math.ec.ECPoint ECPoint}.
+ *
+ * @param p
+ * The point to multiply.
+ * @param k
+ * The multiplier.
+ * @return The result of the point multiplication kP
.
+ */
+ private ECPoint multiply(ECPoint p, BigInteger k)
+ {
+ ECPoint q = p.getCurve().getInfinity();
+ int t = k.bitLength();
+ for (int i = 0; i < t; i++)
+ {
+ if (i != 0)
+ {
+ p = p.twice();
+ }
+ if (k.testBit(i))
+ {
+ q = q.add(p);
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Checks, if the point multiplication algorithm of the given point yields
+ * the same result as point multiplication done by the reference
+ * implementation given in multiply()
. This method chooses a
+ * random number by which the given point p
is multiplied.
+ *
+ * @param p
+ * The point to be multiplied.
+ * @param numBits
+ * The bitlength of the random number by which p
+ * is multiplied.
+ */
+ private void implTestMultiply(ECPoint p, int numBits)
+ {
+ BigInteger k = new BigInteger(numBits, secRand);
+ ECPoint ref = multiply(p, k);
+ ECPoint q = p.multiply(k);
+ assertPointsEqual("ECPoint.multiply is incorrect", ref, q);
+ }
+
+ /**
+ * Checks, if the point multiplication algorithm of the given point yields
+ * the same result as point multiplication done by the reference
+ * implementation given in multiply()
. This method tests
+ * multiplication of p
by every number of bitlength
+ * numBits
or less.
+ *
+ * @param p
+ * The point to be multiplied.
+ * @param numBits
+ * Try every multiplier up to this bitlength
+ */
+ private void implTestMultiplyAll(ECPoint p, int numBits)
+ {
+ BigInteger bound = BigInteger.ONE.shiftLeft(numBits);
+ BigInteger k = BigInteger.ZERO;
+
+ do
+ {
+ ECPoint ref = multiply(p, k);
+ ECPoint q = p.multiply(k);
+ assertPointsEqual("ECPoint.multiply is incorrect", ref, q);
+ k = k.add(BigInteger.ONE);
+ }
+ while (k.compareTo(bound) < 0);
+ }
+
+ /**
+ * Tests ECPoint.add()
and ECPoint.subtract()
+ * for the given point and the given point at infinity.
+ *
+ * @param p
+ * The point on which the tests are performed.
+ * @param infinity
+ * The point at infinity on the same curve as p
.
+ */
+ private void implTestAddSubtract(ECPoint p, ECPoint infinity)
+ {
+ assertPointsEqual("Twice and Add inconsistent", p.twice(), p.add(p));
+ assertPointsEqual("Twice p - p is not p", p, p.twice().subtract(p));
+ assertPointsEqual("TwicePlus(p, -p) is not p", p, p.twicePlus(p.negate()));
+ assertPointsEqual("p - p is not infinity", infinity, p.subtract(p));
+ assertPointsEqual("p plus infinity is not p", p, p.add(infinity));
+ assertPointsEqual("infinity plus p is not p", p, infinity.add(p));
+ assertPointsEqual("infinity plus infinity is not infinity ", infinity, infinity.add(infinity));
+ assertPointsEqual("Twice infinity is not infinity ", infinity, infinity.twice());
+ }
+
+ /**
+ * Calls implTestAddSubtract()
for literature values, both
+ * for Fp
and F2m
.
+ */
+ public void testAddSubtractMultiplySimple()
+ {
+ for (int iFp = 0; iFp < fp.pointSource.length / 2; iFp++)
+ {
+ implTestAddSubtract(fp.p[iFp], fp.infinity);
+
+ // Could be any numBits, 6 is chosen at will
+ implTestMultiplyAll(fp.p[iFp], 6);
+ implTestMultiplyAll(fp.infinity, 6);
+ }
+
+ for (int iF2m = 0; iF2m < f2m.pointSource.length / 2; iF2m++)
+ {
+ implTestAddSubtract(f2m.p[iF2m], f2m.infinity);
+
+ // Could be any numBits, 6 is chosen at will
+ implTestMultiplyAll(f2m.p[iF2m], 6);
+ implTestMultiplyAll(f2m.infinity, 6);
+ }
+ }
+
+ /**
+ * Test encoding with and without point compression.
+ *
+ * @param p
+ * The point to be encoded and decoded.
+ */
+ private void implTestEncoding(ECPoint p)
+ {
+ // Not Point Compression
+ ECPoint unCompP = p.getCurve().createPoint(p.getAffineXCoord().toBigInteger(), p.getAffineYCoord().toBigInteger(), false);
+
+ // Point compression
+ ECPoint compP = p.getCurve().createPoint(p.getAffineXCoord().toBigInteger(), p.getAffineYCoord().toBigInteger(), true);
+
+ byte[] unCompBarr = unCompP.getEncoded();
+ ECPoint decUnComp = p.getCurve().decodePoint(unCompBarr);
+ assertPointsEqual("Error decoding uncompressed point", p, decUnComp);
+
+ byte[] compBarr = compP.getEncoded();
+ ECPoint decComp = p.getCurve().decodePoint(compBarr);
+ assertPointsEqual("Error decoding compressed point", p, decComp);
+ }
+
+ /**
+ * Calls implTestAddSubtract()
,
+ * implTestMultiply
and implTestEncoding
for
+ * the standard elliptic curves as given in SECNamedCurves
.
+ */
+ public void testAddSubtractMultiplyTwiceEncoding()
+ {
+ Enumeration curveEnum = SECNamedCurves.getNames();
+ while (curveEnum.hasMoreElements())
+ {
+ String name = (String) curveEnum.nextElement();
+ X9ECParameters x9ECParameters = SECNamedCurves.getByName(name);
+
+ BigInteger n = x9ECParameters.getN();
+
+ // The generator is multiplied by random b to get random q
+ BigInteger b = new BigInteger(n.bitLength(), secRand);
+ ECPoint g = x9ECParameters.getG();
+ ECPoint q = g.multiply(b).normalize();
+
+ // Get point at infinity on the curve
+ ECPoint infinity = x9ECParameters.getCurve().getInfinity();
+
+ implTestAddSubtract(q, infinity);
+ implTestMultiply(q, n.bitLength());
+ implTestMultiply(infinity, n.bitLength());
+ implTestEncoding(q);
+ }
+ }
+
+ private void assertPointsEqual(String message, ECPoint a, ECPoint b)
+ {
+ assertEquals(message, a, b);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(ECPointTest.class);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/math/ec/test/F2mProofer.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/math/ec/test/F2mProofer.java
new file mode 100644
index 000000000..9d7e5195b
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/math/ec/test/F2mProofer.java
@@ -0,0 +1,203 @@
+package org.spongycastle.math.ec.test;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.spongycastle.asn1.sec.SECNamedCurves;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+
+public class F2mProofer
+{
+ private static final int NUM_SAMPLES = 1000;
+
+ private static final String PATH = "crypto/test/src/org/spongycastle/math/ec/test/samples/";
+
+ private static final String INPUT_FILE_NAME_PREFIX = "Input_";
+
+ private static final String RESULT_FILE_NAME_PREFIX = "Output_";
+
+ /**
+ * The standard curves on which the tests are done
+ */
+ public static final String[] CURVES = { "sect163r2", "sect233r1",
+ "sect283r1", "sect409r1", "sect571r1" };
+
+ private String pointToString(ECPoint.F2m p)
+ {
+ ECFieldElement.F2m x = (ECFieldElement.F2m) p.getAffineXCoord();
+ ECFieldElement.F2m y = (ECFieldElement.F2m) p.getAffineYCoord();
+
+ int m = x.getM();
+ int len = m / 2 + 5;
+
+ StringBuffer sb = new StringBuffer(len);
+ sb.append('(');
+ sb.append(x.toBigInteger().toString(16));
+ sb.append(", ");
+ sb.append(y.toBigInteger().toString(16));
+ sb.append(')');
+
+ return sb.toString();
+ }
+
+ private void generateRandomInput(X9ECParameters x9ECParameters)
+ throws NoSuchAlgorithmException, IOException
+ {
+ ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG();
+ int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM();
+
+ SecureRandom secRand = SecureRandom.getInstance("SHA1PRNG");
+ Properties inputProps = new Properties();
+ for (int i = 0; i < NUM_SAMPLES; i++)
+ {
+ BigInteger rand = new BigInteger(m, secRand);
+ inputProps.put(Integer.toString(i), rand.toString(16));
+ }
+ String bits = Integer.toString(m);
+ FileOutputStream fos = new FileOutputStream(PATH
+ + INPUT_FILE_NAME_PREFIX + bits + ".properties");
+ inputProps.store(fos, "Input Samples of length" + bits);
+ }
+
+ private void multiplyPoints(X9ECParameters x9ECParameters,
+ String classPrefix) throws IOException
+ {
+ ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG();
+ int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM();
+
+ String inputFileName = PATH + INPUT_FILE_NAME_PREFIX + m
+ + ".properties";
+ Properties inputProps = new Properties();
+ inputProps.load(new FileInputStream(inputFileName));
+
+ Properties outputProps = new Properties();
+
+ for (int i = 0; i < NUM_SAMPLES; i++)
+ {
+ BigInteger rand = new BigInteger(inputProps.getProperty(Integer
+ .toString(i)), 16);
+ ECPoint.F2m result = (ECPoint.F2m) g.multiply(rand).normalize();
+ String resultStr = pointToString(result);
+ outputProps.setProperty(Integer.toString(i), resultStr);
+ }
+
+ String outputFileName = PATH + RESULT_FILE_NAME_PREFIX + classPrefix
+ + "_" + m + ".properties";
+ FileOutputStream fos = new FileOutputStream(outputFileName);
+ outputProps.store(fos, "Output Samples of length" + m);
+ }
+
+ private Properties loadResults(String classPrefix, int m)
+ throws IOException
+ {
+ FileInputStream fis = new FileInputStream(PATH
+ + RESULT_FILE_NAME_PREFIX + classPrefix + "_" + m + ".properties");
+ Properties res = new Properties();
+ res.load(fis);
+ return res;
+
+ }
+
+ private void compareResult(X9ECParameters x9ECParameters,
+ String classPrefix1, String classPrefix2) throws IOException
+ {
+ ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG();
+ int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM();
+
+ Properties res1 = loadResults(classPrefix1, m);
+ Properties res2 = loadResults(classPrefix2, m);
+
+ Set keys = res1.keySet();
+ Iterator iter = keys.iterator();
+ while (iter.hasNext())
+ {
+ String key = (String) iter.next();
+ String result1 = res1.getProperty(key);
+ String result2 = res2.getProperty(key);
+ if (!(result1.equals(result2)))
+ {
+ System.err.println("Difference found: m = " + m + ", "
+ + result1 + " does not equal " + result2);
+ }
+ }
+
+ }
+
+ private static void usage()
+ {
+ System.err.println("Usage: F2mProofer [-init | -multiply N
coefficients
+ * between 0
and q-1
.
+ *
+ * @param N length of the polynomial
+ * @param q coefficients will all be below this number
+ * @return a random polynomial
+ */
+ public static IntegerPolynomial generateRandom(int N, int q)
+ {
+ Random rng = new Random();
+ int[] coeffs = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = rng.nextInt(q);
+ }
+ return new IntegerPolynomial(coeffs);
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/polynomial/test/ProductFormPolynomialTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/polynomial/test/ProductFormPolynomialTest.java
new file mode 100644
index 000000000..96506ed47
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/polynomial/test/ProductFormPolynomialTest.java
@@ -0,0 +1,47 @@
+package org.spongycastle.pqc.math.ntru.polynomial.test;
+
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.spongycastle.pqc.crypto.ntru.NTRUEncryptionKeyGenerationParameters;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+
+public class ProductFormPolynomialTest
+ extends TestCase
+{
+ private NTRUEncryptionKeyGenerationParameters params;
+ private int N;
+ private int df1;
+ private int df2;
+ private int df3;
+ private int q;
+
+ public void setUp()
+ {
+ params = NTRUEncryptionKeyGenerationParameters.APR2011_439_FAST;
+ N = params.N;
+ df1 = params.df1;
+ df2 = params.df2;
+ df3 = params.df3;
+ q = params.q;
+ }
+
+ public void testFromToBinary()
+ throws Exception
+ {
+ ProductFormPolynomial p1 = ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3 - 1, new SecureRandom());
+ byte[] bin1 = p1.toBinary();
+ ProductFormPolynomial p2 = ProductFormPolynomial.fromBinary(bin1, N, df1, df2, df3, df3 - 1);
+ assertEquals(p1, p2);
+ }
+
+ public void testMult()
+ {
+ ProductFormPolynomial p1 = ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3 - 1, new SecureRandom());
+ IntegerPolynomial p2 = PolynomialGenerator.generateRandom(N, q);
+ IntegerPolynomial p3 = p1.mult(p2);
+ IntegerPolynomial p4 = p1.toIntegerPolynomial().mult(p2);
+ assertEquals(p3, p4);
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/polynomial/test/SparseTernaryPolynomialTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/polynomial/test/SparseTernaryPolynomialTest.java
new file mode 100644
index 000000000..a4d9ce7e9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/polynomial/test/SparseTernaryPolynomialTest.java
@@ -0,0 +1,45 @@
+package org.spongycastle.pqc.math.ntru.polynomial.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.spongycastle.pqc.math.ntru.polynomial.BigIntPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+public class SparseTernaryPolynomialTest
+ extends TestCase
+{
+
+ /**
+ * tests mult(IntegerPolynomial) and mult(BigIntPolynomial)
+ */
+ public void testMult()
+ {
+ SecureRandom random = new SecureRandom();
+ SparseTernaryPolynomial p1 = SparseTernaryPolynomial.generateRandom(1000, 500, 500, random);
+ IntegerPolynomial p2 = DenseTernaryPolynomial.generateRandom(1000, random);
+
+ IntegerPolynomial prod1 = p1.mult(p2);
+ IntegerPolynomial prod2 = p1.mult(p2);
+ assertEquals(prod1, prod2);
+
+ BigIntPolynomial p3 = new BigIntPolynomial(p2);
+ BigIntPolynomial prod3 = p1.mult(p3);
+
+ assertEquals(new BigIntPolynomial(prod1), prod3);
+ }
+
+ public void testFromToBinary()
+ throws IOException
+ {
+ SecureRandom random = new SecureRandom();
+ SparseTernaryPolynomial poly1 = SparseTernaryPolynomial.generateRandom(1000, 100, 101, random);
+ ByteArrayInputStream poly1Stream = new ByteArrayInputStream(poly1.toBinary());
+ SparseTernaryPolynomial poly2 = SparseTernaryPolynomial.fromBinary(poly1Stream, 1000, 100, 101);
+ assertEquals(poly1, poly2);
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/util/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/util/test/AllTests.java
new file mode 100644
index 000000000..ff675a73f
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/util/test/AllTests.java
@@ -0,0 +1,23 @@
+package org.spongycastle.pqc.math.ntru.util.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class AllTests
+ extends TestCase
+{
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("NTRU ArrayEncoder Tests");
+
+ suite.addTestSuite(ArrayEncoderTest.class);
+
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/util/test/ArrayEncoderTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/util/test/ArrayEncoderTest.java
new file mode 100644
index 000000000..ebd96d1c0
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/pqc/math/ntru/util/test/ArrayEncoderTest.java
@@ -0,0 +1,42 @@
+package org.spongycastle.pqc.math.ntru.util.test;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+import junit.framework.TestCase;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.test.PolynomialGenerator;
+import org.spongycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.spongycastle.util.Arrays;
+
+public class ArrayEncoderTest
+ extends TestCase
+{
+ public void testEncodeDecodeModQ()
+ {
+ int[] coeffs = PolynomialGenerator.generateRandom(1000, 2048).coeffs;
+ byte[] data = ArrayEncoder.encodeModQ(coeffs, 2048);
+ int[] coeffs2 = ArrayEncoder.decodeModQ(data, 1000, 2048);
+ assertTrue(Arrays.areEqual(coeffs, coeffs2));
+ }
+
+ public void testEncodeDecodeMod3Sves()
+ {
+ Random rng = new Random();
+ byte[] data = new byte[180];
+ rng.nextBytes(data);
+ int[] coeffs = ArrayEncoder.decodeMod3Sves(data, 960);
+ byte[] data2 = ArrayEncoder.encodeMod3Sves(coeffs);
+ assertTrue(Arrays.areEqual(data, data2));
+ }
+
+ public void testEncodeDecodeMod3Tight()
+ {
+ SecureRandom random = new SecureRandom();
+
+ int[] coeffs = DenseTernaryPolynomial.generateRandom(1000, random).coeffs;
+ byte[] data = ArrayEncoder.encodeMod3Tight(coeffs);
+ int[] coeffs2 = ArrayEncoder.decodeMod3Tight(data, 1000);
+ assertTrue(Arrays.areEqual(coeffs, coeffs2));
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/AbstractCoderTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/AbstractCoderTest.java
new file mode 100644
index 000000000..03f0bf771
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/AbstractCoderTest.java
@@ -0,0 +1,211 @@
+package org.spongycastle.util.encoders.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.spongycastle.util.encoders.Encoder;
+
+public abstract class AbstractCoderTest extends TestCase
+{
+
+ private static final int[] SIZES_TO_CHECK = {64, 128, 1024, 1025, 1026, 2048,
+ 2049, 2050, 4096, 4097, 4098, 8192, 8193, 8194};
+
+ protected Encoder enc;
+ private Random r;
+
+ AbstractCoderTest(
+ String name)
+ {
+ super(name);
+ }
+
+ protected void setUp()
+ {
+ r = new Random();
+ }
+
+ private void checkArrayOfSize(int size)
+ throws IOException
+ {
+ byte[] original = new byte[size];
+ r.nextBytes(original);
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ enc.encode(original, 0, original.length, bOut);
+
+ byte[] encoded = bOut.toByteArray();
+
+ assertTrue(encoded.length > original.length);
+ assertTrue(encoded.length <= (original.length * 2));
+ checkEncoding(encoded);
+ checkSimpleDecode(original, encoded);
+ checkStringDecode(original, encoded);
+ checkOutputStreamDecode(original, encoded);
+
+ int offset = r.nextInt(20);
+ byte[] offsetEncoded = new byte[offset + encoded.length];
+ System.arraycopy(encoded, 0, offsetEncoded, offset, encoded.length);
+ checkOffsetDecode(original, offsetEncoded, offset, encoded.length);
+
+ offset = r.nextInt(20);
+ byte[] offsetOriginal = new byte[offset + original.length];
+ System.arraycopy(original, 0, offsetOriginal, offset, original.length);
+ checkOffsetEncode(original, offsetOriginal, offset, original.length);
+
+ byte[] encodedWithSpace = addWhitespace(encoded);
+ checkSimpleDecode(original, encodedWithSpace);
+ checkStringDecode(original, encodedWithSpace);
+ checkOutputStreamDecode(original, encodedWithSpace);
+ }
+
+ public void testEncode()
+ throws IOException
+ {
+ for (int i = 0; i < SIZES_TO_CHECK.length; i++)
+ {
+ checkArrayOfSize(SIZES_TO_CHECK[i]);
+ }
+ }
+
+ private void checkEncoding(byte[] encoded)
+ {
+ String encString = convertBytesToString(encoded);
+ for (int i = 0; i < encString.length(); i++)
+ {
+ char c = encString.charAt(i);
+ if (c == paddingChar())
+ {
+ // should only be padding at end of string
+ assertTrue(i > encString.length() - 3);
+ continue;
+ }
+ else if (isEncodedChar(c))
+ {
+ continue;
+ }
+ fail("Unexpected encoded character " + c);
+ }
+ }
+
+ private void checkOutputStreamDecode(byte[] original, byte[] encoded)
+ {
+ String encString = convertBytesToString(encoded);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try
+ {
+ assertEquals(original.length, enc.decode(encString, out));
+ assertTrue(Arrays.equals(original, out.toByteArray()));
+ }
+ catch (IOException e)
+ {
+ fail("This shouldn't happen");
+ }
+ }
+
+ private void checkSimpleDecode(byte[] original, byte[] encoded)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ enc.decode(encoded, 0, encoded.length, bOut);
+
+ assertTrue(Arrays.equals(original, bOut.toByteArray()));
+ }
+
+ private void checkOffsetEncode(byte[] original, byte[] offsetOriginal, int off, int length)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ enc.encode(offsetOriginal, off, length, bOut);
+
+ byte[] encoded = bOut.toByteArray();
+
+ bOut.reset();
+
+ enc.decode(encoded, 0, encoded.length, bOut);
+
+ assertTrue(Arrays.equals(original, bOut.toByteArray()));
+ }
+
+ private void checkOffsetDecode(byte[] original, byte[] encoded, int off, int length)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ enc.decode(encoded, off, length, bOut);
+
+ assertTrue(Arrays.equals(original, bOut.toByteArray()));
+ }
+
+ private void checkStringDecode(byte[] original, byte[] encoded)
+ throws IOException
+ {
+ String encString = convertBytesToString(encoded);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ enc.decode(encString, bOut);
+ assertTrue(Arrays.equals(original, bOut.toByteArray()));
+ }
+
+ private byte[] addWhitespace(byte[] encoded)
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ addSpace(out);
+ for (int i = 0; i < encoded.length - 5; i++)
+ {
+ out.write(encoded, i, 1);
+ if (r.nextInt(100) < 5)
+ {
+ addSpace(out);
+ }
+ }
+ for (int i = encoded.length - 5; i < encoded.length; i++)
+ {
+ out.write(encoded, i, 1);
+ }
+ addSpace(out);
+ return out.toByteArray();
+ }
+
+ private void addSpace(ByteArrayOutputStream out)
+ {
+ do
+ {
+ switch (r.nextInt(3))
+ {
+ case 0 :
+ out.write((int) '\n');
+ break;
+ case 1 :
+ out.write((int) '\r');
+ break;
+ case 2 :
+ out.write((int) '\t');
+ break;
+ case 3 :
+ out.write((int) ' ');
+ break;
+ }
+ } while (r.nextBoolean());
+ }
+
+ private String convertBytesToString(byte[] encoded)
+ {
+ StringBuffer b = new StringBuffer();
+
+ for (int i = 0; i != encoded.length; i++)
+ {
+ b.append((char)(encoded[i] & 0xff));
+ }
+
+ return b.toString();
+ }
+
+ abstract protected char paddingChar();
+
+ abstract protected boolean isEncodedChar(char c);
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/AllTests.java
new file mode 100644
index 000000000..a31c5ad2c
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/AllTests.java
@@ -0,0 +1,20 @@
+package org.spongycastle.util.encoders.test;
+
+import junit.framework.*;
+
+public class AllTests
+{
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run (suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("encoder tests");
+ suite.addTestSuite(Base64Test.class);
+ suite.addTestSuite(UrlBase64Test.class);
+ suite.addTestSuite(HexTest.class);
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/Base64Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/Base64Test.java
new file mode 100644
index 000000000..f74f7eed5
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/Base64Test.java
@@ -0,0 +1,121 @@
+package org.spongycastle.util.encoders.test;
+
+import java.io.IOException;
+
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Base64Encoder;
+import org.spongycastle.util.encoders.DecoderException;
+import org.spongycastle.util.encoders.Hex;
+
+public class Base64Test extends AbstractCoderTest
+{
+ private static final String sample1 = "mO4TyLWG7vjFWdKT8IJcVbZ/jwc=";
+ private static final byte[] sample1Bytes = Hex.decode("98ee13c8b586eef8c559d293f0825c55b67f8f07");
+ private static final String sample2 = "F4I4p8Vf/mS+Kxvri3FPoMcqmJ1f";
+ private static final byte[] sample2Bytes = Hex.decode("178238a7c55ffe64be2b1beb8b714fa0c72a989d5f");
+ private static final String sample3 = "UJmEdJYodqHJmd7Rtv6/OP29/jUEFw==";
+ private static final byte[] sample3Bytes = Hex.decode("50998474962876a1c999ded1b6febf38fdbdfe350417");
+
+ private static final String invalid1 = "%O4TyLWG7vjFWdKT8IJcVbZ/jwc=";
+ private static final String invalid2 = "F%I4p8Vf/mS+Kxvri3FPoMcqmJ1f";
+ private static final String invalid3 = "UJ%EdJYodqHJmd7Rtv6/OP29/jUEFw==";
+ private static final String invalid4 = "mO4%yLWG7vjFWdKT8IJcVbZ/jwc=";
+ private static final String invalid5 = "UJmEdJYodqHJmd7Rtv6/OP29/jUEF%==";
+ private static final String invalid6 = "mO4TyLWG7vjFWdKT8IJcVbZ/jw%=";
+ private static final String invalid7 = "F4I4p8Vf/mS+Kxvri3FPoMcqmJ1%";
+ private static final String invalid8 = "UJmEdJYodqHJmd7Rtv6/OP29/jUE%c==";
+ private static final String invalid9 = "mO4TyLWG7vjFWdKT8IJcVbZ/j%c=";
+ private static final String invalida = "F4I4p8Vf/mS+Kxvri3FPoMcqmJ%1";
+ private static final String invalidb = "UJmEdJYodqHJmd7Rtv6/OP29/jU%Fc==";
+ private static final String invalidc = "mO4TyLWG7vjFWdKT8IJcVbZ/%wc=";
+ private static final String invalidd = "F4I4p8Vf/mS+Kxvri3FPoMcqm%1c";
+
+
+ public Base64Test(
+ String name)
+ {
+ super(name);
+ }
+
+ protected void setUp()
+ {
+ super.setUp();
+ enc = new Base64Encoder();
+ }
+
+ public void testSamples()
+ throws IOException
+ {
+ assertTrue(Arrays.areEqual(sample1Bytes, Base64.decode(sample1)));
+ assertTrue(Arrays.areEqual(sample1Bytes, Base64.decode(Strings.toByteArray(sample1))));
+ assertTrue(Arrays.areEqual(sample2Bytes, Base64.decode(sample2)));
+ assertTrue(Arrays.areEqual(sample2Bytes, Base64.decode(Strings.toByteArray(sample2))));
+ assertTrue(Arrays.areEqual(sample3Bytes, Base64.decode(sample3)));
+ assertTrue(Arrays.areEqual(sample3Bytes, Base64.decode(Strings.toByteArray(sample3))));
+ }
+
+ public void testInvalidInput()
+ throws IOException
+ {
+ String[] invalid = new String[] { invalid1, invalid2, invalid3, invalid4, invalid5, invalid6, invalid7, invalid8, invalid9, invalida, invalidb, invalidc, invalidd };
+
+ for (int i = 0; i != invalid.length; i++)
+ {
+ invalidTest(invalid[i]);
+ invalidTest(Strings.toByteArray(invalid[i]));
+ }
+ }
+
+ private void invalidTest(String data)
+ {
+ try
+ {
+ Base64.decode(data);
+ }
+ catch (DecoderException e)
+ {
+ return;
+ }
+
+ fail("invalid String data parsed");
+ }
+
+ private void invalidTest(byte[] data)
+ {
+ try
+ {
+ Base64.decode(data);
+ }
+ catch (DecoderException e)
+ {
+ return;
+ }
+
+ fail("invalid byte data parsed");
+ }
+
+ protected char paddingChar()
+ {
+ return '=';
+ }
+
+ protected boolean isEncodedChar(char c)
+ {
+ if (Character.isLetterOrDigit(c))
+ {
+ return true;
+ }
+ else if (c == '+')
+ {
+ return true;
+ }
+ else if (c == '/')
+ {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/EncoderTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/EncoderTest.java
new file mode 100644
index 000000000..4e0e79c20
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/EncoderTest.java
@@ -0,0 +1,250 @@
+package org.spongycastle.util.encoders.test;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import org.spongycastle.util.encoders.Base64;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.test.SimpleTest;
+
+public class EncoderTest
+ extends SimpleTest
+{
+ public static final boolean DEBUG = true;
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new EncoderTest());
+ }
+
+ public String getName()
+ {
+ return "Encoder";
+ }
+
+ /*
+ *
+ * TESTS
+ *
+ */
+
+ public void performTest()
+ {
+ testHex();
+ testBase64();
+ testBase64WithNL();
+ }
+
+
+ public void testBase64()
+ {
+ try
+ {
+ Random _r = new Random();
+
+ byte[] _orig1024 = new byte[1024];
+ _r.nextBytes(_orig1024);
+
+ byte[] _orig2048 = new byte[2048];
+ _r.nextBytes(_orig2048);
+
+ byte[] _orig4096 = new byte[4096];
+ _r.nextBytes(_orig4096);
+
+ byte[] _orig8192 = new byte[8192];
+ _r.nextBytes(_orig8192);
+
+ byte[] _enc1024 = Base64.encode(_orig1024);
+ byte[] _enc2048 = Base64.encode(_orig2048);
+ byte[] _enc4096 = Base64.encode(_orig4096);
+ byte[] _enc8192 = Base64.encode(_orig8192);
+
+ byte[] _dec1024 = Base64.decode(_enc1024);
+ byte[] _dec2048 = Base64.decode(_enc2048);
+ byte[] _dec4096 = Base64.decode(_enc4096);
+ byte[] _dec8192 = Base64.decode(_enc8192);
+
+ if(!Arrays.equals(_orig1024, _dec1024))
+ {
+ fail("Failed Base64 test");
+ }
+
+ if(!Arrays.equals(_orig2048, _dec2048))
+ {
+ fail("Failed Base64 test");
+ }
+
+ if(!Arrays.equals(_orig4096, _dec4096))
+ {
+ fail("Failed Base64 test");
+ }
+
+ if(!Arrays.equals(_orig8192, _dec8192))
+ {
+ fail("Failed Base64 test");
+ }
+
+
+
+ byte[] _orig1025 = new byte[1025];
+ _r.nextBytes(_orig1025);
+
+ byte[] _orig2049 = new byte[2049];
+ _r.nextBytes(_orig2049);
+
+ byte[] _orig4097 = new byte[4097];
+ _r.nextBytes(_orig4097);
+
+ byte[] _orig8193 = new byte[8193];
+ _r.nextBytes(_orig8193);
+
+ byte[] _enc1025 = Base64.encode(_orig1025);
+ byte[] _enc2049 = Base64.encode(_orig2049);
+ byte[] _enc4097 = Base64.encode(_orig4097);
+ byte[] _enc8193 = Base64.encode(_orig8193);
+
+ byte[] _dec1025 = Base64.decode(_enc1025);
+ byte[] _dec2049 = Base64.decode(_enc2049);
+ byte[] _dec4097 = Base64.decode(_enc4097);
+ byte[] _dec8193 = Base64.decode(_enc8193);
+
+ if(!Arrays.equals(_orig1025, _dec1025))
+ {
+ fail("Failed Base64 test");
+ }
+
+ if(!Arrays.equals(_orig2049, _dec2049))
+ {
+ fail("Failed Base64 test");
+ }
+
+ if(!Arrays.equals(_orig4097, _dec4097))
+ {
+ fail("Failed Base64 test");
+ }
+
+ if(!Arrays.equals(_orig8193, _dec8193))
+ {
+ fail("Failed Base64 test");
+ }
+ }
+ catch(Exception ex)
+ {
+ fail("Failed Base64 test");
+ }
+ }
+
+ public void testBase64WithNL()
+ {
+ byte[] dec = Base64.decode("SVNC" + "\n" + "QUQ=\n");
+
+ if (dec.length != 5)
+ {
+ fail("got length " + dec.length + " when expecting 10");
+ }
+
+ if (!areEqual(dec, Base64.decode("SVNCQUQ=")))
+ {
+ fail("decodings are not equal");
+ }
+ }
+
+ public void testHex()
+ {
+ try
+ {
+ Random _r = new Random();
+
+ byte[] _orig1024 = new byte[1024];
+ _r.nextBytes(_orig1024);
+
+ byte[] _orig2048 = new byte[2048];
+ _r.nextBytes(_orig2048);
+
+ byte[] _orig4096 = new byte[4096];
+ _r.nextBytes(_orig4096);
+
+ byte[] _orig8192 = new byte[8192];
+ _r.nextBytes(_orig8192);
+
+ byte[] _enc1024 = Hex.encode(_orig1024);
+ byte[] _enc2048 = Hex.encode(_orig2048);
+ byte[] _enc4096 = Hex.encode(_orig4096);
+ byte[] _enc8192 = Hex.encode(_orig8192);
+
+ byte[] _dec1024 = Hex.decode(_enc1024);
+ byte[] _dec2048 = Hex.decode(_enc2048);
+ byte[] _dec4096 = Hex.decode(_enc4096);
+ byte[] _dec8192 = Hex.decode(_enc8192);
+
+ if(!Arrays.equals(_orig1024, _dec1024))
+ {
+ fail("Failed Hex test");
+ }
+
+ if(!Arrays.equals(_orig2048, _dec2048))
+ {
+ fail("Failed Hex test");
+ }
+
+ if(!Arrays.equals(_orig4096, _dec4096))
+ {
+ fail("Failed Hex test");
+ }
+
+ if(!Arrays.equals(_orig8192, _dec8192))
+ {
+ fail("Failed Hex test");
+ }
+
+
+ byte[] _orig1025 = new byte[1025];
+ _r.nextBytes(_orig1025);
+
+ byte[] _orig2049 = new byte[2049];
+ _r.nextBytes(_orig2049);
+
+ byte[] _orig4097 = new byte[4097];
+ _r.nextBytes(_orig4097);
+
+ byte[] _orig8193 = new byte[8193];
+ _r.nextBytes(_orig8193);
+
+ byte[] _enc1025 = Hex.encode(_orig1025);
+ byte[] _enc2049 = Hex.encode(_orig2049);
+ byte[] _enc4097 = Hex.encode(_orig4097);
+ byte[] _enc8193 = Hex.encode(_orig8193);
+
+ byte[] _dec1025 = Hex.decode(_enc1025);
+ byte[] _dec2049 = Hex.decode(_enc2049);
+ byte[] _dec4097 = Hex.decode(_enc4097);
+ byte[] _dec8193 = Hex.decode(_enc8193);
+
+ if(!Arrays.equals(_orig1025, _dec1025))
+ {
+ fail("Failed Hex test");
+ }
+
+ if(!Arrays.equals(_orig2049, _dec2049))
+ {
+ fail("Failed Hex test");
+ }
+
+ if(!Arrays.equals(_orig4097, _dec4097))
+ {
+ fail("Failed Hex test");
+ }
+
+ if(!Arrays.equals(_orig8193, _dec8193))
+ {
+ fail("Failed Hex test");
+ }
+ }
+ catch(Exception ex)
+ {
+ fail("Failed Hex test");
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/HexTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/HexTest.java
new file mode 100644
index 000000000..a0c101bc9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/HexTest.java
@@ -0,0 +1,92 @@
+package org.spongycastle.util.encoders.test;
+
+import java.io.IOException;
+
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.DecoderException;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.encoders.HexEncoder;
+
+public class HexTest extends AbstractCoderTest
+{
+ private static final String invalid1 = "%O4T";
+ private static final String invalid2 = "FZI4";
+ private static final String invalid3 = "ae%E";
+ private static final String invalid4 = "fO4%";
+ private static final String invalid5 = "beefe";
+ private static final String invalid6 = "beefs";
+
+ public HexTest(
+ String name)
+ {
+ super(name);
+ }
+
+ protected void setUp()
+ {
+ super.setUp();
+ enc = new HexEncoder();
+ }
+
+ protected char paddingChar()
+ {
+ return 0;
+ }
+
+ protected boolean isEncodedChar(char c)
+ {
+ if ('A' <= c && c <= 'F')
+ {
+ return true;
+ }
+ if ('a' <= c && c <= 'f')
+ {
+ return true;
+ }
+ if ('0' <= c && c <= '9')
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public void testInvalidInput()
+ throws IOException
+ {
+ String[] invalid = new String[] { invalid1, invalid2, invalid3, invalid4, invalid5, invalid6 };
+
+ for (int i = 0; i != invalid.length; i++)
+ {
+ invalidTest(invalid[i]);
+ invalidTest(Strings.toByteArray(invalid[i]));
+ }
+ }
+
+ private void invalidTest(String data)
+ {
+ try
+ {
+ Hex.decode(data);
+ }
+ catch (DecoderException e)
+ {
+ return;
+ }
+
+ fail("invalid String data parsed");
+ }
+
+ private void invalidTest(byte[] data)
+ {
+ try
+ {
+ Hex.decode(data);
+ }
+ catch (DecoderException e)
+ {
+ return;
+ }
+
+ fail("invalid byte data parsed");
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/UrlBase64Test.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/UrlBase64Test.java
new file mode 100644
index 000000000..9822efea9
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/encoders/test/UrlBase64Test.java
@@ -0,0 +1,119 @@
+package org.spongycastle.util.encoders.test;
+
+import java.io.IOException;
+
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.DecoderException;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.encoders.UrlBase64;
+import org.spongycastle.util.encoders.UrlBase64Encoder;
+
+public class UrlBase64Test extends AbstractCoderTest
+{
+ private static final String sample1 = "mO4TyLWG7vjFWdKT8IJcVbZ_jwc.";
+ private static final byte[] sample1Bytes = Hex.decode("98ee13c8b586eef8c559d293f0825c55b67f8f07");
+ private static final String sample2 = "F4I4p8Vf_mS-Kxvri3FPoMcqmJ1f";
+ private static final byte[] sample2Bytes = Hex.decode("178238a7c55ffe64be2b1beb8b714fa0c72a989d5f");
+ private static final String sample3 = "UJmEdJYodqHJmd7Rtv6_OP29_jUEFw..";
+ private static final byte[] sample3Bytes = Hex.decode("50998474962876a1c999ded1b6febf38fdbdfe350417");
+
+ private static final String invalid1 = "%O4TyLWG7vjFWdKT8IJcVbZ_jwc.";
+ private static final String invalid2 = "F%I4p8Vf_mS-Kxvri3FPoMcqmJ1f";
+ private static final String invalid3 = "UJ%EdJYodqHJmd7Rtv6_OP29_jUEFw..";
+ private static final String invalid4 = "mO4%yLWG7vjFWdKT8IJcVbZ_jwc.";
+ private static final String invalid5 = "UJmEdJYodqHJmd7Rtv6_OP29_jUEF%..";
+ private static final String invalid6 = "mO4TyLWG7vjFWdKT8IJcVbZ_jw%.";
+ private static final String invalid7 = "F4I4p8Vf_mS-Kxvri3FPoMcqmJ1%";
+ private static final String invalid8 = "UJmEdJYodqHJmd7Rtv6_OP29_jUE%c..";
+ private static final String invalid9 = "mO4TyLWG7vjFWdKT8IJcVbZ_j%c.";
+ private static final String invalida = "F4I4p8Vf_mS-Kxvri3FPoMcqmJ%1";
+ private static final String invalidb = "UJmEdJYodqHJmd7Rtv6_OP29_jU%Fc..";
+ private static final String invalidc = "mO4TyLWG7vjFWdKT8IJcVbZ_%wc.";
+ private static final String invalidd = "F4I4p8Vf_mS-Kxvri3FPoMcqm%1c";
+
+ public UrlBase64Test(
+ String name)
+ {
+ super(name);
+ }
+
+ protected void setUp()
+ {
+ super.setUp();
+ enc = new UrlBase64Encoder();
+ }
+
+ public void testSamples()
+ throws IOException
+ {
+ assertTrue(Arrays.areEqual(sample1Bytes, UrlBase64.decode(sample1)));
+ assertTrue(Arrays.areEqual(sample1Bytes, UrlBase64.decode(Strings.toByteArray(sample1))));
+ assertTrue(Arrays.areEqual(sample2Bytes, UrlBase64.decode(sample2)));
+ assertTrue(Arrays.areEqual(sample2Bytes, UrlBase64.decode(Strings.toByteArray(sample2))));
+ assertTrue(Arrays.areEqual(sample3Bytes, UrlBase64.decode(sample3)));
+ assertTrue(Arrays.areEqual(sample3Bytes, UrlBase64.decode(Strings.toByteArray(sample3))));
+ }
+
+ public void testInvalidInput()
+ throws IOException
+ {
+ String[] invalid = new String[] { invalid1, invalid2, invalid3, invalid4, invalid5, invalid6, invalid7, invalid8, invalid9, invalida, invalidb, invalidc, invalidd };
+
+ for (int i = 0; i != invalid.length; i++)
+ {
+ invalidTest(invalid[i]);
+ invalidTest(Strings.toByteArray(invalid[i]));
+ }
+ }
+
+ private void invalidTest(String data)
+ {
+ try
+ {
+ UrlBase64.decode(data);
+ }
+ catch (DecoderException e)
+ {
+ return;
+ }
+
+ fail("invalid String data parsed");
+ }
+
+ private void invalidTest(byte[] data)
+ {
+ try
+ {
+ UrlBase64.decode(data);
+ }
+ catch (DecoderException e)
+ {
+ return;
+ }
+
+ fail("invalid byte data parsed");
+ }
+
+ protected char paddingChar()
+ {
+ return '.';
+ }
+
+ protected boolean isEncodedChar(char c)
+ {
+ if (Character.isLetterOrDigit(c))
+ {
+ return true;
+ }
+ else if (c == '-')
+ {
+ return true;
+ }
+ else if (c == '_')
+ {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/io/pem/test/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/io/pem/test/AllTests.java
new file mode 100644
index 000000000..6c04ace63
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/io/pem/test/AllTests.java
@@ -0,0 +1,71 @@
+package org.spongycastle.util.io.pem.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.spongycastle.util.io.pem.PemHeader;
+import org.spongycastle.util.io.pem.PemObject;
+import org.spongycastle.util.io.pem.PemWriter;
+
+public class AllTests
+ extends TestCase
+{
+ public void testPemLength()
+ throws IOException
+ {
+ for (int i = 1; i != 60; i++)
+ {
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[i]);
+ }
+
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[100]);
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[101]);
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[102]);
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[103]);
+
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[1000]);
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[1001]);
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[1002]);
+ lengthTest("CERTIFICATE", Collections.EMPTY_LIST, new byte[1003]);
+
+ List headers = new ArrayList();
+
+ headers.add(new PemHeader("Proc-Type", "4,ENCRYPTED"));
+ headers.add(new PemHeader("DEK-Info", "DES3,0001020304050607"));
+
+ lengthTest("RSA PRIVATE KEY", headers, new byte[103]);
+ }
+
+ private void lengthTest(String type, List headers, byte[] data)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
+
+ PemObject pemObj = new PemObject(type, headers, data);
+ pWrt.writeObject(pemObj);
+
+ pWrt.close();
+
+ assertEquals(bOut.toByteArray().length, pWrt.getOutputSize(pemObj));
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run (suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("util tests");
+ suite.addTestSuite(AllTests.class);
+ return suite;
+ }
+}
\ No newline at end of file
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/io/test/BufferingOutputStreamTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/io/test/BufferingOutputStreamTest.java
new file mode 100644
index 000000000..faaaa5329
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/io/test/BufferingOutputStreamTest.java
@@ -0,0 +1,68 @@
+package org.spongycastle.util.io.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.spongycastle.util.io.BufferingOutputStream;
+import org.spongycastle.util.test.SimpleTest;
+
+public class BufferingOutputStreamTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "BufferingStreamTest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ SecureRandom random = new SecureRandom();
+
+ for (int i = 1; i != 256; i++)
+ {
+ byte[] data = new byte[i];
+
+ random.nextBytes(data);
+
+ checkStream(data, 16);
+ checkStream(data, 33);
+ checkStream(data, 128);
+ }
+ }
+
+ private void checkStream(byte[] data, int bufsize)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BufferingOutputStream bfOut = new BufferingOutputStream(bOut, bufsize);
+
+ for (int i = 0; i != 10; i++)
+ {
+ bfOut.write(data[0]);
+ bfOut.write(data, 1, data.length - 1);
+ }
+
+ bfOut.close();
+
+ byte[] output = bOut.toByteArray();
+
+ for (int i = 0; i != 10; i++)
+ {
+ for (int j = 0; j != data.length; j++)
+ {
+ if (output[i * data.length + j] != data[j])
+ {
+ fail("data mismatch!");
+ }
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new BufferingOutputStreamTest());
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/AllTests.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/AllTests.java
new file mode 100644
index 000000000..37c73eb82
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/AllTests.java
@@ -0,0 +1,20 @@
+package org.spongycastle.util.utiltest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests
+{
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run (suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("util tests");
+ suite.addTestSuite(IPTest.class);
+ suite.addTestSuite(BigIntegersTest.class);
+ return suite;
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/BigIntegersTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/BigIntegersTest.java
new file mode 100644
index 000000000..f17745eee
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/BigIntegersTest.java
@@ -0,0 +1,90 @@
+package org.spongycastle.util.utiltest;
+
+import java.math.BigInteger;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.IPAddress;
+import org.spongycastle.util.encoders.Hex;
+
+public class BigIntegersTest
+ extends TestCase
+{
+ public String getName()
+ {
+ return "BigIntegers";
+ }
+
+ public void testAsUnsignedByteArray()
+ {
+ BigInteger a = new BigInteger(1, Hex.decode("ff12345678"));
+
+ byte[] a5 = BigIntegers.asUnsignedByteArray(a);
+
+ Assert.assertEquals(5, a5.length);
+ Assert.assertTrue(Arrays.areEqual(a5, Hex.decode("ff12345678")));
+
+ BigInteger b = new BigInteger(1, Hex.decode("0f12345678"));
+
+ byte[] b5 = BigIntegers.asUnsignedByteArray(b);
+
+ Assert.assertEquals(5, b5.length);
+ Assert.assertTrue(Arrays.areEqual(b5, Hex.decode("0f12345678")));
+ }
+
+ public void testFixedLengthUnsignedByteArray()
+ {
+ BigInteger a = new BigInteger(1, Hex.decode("ff12345678"));
+
+ byte[] a5 = BigIntegers.asUnsignedByteArray(5, a);
+
+ Assert.assertEquals(5, a5.length);
+ Assert.assertTrue(Arrays.areEqual(a5, Hex.decode("ff12345678")));
+
+ byte[] a6 = BigIntegers.asUnsignedByteArray(6, a);
+
+ Assert.assertEquals(6, a6.length);
+ Assert.assertEquals(0, a6[0]);
+ Assert.assertTrue(Arrays.areEqual(a6, Hex.decode("00ff12345678")));
+
+ BigInteger b = new BigInteger(1, Hex.decode("0f12345678"));
+
+ byte[] b5 = BigIntegers.asUnsignedByteArray(5, b);
+
+ Assert.assertEquals(5, b5.length);
+ Assert.assertTrue(Arrays.areEqual(b5, Hex.decode("0f12345678")));
+
+ byte[] b6 = BigIntegers.asUnsignedByteArray(6, b);
+
+ Assert.assertEquals(6, b6.length);
+ Assert.assertEquals(0, b6[0]);
+ Assert.assertTrue(Arrays.areEqual(b6, Hex.decode("000f12345678")));
+
+ BigInteger c = new BigInteger(1, Hex.decode("ff123456789a"));
+
+ try
+ {
+ byte[] c5 = BigIntegers.asUnsignedByteArray(5, c);
+
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // ignore
+ }
+
+ BigInteger d = new BigInteger(1, Hex.decode("0f123456789a"));
+ try
+ {
+ byte[] c5 = BigIntegers.asUnsignedByteArray(5, d);
+
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // ignore
+ }
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/IPTest.java b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/IPTest.java
new file mode 100644
index 000000000..93df95b97
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/java/org/spongycastle/util/utiltest/IPTest.java
@@ -0,0 +1,55 @@
+package org.spongycastle.util.utiltest;
+
+import junit.framework.TestCase;
+import org.spongycastle.util.IPAddress;
+
+public class IPTest
+ extends TestCase
+{
+
+ private static final String validIP4v[] = new String[]
+ { "0.0.0.0", "255.255.255.255", "192.168.0.0" };
+
+ private static final String invalidIP4v[] = new String[]
+ { "0.0.0.0.1", "256.255.255.255", "1", "A.B.C", "1:.4.6.5" };
+
+ private static final String validIP6v[] = new String[]
+ { "0:0:0:0:0:0:0:0", "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
+ "0:1:2:3:FFFF:5:FFFF:1" };
+
+ private static final String invalidIP6v[] = new String[]
+ { "0.0.0.0:1", "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFFF" };
+
+ private void testIP(String[] valid, String[] invalid)
+ {
+ for (int i = 0; i < valid.length; i++)
+ {
+ if (!IPAddress.isValid(valid[i]))
+ {
+ fail("Valid input string not accepted: " + valid[i] + ".");
+ }
+ }
+ for (int i = 0; i < invalid.length; i++)
+ {
+ if (IPAddress.isValid(invalid[i]))
+ {
+ fail("Invalid input string accepted: " + invalid[i] + ".");
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "IPTest";
+ }
+
+ public void testIPv4()
+ {
+ testIP(validIP4v, invalidIP4v);
+ }
+
+ public void testIPv6()
+ {
+ testIP(validIP6v, invalidIP6v);
+ }
+}
diff --git a/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/asn1/test/package.html b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/asn1/test/package.html
new file mode 100644
index 000000000..df45e190e
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/asn1/test/package.html
@@ -0,0 +1,5 @@
+
+
+Test programs for the ASN.1 package.
+
+
diff --git a/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/cms/test/package.html b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/cms/test/package.html
new file mode 100644
index 000000000..a24291993
--- /dev/null
+++ b/libraries/spongycastle/core/src/test/javadoc/org/spongycastle/cms/test/package.html
@@ -0,0 +1,7 @@
+
+
+Regression tests for the org.spongycastle.cms package.
+